Vulsのコードを読む その1 全体像の把握, Vulsのコードを読む その2 go-cve-dictionary を理解する、バグを見つけてプルリクを出してマージされるまで, Vulsのコードを読む その3 Vuls scanを調べてみた の続きになります。
今回はVuls reportの部分を調べてみました。
Vuls reportはvuls scanで取得したscan対象のサーバーやコンテナ情報を元にどんな脆弱性が含まれるのかを調査し、保存やメール・slackで通知する機能を行なっています。
忙しい人のために先に書いておくと、report作成のメインとなる部分はvuls/commands/report.go のFillCveInfos()の部分です。
それ以外の部分はメール、slackなどの通知方法の設定確認やエラーチェックなどがメインに行われています。
slack, chatwork, Telegramなどに通知する方法が気になる方はWrite()を見るとgoでどう書けばいいのかわかります。
(更新 2020/07/20 v0.11.0へのアップデートの内容(go-msfdb)を一部取り込みました)
- Vuls Report
- L213~L389 設定情報の読み込み、エラーチェック、DB接続
- L437 FillCveInfos() reportの作成
- FillLibrary() ライブラリの脆弱性取得 (trivy)
- FillWithOval() 各OS(debian, redhat等)毎のCVEの取得、カウント (goval-dictionary)
- パッチが未提供のものかどうかの判定
- fillVulnByCpeURIs() cpeURIsに含まれるcpeからCVEの取得 (go-cve-dictionary)
- Github Secruity Alerts とWordPressのCVEの取得
- FillWithGost() Security Trackerの情報と突き合せてCVEの取得 (gost)
- fillCveDetail() NVDとJVNのCVEの詳細情報とCERの情報を取得 (go-cve-dictionary)
- FillWithExploit() CVEのexploitコードの取得 (go-exploitdb)
- FillWithMetasploit() rapid7のexploitコードの取得 (go-msfdb)
- fillCweDict() CVEに紐づけられたCWEの取得
- L413 Write() slack, メール, telegramなどにreportを出力
- 感想
Vuls Report
vuls/commands/report.go の Execute() の部分からみていきます。
メインとなるFillCveInfos() 以外の前半は簡単な説明のみしたいと思います。
L213~L389 設定情報の読み込み、エラーチェック、DB接続
L222~L227ではvuls repot実行時の引数で渡されたgo-cve-dictionaryやgoval-dictionaryの設定情報を読み込み(設定ファイルの上書き)をします。
L246~L320では設定ファイルからメールやslackなど、どこに通知をするのかの設定を読み込みこみ、エラーチェックします。
現在、通知先として設定できるのは以下の11個。
[-to-email]
[-to-http]
[-to-slack]
[-to-stride]
[-to-hipchat]
[-to-chatwork]
[-to-telegram]
[-to-localfile]
[-to-s3]
[-to-azure-blob]
[-to-saas]
少し気になったのは一番最後の[-to-saas]の部分。
saasは何を指しているのかというと Future Vulsのことのようです。
L168のSetFlags()の部分を見てみると以下のように書かれています。
Upload report to Future Vuls(https://vuls.biz/) before report
Future Vulsと連携するためのもののようです。
Future Vulsも(Windows以外の)スキャン、レポート作成の部分はOSS Vulsをほぼそのまま利用しているのかもしれません。
L333~L349ではvuls scanでscanした結果データの読み込み、エラーチェックを行います。
L375~L418ではgo-cve-dictionary, goval-dictionary, gost, go-exploitdb, go-msfdb
がサーバーモードで起動しているかどうかのエラーチェックをします。
L419のNewDBClient()でそれぞれのDBヘの接続を行います。
go-msfdbはvulsのv0.11.0(2020/07/13)から追加された機能(ライブラリ)でrapid7のVulnerability & Exploit DatabaseをクローリングしてMetasploitでエクスプロイトできるCVE一覧を取得できるようです。
CVEで一覧化されたデータはmsfdb-listで公開されており、rapid7で更新があれば自動でアップデートされるようです。
L437 FillCveInfos() reportの作成
L437のFillCveInfos()でreportの作成を行います。
FillCveInfos()の中で利用されているFillCveInfo() でvuls scanで取集したライブラリ情報、パッケージ情報、コンテナ情報とそれぞれの脆弱性DBを利用して脆弱性(CVE)の取得とカウントを行います。
FillLibrary() ライブラリの脆弱性取得 (trivy)
L156 FillLibrary()でライブラリの脆弱性の取得、VulnInfo{}への整形を行なっています。
前回も書きましたが、ライブラリの脆弱性の部分はtrivyの機能を利用しています。
(主にこのdetect() の部分が参考になります。)
FillWithOval() 各OS(debian, redhat等)毎のCVEの取得、カウント (goval-dictionary)
L163 FillWithOval() で各OS(debian, redhat等)毎のCVEの取得、カウントを行います。
ここではgoval-dictionaryの機能を利用しています。
この部分は例えば、Ubuntuの場合、FillWithOval()でメジャーバージョン(14,16,18)毎に個別設定をして、OVALDBとの突き合わせを行なっているようです。
(govalはまだ中身をちゃんと見れてません・・・)
パッチが未提供のものかどうかの判定
L170のScannedCvesは上記のFillLibrary()、FillWithOval() でスキャンしたCVEが入っています。OVALからはパッチが提供済みかどうかの判断できる情報も取得できるようなのでパッチが提供されていないものはNotFixedYetのステータスが付けられます。
fillVulnByCpeURIs() cpeURIsに含まれるcpeからCVEの取得 (go-cve-dictionary)
L179の fillVulnByCpeURIs()ではcpeURIsに含まれるcpeからCVEの取得を行なっています。
cpeからCVEの取得はgo-cve-dictionaryを利用しています。
cpeURIsには設定ファイル(servers, containers)に記載したもの、OWASP Dependency Checkによるものが記載されています。
Vulsでcpeを使ったスキャンは以下のドキュメントに書かれています。
Scan vulnerabilites of non-OS packages · Vuls
Github Secruity Alerts とWordPressのCVEの取得
L185~L190ではGithub Secruity Alerts とWordPressのCVEの取得が行われています。
FillCveInfo()の引数のintegrationsが可変長引数になっており、現在はGithub Security AlertsとWordPressが引数として与えられています。
ここは今後も拡張しやすく何か追加ができそうです。
ちなみに、Github Security AlertはFillGitHubSecurityAlerts()で処理が行われています。
GithubのAPIを利用して情報を取得し、VulnInfo{}の形式に変換することが主な処理内容のようです。
FillWithGost() Security Trackerの情報と突き合せてCVEの取得 (gost)
L193のFillWithGost()ではRedhat, Debian, MicrosoftのSecurity Trackerの情報とスキャンした結果の情報を突き合せてCVEの取得を行います。
この部分の処理は主にgost (go-security-tracker)の機能を利用しています。
fillCveDetail() NVDとJVNのCVEの詳細情報とCERの情報を取得 (go-cve-dictionary)
L201のfillCveDetail()ではgo-cve-dictionaryを使って、NVDとJVNのCVEの詳細情報とCERT(US-CERT, kb.cert.org, JPCERT)の情報を取得を行います。
FillWithExploit() CVEのexploitコードの取得 (go-exploitdb)
L206のFillWithExploit()ではgo-exploitdbを使ってCVEのexploitコードの取得を行います。
go-exploitdbでは以下の3つからCVEに紐付くexploitコードの収集を行なっています。
・ExploitDB(OffensiveSecurity)
・GitHub Repositories
・Awesome Cve Poc
Github RepositoriesはGithubのrepository名からCVE番号っぽいものを探してきて保存するということを行なっているようです。
CVE番号っぽいものは
CVE : CVE- 2018-7198、CVE : [CVE-2018- 11034]、CVE: 2017-13056
などを正規表現を使って特定しているようです。
(この辺りが参考になりそうです)
Awesome Cve Pocはqazbnm456さんが個人集めているPoCが一覧になっているrepositoryのようです。
FillWithMetasploit() rapid7のexploitコードの取得 (go-msfdb)
L214のFillWithMetasploit()では先ほど少し説明したgo-msfdbを使って収集したrapid7のMetasploitのモジュール一覧と突合させます。
fillCweDict() CVEに紐づけられたCWEの取得
L221のfillCweDict()ではCVEに紐づけられているCWEを取得します。
CWEのID, Name, Descriptionの辞書情報はvuls/cwe/en.goで定義されているため、この情報も使ってreportの作成を行います。
L413 Write() slack, メール, telegramなどにreportを出力
report.goに戻り、L413のWrite()で最後にreportの出力を行います。
出力先は事前に設定したslackやメール、telegramなどに出力をします。
例えば、report/slack.goのWrite()を見ればgo言語でslack通知を実現するための方法の参考になるかもしれません。
感想
gost
redhat, debianの脆弱性を調査したい訳ではない時でもdebian Security TrackerのNoteやSourceに書かれているリンク、bugzillaから脆弱性(CVE)の詳細情報を確認することがある。
gostもserver modeからCVEの検索ができるので少し変わった用途ではあるがgostを使ってCVE検索し脆弱性調査を行うのも良いかもと思いました。
(それよりもVuls本体にCVEの横断検索機能のようなものがあると個人的には嬉しいかも)
Awesome Web Security
Awesome Cve Pocのqazbnm456さんのAwesome Web Securityが面白そう。
awesome-ctfというものもあるみたい。
その1、その2、その3はこちら。
Vulsのコードを読む その1 全体像の把握 - Security Index
Vulsのコードを読む その2 go-cve-dictionary を理解する、バグを見つけてプルリクを出してマージされるまで - Security Index
Vulsのコードを読む その3 Vuls scanを調べてみた - Security Index
Twitter アカウント
Security Index (@security_index) | Twitter
プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)