ClickCease Linux Kernel CVE 데이터 분석(업데이트됨)

인기 뉴스레터 구독하기

4,500명 이상의 Linux 및 오픈소스 전문가와 함께하세요!

한 달에 두 번. 스팸이 없습니다.

Linux Kernel CVE 데이터 분석(업데이트됨)

2021년 12월 13일 TuxCare 홍보팀

Linux 보안, Kernel 취약성에 관심이 있거나 단순히 테스트를 실행할 여유 시간이 있다면 이 문서가 도움이 될 것입니다. 이 문서에서는 CVE 리포지토리에서 데이터를 추출하고 자체 통계 분석을 생성하는 데 사용할 수 있는 방법론의 업데이트된 버전을 제공합니다. 어떤 Kernel 버전에 가장 많은 취약점이 있는지 알아보세요.

편집자 주: 이 글은 이전 KernelCare 블로그에 게시된 일련의 글의 업데이트 버전입니다. 게시된 이후 CVE 리포지토리의 데이터 형식이 변경되어 원래 지침이 더 이상 작동하지 않습니다. 가시성이 없는 이전 문서를 업데이트하는 대신 전체 내용을 단계별 지침과 함께 다시 게시합니다.

일부 Kernel 버전은 다른 Kernel 버전보다 널리 채택되어 더 광범위하게 테스트되기 때문에 결과가 다소 왜곡될 수 있으며, CVE가 특정 Kernel 버전의 (내)보안 수준을 주장하는 최선의 방법은 아니라는 것을 알고 있습니다. 하지만 여전히 흥미로운 연습이며, 다른 방법으로는 사용하지 않을 수도 있는 여러 도구를 사용해 볼 수 있습니다. 이제 "이 Kernel이나 저 Kernel에 취약점이 너무 많다"는 주장을 직접 확인할 수 있는 방법이 생겼고, 이 기초 작업을 통해 약간의 변경만 하면 Kernel뿐만 아니라 다른 프로젝트의 취약점도 추적할 수 있습니다.

원본 글의 업데이트 버전을 제출해 주신 폴 제이콥스에게 "감사합니다"라고 전합니다.

 

콘텐츠

1부 - 설정 및 데이터 수집

2부 - 데이터 필터링

3부 - 고급 쿼리 및 그래프

 

 

2021년에 업데이트되었습니다. 변경 사항:

    • CVE 1.1 사양에 맞게 망고 쿼리 선택기 구문이 업데이트되었습니다.
    • CouchDB Docker 컨테이너를 사용합니다.

 

파트 1

Linux Kernel 개발자들은 사용하기에 '가장 좋은' Linux Kernel은 사용 중인 배포판과 함께 제공되는 Kernel이라고 말합니다. 또는 가장 안정적인 최신 버전. 또는 가장 최신의 장기 지원(LTS) 버전. 또는 유지 보수만 된다면 원하는 어떤 것이든 상관없습니다.

선택의 폭이 넓은 것도 좋지만, 차라리 정답이 하나만 있다면 최고를 원합니다. 문제는 어떤 사람들에게는 최고가 가장 빠른 것을 의미한다는 것입니다. 다른 사람들에게는 최신 기능이나 특정 기능을 갖춘 것이 최고입니다. 저에게 있어 최고의 Linux Kernel은 가장 안전한 Kernel입니다.

'가장 안전한' Linux Kernel은 무엇인가요?

소프트웨어의 안전성을 고려하는 조잡한 방법은 특정 버전의 소프트웨어에 얼마나 많은 보안 문제가 나타나는지 확인하는 것입니다. (불안정한 전제라는 것을 알지만, CVE에 대해 자세히 알아보고 해석하는 방법을 배우기 위해서라도 이 방법을 사용해보겠습니다.) 이 전술을 Linux Kernel에 적용하려면 취약성 보고서를 검토해야 합니다.

이 세 개의 기사 시리즈에서 제가 할 일은 바로 이 것입니다. Linux Kernel CVE(보고된 취약점)를 분석하여 어떤 것이 사용하기에 가장 좋은(즉, 가장 안전한) Linux Kernel인지에 대한 질문에 답하는 데 도움이 되는 트렌드가 있는지 살펴볼 것입니다.

하지만 잠깐만요, 이미 해본 적이 있지 않나요?

그런 셈이죠.

  • CVE 세부 정보: CVE 검색 엔진. 심각도, 제품, 공급업체 등을 기준으로 CVE를 찾을 수 있습니다.

  • Linux Kernel CVE: 또한 특정 Linux Kernel 버전에 대한 CVE ID를 나열하는 CVE 검색 엔진입니다.

  • 알렉산더 레오노프: CVE 데이터 파일의 구조에 대한 좋은 글이지만, CPE 데이터에 초점을 맞춘 글입니다.

  • Tenable.sc: CVE 분석 대시보드가 포함된 상용 제품입니다.

