KazuminEngine

プログラマーの日記

vulsのコアのアルゴリズムについて

こんにちは、

かずみん (@warugaki_k_k) | Twitter

です。

ブログのタイトルは、ほんとは違うかも。本当は全然コアじゃないのかもしれないです。

vulsって結局どうやって脆弱性判断しているのかわからなくて、コード読みました。そしてわかったことを書きます。

どっかで読んだ記憶によると、OVALと言うもので検知されてる。何それわからない。

ovalのデータ構造やら。わかりませんでした。

(ここで、コードを読むまでの予想だが、バージョンとcveが一対一やら、なんやかんやになってる。と予想。それをどうやって流のかが不明。

まー、scan時に取得されたversionが脆弱性を含むかをfillするのは、report時なので、そのへんのコードを読むと、こんなのがgetDefsByPackNameFromOvalDB関数の

 245 definitions, err := driver.GetByPackName(r.Family, r.Release, req.packName, req.arch)

多分ここで、検査してるんだろうけど、どうやら、これは、goval-dictionaryのGetByPackName()を呼び出しているようだ。

ま、見てみると。ここで色々データを取ってくるみたい。

 func (o *RedHat) GetByPackName(driver *gorm.DB, osVer, packName, _ string) ([]models.Definition, error) {
    osVer = major(osVer)
    packs := []models.Package{}
    err := driver.Where(&models.Package{Name: packName}).Find(&packs).Error
    if err != nil && err != gorm.ErrRecordNotFound {
        return nil, err
    }

    defs := []models.Definition{}
    for _, p := range packs {
        def := models.Definition{}
        err = driver.Where("id = ?", p.DefinitionID).Find(&def).Error
        if err != nil && err != gorm.ErrRecordNotFound {
            return nil, err
        }

        root := models.Root{}
        err = driver.Where("id = ?", def.RootID).Find(&root).Error
        if err != nil && err != gorm.ErrRecordNotFound {
            return nil, err
        }

        if root.Family == config.RedHat && major(root.OSVersion) == osVer {
            defs = append(defs, def)
        }
    }

    for i, def := range defs {
        adv := models.Advisory{}
        err = driver.Model(&def).Related(&adv, "Advisory").Error
        if err != nil && err != gorm.ErrRecordNotFound {
            return nil, err
        }

        cves := []models.Cve{}
        err = driver.Model(&adv).Related(&cves, "Cves").Error
        if err != nil && err != gorm.ErrRecordNotFound {
            return nil, err
        }
        adv.Cves = cves

        bugs := []models.Bugzilla{}
        err = driver.Model(&adv).Related(&bugs, "Bugzillas").Error
        if err != nil && err != gorm.ErrRecordNotFound {
            return nil, err
        }
        adv.Bugzillas = bugs

        cpes := []models.Cpe{}
        err = driver.Model(&adv).Related(&cpes, "AffectedCPEList").Error
        if err != nil && err != gorm.ErrRecordNotFound {
            return nil, err
        }
        adv.AffectedCPEList = cpes

        defs[i].Advisory = adv

        packs := []models.Package{}
        err = driver.Model(&def).Related(&packs, "AffectedPacks").Error
        if err != nil && err != gorm.ErrRecordNotFound {
            return nil, err
        }
        defs[i].AffectedPacks = filterByMajor(packs, osVer)

        refs := []models.Reference{}
        err = driver.Model(&def).Related(&refs, "References").Error
        if err != nil && err != gorm.ErrRecordNotFound {
            return nil, err
        }
        defs[i].References = refs
    }

    return defs, nil
}

なになに、パッケージネームからディフィニッション(数字)をデータベースから取ってきて、そのディフィニッションからcveを取って来る。あ、パッケージ名に対するcveが影響するバージョンを取ってくる(def.AffectedPacks)。データベース操作がいまいち読めないから、間違ってそうだが、大筋そうだろう。絶対間違ってるけど。

これで比較する準備ができたので、vulsのisOvalDefAffected()で現在のバージョンとOVALで取ってきたバージョンを比較すればよくて。lessThan関数でgithub.com/knqyf263/go-deb-versionライブラリを使って、比較する。これで、検知が可能

と言うことで、初めの予想と同じで、バージョンとcveが一対一やら、なんやかんやになってる。それはgoval-dictionaryがやってくれているんだろうけど、わからない。今度、そこ読んでみるかと。

感想としては、バージョンごとにcveやらを持っていて、それで全部比較するとなると、すごい計算量になりそう。。。

ま、私は、vulsをこれぜ完全理解したつもりです。

vuls何もわからない......

この記事は10分で書いたので、適当です。何か、間違いや、指摘があったら、教えてほしいです。

あと、こんな記事もあります。

Vulsのコードを読む その4 Vuls reportを調べてみた - Security Index

こちらの記事は、cpeから、cveを検知する話になってます。