みつきんのメモ

組み込みエンジニアです。Interface誌で「My オリジナルLinuxの作り方」連載中

YoctoProject CVE Checkについて調べる

はじめに

bitbakeではビルドするパッケージの脆弱性CVEによってチェックを行う機能がある。

使い方

local.confに下記を追加する。

INHERIT += "cve-check"

すると、各パッケージのタスクにdo_cve_checkが追加される。

bitbakeを実行すると、このタスクが実行され、未対策の脆弱性があった場合には警告として表示される。

実行例

この機能を実装しているcve-check.bbclassに記載されているコマンドの実行例を示す。

$ bitbake -c cve_check openssl
$ bitbake core-image-sato
$ bitbake -k -c cve_check universe

出力結果

CVEチェックの結果はログファイルとして保存される。

各パッケージに対する出力結果は${WORK_DIR}/temp/cve.logとして保存される。

イメージに対する出力結果はbuild/tmp/deploy/images/イメージ名.cveとして保存される。ラスベリーパイ4向けのcore-image-baseの場合はbuild/tmp/deploy/images/raspberrypi4/core-image-base-raspberrypi4.cveの様になる。

出力結果の例を下記に示す。

PACKAGE NAME: libvorbis
PACKAGE VERSION: 1.3.6
CVE: CVE-2020-20412
CVE STATUS: Patched
CVE SUMMARY: lib/codebook.c in libvorbis before 1.3.6, as used in StepMania 5.0.12 and other products, has insufficient array bounds checki\
ng via a crafted OGG file.
CVSS v2 BASE SCORE: 4.3
CVSS v3 BASE SCORE: 6.5
VECTOR: NETWORK
MORE INFORMATION: https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2020-20412

PACKAGE NAME: pixman
PACKAGE VERSION: 1_0.38.4
CVE: CVE-2013-0800
CVE STATUS: Unpatched
CVE SUMMARY: Integer signedness error in the pixman_fill_sse2 function in pixman-sse2.c in Pixman, as distributed with Cairo and used in Mo\
zilla Firefox before 20.0, Firefox ESR 17.x before 17.0.5, Thunderbird before 17.0.5, Thunderbird ESR 17.x before 17.0.5, SeaMonkey before \
2.17, and other products, allows remote attackers to execute arbitrary code via crafted values that trigger attempted use of a (1) negative\
 box boundary or (2) negative box size, leading to an out-of-bounds write operation.
CVSS v2 BASE SCORE: 6.8
CVSS v3 BASE SCORE: 0.0
VECTOR: NETWORK
MORE INFORMATION: https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2013-0800

適用済みのものはPatched、そうじゃないものはUnpatchedとなっている。

検出の仕組み

cvecheckerがベースとなっているらしい。

これは、オンラインからCVEのデータベースを取得し、SQLite3で脆弱性情報に対してマッチするものがあるかを検索するというもの。

プロダクト名とバージョンの組み合わせによって関係ある脆弱性があるかどうかを簡易的に判定するだけなので、このチェックによって全ての脆弱性に関する危険性を排除できるわけではないとされている。

Yoctoでの実装

do_cve_check

python do_cve_check () {
    """
    Check recipe for patched and unpatched CVEs
    """

    if os.path.exists(d.getVar("CVE_CHECK_DB_FILE")):
        try:
            patched_cves = get_patches_cves(d)
        except FileNotFoundError:
            bb.fatal("Failure in searching patches")
        whitelisted, patched, unpatched = check_cves(d, patched_cves)
        if patched or unpatched:
            cve_data = get_cve_info(d, patched + unpatched)
            cve_write_data(d, patched, unpatched, whitelisted, cve_data)
    else:
        bb.note("No CVE database found, skipping CVE check")

}

get_patches_cves(d)check_cves(d, patched_cves)によってチェックしているようだ。

get_patches_cves

レシピに含まれるファイル名にCVE-IDが含まれているか、ファイル中の文字列にCVE-IDが含まれているものは対策済みとして扱うようにしている。 どちらも正規表現による文字列マッチとなっている。

ファイル名は下記の条件でマッチしている。CVE-1234-211432やみたいなものが引っかかるようになっている。CVEに対する大文字/小文字の混在もOKのようだ。

cve_file_name_match = re.compile(".*([Cc][Vv][Ee]\-\d{4}\-\d+)")

ファイル中の文字列は下記の条件でマッチしている。

cve_match = re.compile("CVE:( CVE\-\d{4}\-\d+)+")

こちらはCVE: CVE-2020-13362のようなものが引っかかるようになっている。

check_cves

こちらはホワイトリストに含まれていれば無視し、それ以外のものをCVE_CHECK_DB_FILEで指定されているデータベースで検索する。

ホワイトリストには下記の2種類がある。

  • CVE_CHECK_WHITELIST(CVE-ID)
  • CVE_CHECK_PN_WHITELIST(パッケージ名)

CVE_CHECK_WHITELISTは特定のCVEを無視したい場合にCVE-IDを指定する。 CVE_CHECK_PN_WHITELISTは特定のパッケージに関するCVEを無視したい場合にパッケージ名を指定する。

データベースの検索条件はCVE_PRODUCTCVE_VERSIONの組み合わせとなっている。

CVE_PRODUCTはデフォルトではBPNが使用される。CVEデータベース上のプロダクト名とBPNがマッチしない場合や、1つのレシピで複数のパッケージを生成する場合などは明示的に設定する必要がある。

flacのレシピでは下記のように設定されている。

CVE_PRODUCT = "libflac flac"

CVE_VERSIONはデフォルトではPVが使用される様になっている。こちらもCVEデータベース上のバージョン表記と一致しない場合などは明示的に設定する必要がある。

CVE_CHECK_DB_FILEはSQLite3のデータベースとなっており、筆者が実行した環境では「"${DL_DIR}/CVE_CHECK/nvdcve_1.1.db"」が指定されている。 このファイルはcve-update-db-nativeによりダウンロードされる。

CVE対策をレシピに含める場合

get_patches_cvesのロジックにより、CVE対策としてパッチを作成する場合、ファイル名を下記のようにするか、

CVE-2020-12723.patch

コメント中に下記のような文字列を含めるかすると、対策済みとして扱われるようになる。

CVE: CVE-2020-13362

まとめ

bitbakeではCVE情報によって脆弱性をチェックする機能がある。

基本的にはプロダクト名とバージョンによって、関係あるCVEが対策済みかどうかを判断する。 レシピ中のSRC_URIで指定されたファイル名やコメントなどによって、データベース上では未対策のCVEでも、対策済みと扱うことができるようになっている。

また、特定のCVEや特定のパッケージをホワイトリストに追加することによって、未対策のものであっても問題ないものに関しては明示的にチェックしないようにすることもできる。

基本的には「プロダクト名+バージョン」や、ファイル名やコメントなどによる簡易的なチェックであり、 これらのチェック機能によって全ての脆弱性を網羅できるわけではないが、これで得られた情報をもとに脆弱性に対する方針を立てることができる。