그렇다면 왜 이렇게 해야 할까요?

이러한 리소스와 프로젝트는 제 질문에 대한 답을 주지 못하거나 비용이 들기 때문입니다.

또한 CVE가 무엇인지, Linux Kernel 버전 간의 차이점(있는 경우)이 무엇인지, 이 접근 방식의 한계가 무엇인지에 대해 알고 싶습니다.

프로세스 및 도구

데이터를 가져와서 분석하고 결과를 차트로 표시하는 것은 매우 간단합니다.

이 도구는 무료이며 다양한 플랫폼에서 실행됩니다. 저는 데비안 Linux에서 작업할 예정이므로 벌거벗은 터미널을 보고도 당황하지 않으실 거라고 가정하겠습니다.

어떤 절차가 필요한지 알 수 있도록 주요 단계는 다음과 같습니다.

  • Docker를 사용하여 최신 버전의 Apache CouchDB를 실행합니다. (단일 노드 설정이 될 것입니다. 관리에 얽매이지 않고 데이터를 탐색하고 싶기 때문입니다.)

  • NVD 데이터(JSON 파일)를 다운로드하고 Linux Kernel에 대한 레코드뿐만 아니라 모든 CVE 레코드를 가져옵니다. (이렇게 하면 JSON 파일에서 레코드를 구문 분석하고 추출하는 것보다 쿼리 시 관련 데이터를 더 쉽게 선택할 수 있습니다.)

  • Mango를 사용하여 데이터를 쿼리합니다.

먼저, CVE가 무엇이며 CVE 레코드에 어떤 내용이 포함되어 있는지 간략히 설명합니다.

CVE란 무엇인가요?

CVE는 공통 취약점 및 노출의 약자입니다.

여기서 CVE는 소프트웨어 취약점을 식별하는 코드입니다. 다음과 같은 형식입니다. CVE-YYYY-N여기서 YYYY는 ID가 할당되거나 공개된 연도이고, N은 임의의 길이의 시퀀스 번호입니다. 조직은 연귀 좌표는 CVE 목록.

분명히 CVE는 ID가 아니라 세부 정보에 의해 정의됩니다. 여러 기관이나 단체에서 이러한 세부 정보를 관리하지만, 여기서는 가장 잘 알려진 미국 국립표준기술연구소(NIST)에서 관리하는 국가 취약성 데이터베이스 (NVD)를 사용하겠습니다. 누구나 무료로 CVE 데이터를 다운로드할 수 있습니다.

NIST에는 연도별로 취약점의 심각도가 어떻게 달라지는지 보여주는 간단한 막대 차트가 있습니다. 이 차트는 2001년부터 올해까지의 모든 소프트웨어 CVE(Linux뿐만 아니라)에 대한 차트입니다. 저도 비슷한 차트를 원하지만, Linux 취약성에 대해서만 Kernel 버전별로 분류된 차트를 원합니다.

CVE란 무엇인가요?

JSON 형식의 취약점 파일을 로드하고 쿼리하기 전에 그 안에 무엇이 있는지 보여드리겠습니다. (파일 사양 스키마가 있지만 여기에는 우리가 알 필요가 없는 세부 정보가 많이 있습니다.) 각 NVD CVE 파일에는 1년치(1월 1일~12월 31일)의 CVE가 포함되어 있습니다. 다음은 CVE 항목의 기본 구조입니다.

  • 첫 번째 JSON 섹션은 헤더로, 데이터 형식, 버전, 레코드 수, 타임스탬프로 구성됩니다.

  • 나머지는 하나의 큰 배열입니다, CVE_Items 이러한 하위 요소와 함께:

    • cve
    • problemtype
    • references
    • description
    • configurations
    • impact
    • 와 두 개의 날짜 필드, publishedDate 그리고 lastModifiedDate.

    관심 있는 것은 configurations. 포함되어 있습니다:

    • nodes의 배열입니다:
      • children의 배열입니다:

    그것은 cpe23Uri 그리고 impact 결국 쿼리하게 될 부분입니다.

    • 영향 블록은 공통 취약점 점수 시스템(CVSS)을 사용하여 취약점의 심각도를 숫자와 이름으로 정의합니다. (2.0과 3.0의 두 가지 버전이 있으며 아래와 같이 약간 다릅니다.)

      • 숫자는 기본 점수 범위 (baseScore), 0.0에서 10.0 사이의 소수점 값입니다.

      • 이름(severity)는 낮음, 중간 또는 높음 중 하나입니다(CVSS V2.0의 경우, v3.0에는 없음 및 중요가 추가되었습니다).

CVSS v2.0 대 v3.0

두 버전은 기본 점수를 심각도에 매핑하는 방식이 다릅니다. (아래 표는 NVD CVSS 페이지에서 가져온 것입니다.)

CVSS v2.0 등급 CVSS v3.0 등급
심각도 기본 점수 범위 심각도 기본 점수 범위
없음 0.0
낮음 0.0-3.9 낮음 0.1-3.9
Medium 4.0-6.9 Medium 4.0-6.9
높음 7.0-10.0 높음 7.0-8.9
크리티컬 9.0-10.0

이제 CouchDB를 설정하고 데이터를 로드한 다음 Mango로 쿼리해 보겠습니다.

Docker를 통해 CouchDB 실행

  • 플랫폼에 맞는 Docker를 다운로드 하세요.

  • 이 명령어를 터미널에 복사하여 붙여넣고 실행합니다. (이제부터는 명령이 보이면 이를 인식하고 무엇을 해야 하는지 알 수 있다고 가정하겠습니다.)

    sudo 도커 실행 -d
    -p 5984:5984
    -e COUCHDB_USER=admin
    -e COUCHDB_PASSWORD=password
    -name couchdb couchdb

     

    • 사용하겠습니다. password 여기를 클릭하세요. 직접 선택하되 여기에서부터 어디에 사용되는지 확인하세요.
    • 컨테이너 내부가 아닌 일반 애플리케이션 설치로 CouchDB를 네이티브 설치하는 것을 선호하는 경우 docs.couchdb.org를 참조하세요.
  • 웹 브라우저를 열고 http://localhost:5984/_utils 으로 이동합니다.

  • Docker 명령에 사용된 자격 증명으로 로그인합니다( COUCHDB_USER 그리고 COUCHDB_PASSWORD).

  • 데이터베이스 창에서 데이터베이스 생성을 선택합니다.

  • In 데이터베이스 이름를 입력하고 nvd (또는 사용자가 직접 선택한 데이터베이스 이름)을 입력합니다.

  • 파티셔닝의 경우 파티션되지 않음을 선택합니다.

  • 만들기를 클릭합니다.

CVE 데이터를 CouchDB로 가져오기

  • 시스템에 curl과 jq가 설치되어 있는지 확인합니다(설치되어 있지 않은 경우 설치).
curl -버전 || sudo apt install -y curl
jq -버전 || sudo apt install -y jq

 

sudo apt-get 설치 -y npm
npm 설치 -g couchimport

 

  • CVE JSON 데이터 파일을 다운로드합니다.
VERS=“1.1”
R=“https://nvd.nist.gov/feeds/json/cve/${VERS}/”
for YEAR in $(seq 2009 $(date +%Y))
do
    FILE=$(printf “nvdcve-${VERS}-%s.json.gz” $YEAR)
    URL=${R}${FILE}
    echo “Downloading ${URL} to ${FILE}
    curl ${URL} –output ${FILE} –progress-bar –retry 10
done

 

    • 관심사에 맞게 연도 범위를 조정하세요. NVD에는 2002년부터의 데이터가 있습니다.
    • 버전은 수시로 변경됩니다. https://nvd.nist.gov/vuln/data-feeds 을 확인하고 그에 따라 버전을 설정하되, 데이터 파일 형식이 변경되었는지 확인하세요.

 

  • 파일의 압축을 풉니다.
건집 *.gz

 

  • CVE 데이터를 CouchDB 데이터베이스로 가져옵니다.
export
COUCH_URL=“http://admin:password@localhost:5984”
COUCH_DATABASE=“nvd”
COUCH_FILETYPE=“json”
COUCH_BUFFER_SIZE=100
for f in $(ls -1 nvdcve*.json); do
    echo “Importing ${f}
    cat ${f} | couchimport –jsonpath “CVE_Items.*”
done

 

    • 다른 이름으로 COUCH_DATABASE를 생성한 경우 변경합니다.
    • 미리 보기 참 옵션을 사용하여 드라이런을 수행합니다.
    • 실패하면 COUCH_BUFFER_SIZE의 크기를 줄이세요. 기본값은 500이지만 버퍼 크기를 낮추지 않으면 2020년 데이터를 가져오는 데 실패합니다(저 같은 경우).
    • 카우치임포트에 대해 자세히 알아보세요.

 

  • 데이터베이스의 레코드 수가 파일과 동일한지 확인합니다. (이 두 명령의 개수는 동일해야 합니다.)
grep CVE_data_numberOfCVEs nvdcve*.json |
cut -d‘:’ -f3 |
tr –cd ‘[:digit:][:cntrl:]’ |
awk ‘{s+=$1} END {print s}’

 

curl -sX GET ${COUCH_URL}/nvd | jq '.doc_count'
    • 개수가 다르면 이전 단계의 출력을 확인하고 가져오기 실패가 있는지 확인하세요.

Fauxton에서 망고 쿼리 실행하기

이 첫 번째 쿼리를 쉽게 하기 위해 CouchDB 브라우저 기반 UI인 Fauxton을 사용하겠습니다. 하지만 2부와 3부에서는 명령줄로만 작업할 것이므로 너무 익숙해지지는 마세요.

  • 브라우저에서 다음 주소로 이동합니다: http://localhost:5984/_utils/#database/nvd/_find
    일정 시간 동안 사용하지 않으면 Fauxton에서 로그아웃됩니다. 이 경우 로그아웃했다가 다시 로그인하여 데이터베이스로 이동한 다음 nvd 데이터베이스(사용한 이름인 경우)를 선택한 다음 망고로 쿼리 실행을 클릭합니다.
  • 망고 쿼리 창의 내용을 삭제한 다음 이 쿼리 텍스트를 복사하여 붙여넣습니다:
{
    “selector”: {
        “configurations.nodes”: {
            “$elemMatch”: {
                “operator”: “OR”,
                “cpe_match”: {
                    “$elemMatch”: {
                        “cpe23Uri”: {
                            “$regex”: “linux_kernel”
                        }
                    }
                }
            }
        },
        “publishedDate”: {
            “$gte”: “2021-01-01”,
            “$lte”: “2021-12-31”
        }
    },
    “fields”: [
        “cve.CVE_data_meta.ID”
    ],
    “limit”: 999999
}

 

  • 페이지당 문서 수를 최대값으로 설정합니다.
  • 쿼리 실행을 클릭합니다.

그 결과 2021년에 할당되거나 게시된 모든 심각도 및 Kernel 버전에 대한 Linux Kernel 취약성의 CVE ID 목록이 표시됩니다.

숙제

  1. cve.CVE_data_meta.ID를 복사하여 NVD 검색 페이지에 붙여넣으면 CVE의 세부 정보를 확인할 수 있습니다.
  2. 다양한 날짜 범위로 실험해 보세요. 예를 들어 2019년 1월부터 시작하려면 게시된 날짜 요소를 다음으로 바꿉니다:
“publishedDate”: {
  $gte: “2019-01-01”,
  $lte: “2019-12-31”
}

 

축하드립니다. 이제 여러분은 10년 이상(또는 여러분이 선택한 것)의 CVE로 가득 찬 CouchDB 데이터베이스의 자랑스러운 소유자가 되셨습니다. 데이터베이스에는 모든 공급업체의 모든 소프트웨어에 대한 데이터가 포함되어 있다는 점을 기억하세요.

다음으로 심각도 및 Kernel 버전에 대한 선택기를 추가하고 쿼리를 실행한 후 결과를 차트로 만들어 보겠습니다.

 

파트 2

예선전

모든 것을 추적할 수 있는 디렉터리를 만드세요.

mkdir 빈 큐리 이미지 아웃
  • 이렇게 사용하겠습니다:
    • bin: 스크립트;
    • qry: 망고 쿼리 파일;
    • 이미지: 차트 이미지;
    • 아웃: 쿼리에서 출력합니다.

Gnuplot을 설치합니다.

sudo apt 설치 -y gnuplot

 

다음으로 Linux Kernel 취약점의 수가 매년 어떻게 변화하는지 살펴보겠습니다. 이를 위해 실행 시간에 값을 대체하여 Mango 쿼리에서 printf 토큰을 사용하여 매개 변수를 추가하겠습니다. (나중에 Kernel 버전에 대한 매개변수도 추가할 예정입니다.) 작업을 빠르고 간단하게 유지하기 위해, 명령줄에서 쿼리를 제출하기 위해 CouchDB POST API를 사용하겠습니다.

쿼리 1a - 연도별 Linux Kernel CVE

이를 실행하여 Mango 쿼리 파일을 생성합니다:

cat<<EOF>qry/1-linux-kernel-cves-by-year.json
{
    “selector”: {
        “configurations.nodes”: {
            $elemMatch: {
                “operator”: “OR”,
                “cpe_match”: {
                    $elemMatch: {
                        “cpe23Uri”: {
                            $regex: “linux_kernel”
                        }
                    }
                }
            }
        },
        “publishedDate”: {
            $gte: “%s-01-01”,
            $lte: “%s-12-31”
        }
    },
    “fields”: [
        “cve.CVE_data_meta.ID”
    ],
    “limit”: 999999
}
EOF

 

  • 실행하세요.
for YEAR in (seq 2009 $(date +%Y)); do
echo -en "$YEARt"
printf "(cat qry/1-linux-kernel-cves-by-year.json)"
$YEAR $YEAR
| curl -sX POST -d @-
http://admin:password@localhost:5984/nvd/_find
-header "Content-Type:application/json"
| jq '.docs | 길이'
done | tee out/1-linux-kernel-cves-by-year.tsv
    • 이렇게 하면 범위(1줄)의 모든 연도에 대해 쿼리를 루프에서 실행하고 결과를 콘솔과 (그래프 작성에 사용할) out/1-linux-kernel-cves-by-year.tsv에 모두 인쇄합니다.
    • 관심사에 맞게 또는 파트 1에서 가져온 내용과 일치하도록 연도 범위를 변경하세요.
    • 결과가 나오지 않으면 쿼리 파일(qry/1-linux-kernel-cves-by-year.json)과 CouchDB 자격증명(관리자/비밀번호)을 확인합니다.

다음은 제가 얻은 결과입니다.
2009         101

2010         116

2011         81

2012         114

2013         190

2014         137

2015         79

2016         218

2017         453

2018         184

2019         287

2020         125

2021         127

 

  • 연도별 Linux Kernel 취약점 수입니다.
  • 취약점을 소급하여 기록할 수 있으므로 일부 수치는 더 높을 수 있습니다.

이를 실행하여 bin/1-linux-kernel-cves-by-year.gnuplot에 gnuplot 스크립트를 생성합니다:

cat<<EOF>bin/1-linux-kernel-cves-by-year.gnuplot
reset
set terminal png size 800,600
set output ‘img/1-linux-kernel-cves-by-year.png’
set style fill solid 0.5
set boxwidth 0.8 relative
set xtics 1
set key top right
set title ‘Linux Kernel CVEs by Year’
set xlabel “Year”
set ylabel “Number of CVEs”
set autoscale y
plot [2008.5:*] []
‘out/1-linux-kernel-cves-by-year.tsv’
w boxes t ‘All severities’,
‘out/1-linux-kernel-cves-by-year.tsv’
u 1:($2+15):2 w labels t
EOF
  • 이를 실행하여 img/1-linux-kernel-cves-by-year.png에 이미지 파일을 만듭니다:
gnuplot -c bin/1-linux-kernel-cves-by-year.gnuplot

 

  • 즐겨 사용하는 이미지 뷰어에서 img/1-linux-kernel-cves-by-year.png를 엽니다. 여기 제 것이 있습니다.

1-Linux-Kernel-연도별-CVE

해설

그만큼 많은 Linux Kernel 취약점이 발견되었습니다. 2017년은 Linux Kernel 취약성에 있어 매우 좋은 해이자 나쁜 해였습니다.

이 수치를 보고 걱정이 되어 다른 출처, 그중에서도 CVEDetails와 교차 확인했습니다. 숫자는 꽤 잘 맞았습니다.

연도 내 이력서세부 사항
2009 101 104
2010 116 118
2011 81 81
2012 114 114
2013 190 186
2014 137 128
2015 79 79
2016 218 215
2017 453 449
2018 184 178
2019 287 289
2020 125 126
2021 127 130

모든 차이는 CVE 세부 정보 관찰로 설명할 수 있습니다:

"...CVE 데이터에는 표시되는 데이터의 정확성에 영향을 미치는 불일치가 있습니다... 예를 들어 Oracle Database 10g와 관련된 취약점은 'Oracle Database', 'Oracle Database10g', 'Database10g', 'Oracle 10g' 등의 제품에 대해 정의되었을 수 있습니다."

즉, NVD CVE 데이터는 완전히 정확하지 않습니다. (이에 대해서는 나중에 자세히 설명합니다.)

이 차트는 모든 CVE 심각도에 걸쳐 집계된 총 CVE 수를 보여줍니다. 결과를 심각도별로 세분화하기 위해 Mango 쿼리에 선택기를 추가하겠습니다.

쿼리 1b - 연도별 및 심각도별 Linux Kernel CVE

  • 이를 실행하여 qry/cve-1b.json (심각도 매개변수가 추가된 쿼리 1a의 복사본)을 생성합니다:
cat<<EOF>qry/2-linux-kernel-cves-by-year-and-severity.json
{
    “selector”: {
        “configurations.nodes”: {
            $elemMatch: {
                “operator”: “OR”,
                “cpe_match”: {
                    $elemMatch: {
                        “cpe23Uri”: {
                            $regex: “linux_kernel”
                        }
                    }
                }
            }
        },
        “impact.baseMetricV2.severity”: “%s”,
        “publishedDate”: {
            $gte: “%s-01-01”,
            $lte: “%s-12-31”
        }
    },
    “fields”: [
        “cve.CVE_data_meta.ID”
    ],
    “limit”: 999999
}
EOF
  • 실행하세요.
export
OUT=“2-linux-kernel-cves-by-year-and-severity”
SEVS=(LOW MEDIUM HIGH)
(IFS=$‘t’; echo -e “YEARt${SEVS[*]} | tee out/${OUT}.tsv);
for YEAR in $(seq 2009 $(date +%Y)); do
    RES=()
    echo -en $YEARt”
    for SEV in ${SEVS[*]}; do
        RES+=($(printf $(cat qry/${OUT}.json)
        ${SEV} ${YEAR} ${YEAR}
        | curl -sX POST -d @-
        http://admin:password@localhost:5984/nvd/_find
          –header “Content-Type:application/json”
        | jq ‘.docs | length’))
    done
    (IFS=$‘t’; echo ${RES[*]})
done | tee -a out/${OUT}.tsv

 

다음은 제 결과물입니다.
YEAR LOW MEDIUM HIGH

2009   11   51   39

2010   37   47   32

2011   19   41   21

2012   24   66   24

2013   46   126  18

2014   20   87   30

2015   17   44   18

2016   25   114  79

2017   73   91  289

2018   36   93  55

2019   47   151 89

2020   38   64  23

2021   44   59  24

 

  • 이를 실행하여 bin/cve-1b.gnuplot에 gnuplot 스크립트를 생성합니다:
cat<<EOF>bin/${OUT}.gnuplot
reset
set terminal png size 800,600
set output “img/`echo ${OUT}`.png”
set style data histograms
set style histogram rowstacked
set style fill solid 0.5
set boxwidth 0.8 relative
set xtics 1
set key top right title “Severity”
set title “Linux kernel CVEs”
set xlabel “Year”
set ylabel “Number of CVEs”
set autoscale y
plot “out/`echo ${OUT}`.tsv”
u ($2):xtic(1) t col,
u ($3):xtic(1) t col,
u ($4):xtic(1) t col,
u ($0-1):($2+10):(sprintf(“%d”,$2)) with labels t ,
u ($0-1):($2+$3+10):(sprintf(“%d”,$3)) with labels t ,
u ($0-1):($2+$3+$4+10):(sprintf(“%d”,$4)) with labels t
EOF

 

  • 이를 실행하면 이미지 파일이 img/cve-1b.png로 생성됩니다.
gnuplot -c bin/${OUT}.gnuplot

여기 제 것이 있습니다.

2-Linux-Kernel-연도별-심각도-별

심각도 관련 참고 사항

    • 쿼리 1b는 최신 버전 3이 아닌 CVSS 버전 2를 사용합니다. 여기에는 이유가 있습니다. CVSS 버전 3은 2015년 12월에 도입되었으며, 그 이전의 CVE는 버전 2 CVSS 체계로만 선택할 수 있습니다.
    • CVSS 버전 2와 3은 모두 심각도를 수치와 텍스트로 표현합니다. 숫자는 더 정확한 반면 텍스트는 더 거칠지만 이해하기 쉽습니다. 간단하게 설명하기 위해 저는 텍스트 방식을 사용하고 있습니다. 정확성을 위해 나중에 숫자 체계로 변경할 예정입니다.

쿼리 1b는 쿼리 1a보다 더 선명한 그림이지만 덜 충격적이지는 않습니다. 예를 들어, 심각도가 높은 Linux Kernel 취약점의 상대적 비율을 2016년 이전과 이후의 취약점과 비교해 보세요.

이러한 취약점 중 상당수가 몇몇 Kernel 버전에 집중되어 있을 수 있습니다. 버전과 연도 및 심각도까지 선택하도록 쿼리를 수정할 때까지는 알 수 없습니다. 그 작업은 다음에 할 예정입니다.

 

파트 3

여기 3부에서는 핵심 Mango 쿼리를 개발하여 Kernel 버전에 따라 Linux Kernel 취약점의 수가 어떻게 달라지는지 살펴봅니다.

버전이 너무 많기 때문에 쿼리와 명령이 더 복잡해지고 완료하는 데 시간이 오래 걸리기 시작합니다. 따라서 지금은 연도와 Kernel 버전만 변경하겠습니다. 심각도는 여전히 매개 변수로 남아 있지만 모든 심각도에 대한 결과를 쿼리하겠습니다.

먼저 오래 지속되는 것으로 유명한 2.6.x Linux Kernel 브랜치의 마지막 다섯 가지 릴리스(2.6.35~2.6.39)를 살펴볼 것입니다.

쿼리 2 - 연도별 및 Kernel 버전별 Linux Kernel CVE

  • 이를 실행하여 쿼리 파일을 생성합니다:
export QRY=“3-linux-kernel-cves-by-year-and-version”
cat<<EOF>qry/${QRY}.json
{
    “selector”: {
        “configurations.nodes”: {
            $elemMatch: {
                “operator”: “OR”,
                “cpe_match”: {
                    $elemMatch: {
                        “cpe23Uri”: {
                            $regex: “linux_kernel:%s”
                        }
                    }
                }
            }
        },
        “publishedDate”: {
            $gte: “%s-01-01”,
            $lte: “%s-12-31”
        }
    },
    “fields”: [
        “cve.CVE_data_meta.ID”
    ],
    “limit”: 999999
}
EOF

 

  • 실행하세요.
export
VERS_MAJOR=2.6
VERS_MINOR_START=35
VERS_MINOR_STOP=39
YEAR_START=2009
YEAR_STOP=$(date +%Y)
VERSIONS=()
COUCHDB_URL=“http://admin:password@localhost:5984/nvd/_find”
QRY=“3-linux-kernel-cves-by-year-and-version”
export OUT=${QRY}_${VERS_MAJOR}.x”
for VERS_MINOR in $(seq ${VERS_MINOR_START} ${VERS_MINOR_STOP}); do
    VERSIONS+=(${VERS_MAJOR}.${VERS_MINOR});
done
(IFS=$‘t’; echo -e “t${VERSIONS[*]} | tee out/${OUT}.tsv);
for Y in $(seq ${YEAR_START} ${YEAR_STOP}); do
    RES=()
    echo -en ${Y}t”
    for V in ${VERSIONS[*]}; do
        RES+=($(printf $(cat qry/${QRY}.json) ${V} ${Y} ${Y}
        | curl -sX POST -d @- ${COUCHDB_URL}
        –header “Content-Type:application/json”
        | jq ‘.docs | length’))
    done
    (IFS=$‘t’; echo ${RES[*]})
done | tee -a out/${OUT}.tsv
  • 출력입니다:
2.6.35 2.6.36 2.6.37 2.6.38 2.6.39
2009 0 0 0 0 0
2010 1 22 8 0 0
2011 10 9 12 15 2
2012 1 5 2 13 8
2013 0 0 0 0 0
2014 1 1 0 0 0
2015 0 0 0 0 0
2016 0 0 0 0 0
2017 0 0 0 0 0
2018 0 0 0 0 0
2019 0 0 1 1 0
2020 0 0 1 0 0
2021 0 0 0 0 0
  • 이 스크립트를 실행하여 gnuplot 스크립트를 생성합니다:
cat<<EOF>bin/${OUT}.gnuplot
reset
set terminal png size 800,600
set output “img/`echo ${OUT}`.png”
set xtics 1
set key top right autotitle columnheader title “Kernel Version”
set title “Linux kernel CVEs by version (`echo ${VERS_MAJOR}`.x)”
set xlabel “Year”
set ylabel “Number of CVEs”
set autoscale y
plot [] [] for [c=2:*] “out/`echo ${OUT}`.tsv”
using 1:c with lines lw 3
EOF
  • gnuplot을 실행하여 이미지 파일을 생성합니다.
gnuplot -c bin/${OUT}.gnuplot
  • 여기 제 것이 있습니다.

3-linux-kernel-cves-by-year-and-version_2.6.x

연도별, 버전별 Linux Kernel 3.x, 4.x, 5.x용 스크립트 CVE

다음은 마지막 세 개의 명령을 단일 블록으로 결합하고 환경 변수를 사용하여 다른 주요 Linux Kernel 브랜치의 마지막 다섯 가지 릴리스를 쿼리하는 스크립트입니다.

코드를 파일에 저장하고 실행 가능한 상태로 만든 다음 실행합니다. 그리고 기다리세요.

#!/bin/bash
# Script to chart Linux kernel CVEs for kernel versions
# 3.x, 4.x, 5.x.
# You must have loaded a CouchDB database with CVE
# data as described here:
# https://blog.kernelcare.com/linux-kernel-cve-data-analysis-part-1-importing-into-couchdb

# Parallel arrays for Linux kernel versions:
# 3.0-3.19 (2011-DATE)
# 4.0-4.20 (2015-DATE)
# 5.0-5.12 (2019-DATE)
VERS_MAJOR=(3 4 5)
VERS_MINOR_START=(0 0 0)
VERS_MINOR_STOP=(19 20 12)
YEAR_START=(2011 2015 2019)

YEAR_STOP=$(date +%Y)
# admin/password are CouchDB admin login credentials
COUCHDB_URL=“http://admin:password@localhost:5984/nvd/_find”

QRY=“3-linux-kernel-cves-by-year-and-version”

##
# Construct version string from args
# and set VERSIONS.
# Args:
# 1: Major version number
# 2: Minor version start number
# 3: Minor version stop number
build_versions() {
    VERSIONS=()
    for VERS_MINOR in $(seq ${2} ${3}); do
        VERSIONS+=(${1}.${VERS_MINOR})
    done
}

##
# Write a query file to qry subdir
# Args:
# 1: Name of query file without extension
write_query() {
cat<<EOF>qry/${1}.json
{
    “selector”: {
        “configurations.nodes”: {
            $elemMatch: {
                “operator”: “OR”,
                “cpe_match”: {
                    $elemMatch: {
                        “cpe23Uri”: {
                            $regex: “linux_kernel:%s”
                        }
                    }
                }
            }
        },
        “publishedDate”: {
            $gte: “%s-01-01”,
            $lte: “%s-12-31”
        }
    },
    “fields”: [
        “cve.CVE_data_meta.ID”
    ],
    “limit”: 999999
}
EOF
}

##
# Run the query
#
run_query() {
    (IFS=$‘t’; echo -e “t${VERSIONS[*]} | tee out/${OUT}.tsv);
    for Y in $(seq ${YEAR_START[$i]} ${YEAR_STOP}); do
        RES=()
        echo -en ${Y}t”
        for V in ${VERSIONS[*]}; do
            RES+=($(printf $(cat qry/${QRY}.json) ${V} ${Y} ${Y}
            | curl -sX POST -d @- ${COUCHDB_URL}
            –header “Content-Type:application/json”
            | jq ‘.docs | length’))
        done
    (IFS=$‘t’; echo ${RES[*]})
    done | tee -a out/${OUT}.tsv
}

# Run a gnuplot script with values supplied
# by:
# 1: Base of filename
# 2: Major version number
# 3: As per 1
run_gnuplot() {

printf
reset
set terminal png size 800,600
set output “img/%s.png”
set xtics 1
set key top right autotitle columnheader title “Kernel Version”
set title “Linux kernel CVEs by version %s.x”
set xlabel “Year”
set ylabel “Number of CVEs”
set autoscale y
plot [] [] for [c=2:*] “out/%s.tsv” using 1:c with lines lw 3
$1 $2 $1 | gnuplot –
}

main() {
    write_query ${QRY}

    for i in 0 1 2; do
        build_versions ${VERS_MAJOR[$i]} ${VERS_MINOR_START[$i]} ${VERS_MINOR_STOP[$i]}
        OUT=${QRY}_${VERS_MAJOR[$i]}.x”
        run_query
        run_gnuplot $OUT ${VERS_MAJOR[$i]}
    done
}

main

다음은 결과 차트입니다.

3-linux-kernel-cves-by-year-and-version_3.x

예, 2017년에 큰 폭으로 증가한 것은 주로 3.18에서 발견된 취약점 때문이었습니다.

3-linux-kernel-cves-by-year-and-version_4.x

3-linux-kernel-cves-by-year-and-version_5.x

 

결론

  1. 모든 주요 Linux Kernel 브랜치에 대한 전체 그래프 세트를 종합하면, 가장 안전한 Linux Kernel(취약점이 가장 적은 Kernel)은 최신[편집자 주: 이 글 작성 당시] 버전인 5.12인 것 같습니다.

  2. 많은 취약점의 영향은 브랜치 및 'Lifecycle 종료' 날짜를 넘어 확장됩니다. 예를 들어, 2.6의 경우 마지막 릴리스인 2.6.39는 2011년 5월이었습니다. 같은 해 8월에 2.6.39.4 버전에서 'Lifecycle 종료'로 지정되었습니다. 그럼에도 불구하고 취약점에 대한 보고는 멈추지 않았고 2016년부터 증가했습니다.

  3. 취약점을 지점별로 비교하는 것은 최선의 방법이 아닙니다. 브랜치는 연속적이며, 한 브랜치의 최종 릴리스가 다음 브랜치의 기초를 형성합니다. 예를 들어, 5.0은 4.x 브랜치의 연속이고, 4.0은 3.19의 연속이며, 오래 지속된 2.6 브랜치의 최종 릴리스인 2.6.39는 3.0에서 계속 이어집니다. 코드 베이스가 이렇게 브랜치되는 방식은 각 후속 브랜치마다 버그가 점차 줄어든다는 것을 의미합니다.

 

Kernel 재부팅, 시스템 다운타임 또는 예정된 유지 보수 기간 없이 취약성 패치를 자동화하고 싶으신가요?

TuxCare로 라이브 패치에 대해 알아보기

TuxCare 게스트 작가 되기

시작하기

메일

가입

4,500

Linux & 오픈 소스
전문가!


뉴스레터 구독하기