LoginSignup
16
9

More than 3 years have passed since last update.

NVD公開のREST APIを用いて脆弱性情報を取得する

Last updated at Posted at 2021-01-09

背景

NVD (National Vulnerability Database) は、NISTが管理している脆弱性情報のデータベースであり、ソフトウェアやハードウェアの脆弱性情報を確認する際、NVDにお世話になることは非常に多いです。

そんなNVDですが、各CVEの脆弱性の情報が取得できればいいなあと思っていたところ、
NVDがREST APIを2019年9月に公開しているのを見つけました。
https://nvd.nist.gov/General/News/New-NVD-CVE-CPE-API-and-SOAP-Retirement

結構簡単に情報を取得できたので、情報共有も兼ねてプログラムを公開します。

実行環境

  • Ubuntu 18.04 LTS
  • Python 3.9.1

ざっくりセキュリティ用語

厳密な意味はググれば出てくると思うので、ここではざっくりレベルで。

  • CVE (Common Vulnerabilities and Exposures) : 特定の脆弱性に割り当てられる識別子
  • CWE (Common Weakness Enumeration) : 脆弱性のカテゴリ。例えばCWE-89はSQLインジェクションを指す。
  • CPE (Common Platform Enumeration) : ハードウェア、ソフトウェアを識別するための仕様。CPEを使うことで、例えばどのOSSのどのバージョンで脆弱性の影響が出るか分かる。
  • CVSS (Common Vulnerability Scoring System) : 脆弱性の深刻度を表す指標。深刻度のスコアや攻撃難度とか。NVDではCVSS v3とCVSS v2が公開されている

REST API

あるCVEの情報を取得したい場合は下記のリクエストを実行します。

https://services.nvd.nist.gov/rest/json/cve/1.0/<cveId>

条件に合致するCVEの情報を取得したい場合は、下記のリクエストを実行します。
先程のリクエストは"cve"であることに対し、こちらは"cves"であることに注意。

https://services.nvd.nist.gov/rest/json/cves/1.0

例えば、CPEに合致するCVE情報を取得したい場合は下記のようなリクエストを送信すれば良いです。
例:OpenSSL1.1.1cのCVE情報を取得したい場合

https://services.nvd.nist.gov/rest/json/cves/1.0?cpeMatchString=cpe:2.3:a:openssl:openssl:1.1.1c:*:*:*:*:*:*:*

レスポンスはJSON形式で返ってきます。

詳しくは下記。
https://csrc.nist.gov/CSRC/media/Projects/National-Vulnerability-Database/documents/web%20service%20documentation/Automation%20Support%20for%20CVE%20Retrieval.pdf

Current Descriptionを取得する

NVDに記載されている脆弱性の説明(Current Description)を取得したい場合は、レスポンスのJSONデータに下記のようにアクセスします。

json_data['result']['CVE_Items'][x]['cve']['description']['description_data'][0]['value'] 

※ x はCVEリストのインデックス

CWEを取得する

CWEを取得したい場合は、レスポンスのJSONデータに下記のようにアクセスします。

json_data['result']['CVE_Items'][x]['cve']['problemtype']['problemtype_data'][0]['description'][0]['value']

※ x はCVEリストのインデックス

CVSSv3 情報を取得する

BaseScoreを取得したい場合は、レスポンスのJSONデータに下記のようにアクセスします。

json_data['result']['CVE_Items'][x]['impact']['baseMetricV3']['cvssV3']['baseScore']

※ x はCVEリストのインデックス

VectorString(例:AV:N/AC:M/Au:N/C:P/I:P/A:P)を取得したい場合は、レスポンスのJSONデータに下記のようにアクセスします。

json_data['result']['CVE_Items'][x]['impact']['baseMetricV3']['cvssV3']['vectorString']

※ x はCVEリストのインデックス

CVSSv2 情報を取得する

BaseScoreを取得したい場合は、レスポンスのJSONデータに下記のようにアクセスします。

json_data['result']['CVE_Items'][x]['impact']['baseMetricV2']['cvssV2']['baseScore']

※ x はCVEリストのインデックス

VectorStringを取得したい場合は、レスポンスのJSONデータに下記のようにアクセスします。

json_data['result']['CVE_Items'][x]['impact']['baseMetricV2']['cvssV2']['vectorString']

※ x はCVEリストのインデックス

CPEの条件を満たすCVEの情報を取得

上記を踏まえて、CPEに合致するCVEの情報を取得し、出力するプログラムを実装しました。

get_software_vuln.py
#!/usr/bin/env python
import requests
import json
import argparse
import textwrap


def main():
    #  コマンドライン引数Parse
    parser = argparse.ArgumentParser()
    parser.add_argument('cpe_name', help='CPE Name')
    args = parser.parse_args()

    #  CPEに対応した脆弱性情報をNVDからJSON形式で取得
    cpe_name = args.cpe_name
    api = 'https://services.nvd.nist.gov/rest/json/cves/1.0?cpeMatchString={cpe_name}'
    uri = api.format(cpe_name=cpe_name)
    response = requests.get(uri)
    json_data = json.loads(response.text)

    vulnerabilities = json_data['result']['CVE_Items']
    for vuln in vulnerabilities:
        cve_id = vuln['cve']['CVE_data_meta']['ID']  # CVE-IDを取得
        current_description = vuln['cve']['description']['description_data'][0]['value']  # Current Descriptionを取得
        cwe_id = vuln['cve']['problemtype']['problemtype_data'][0]['description'][0]['value']  # CWE-IDを取得

        #  CVSS v3の情報があればBaseScoreとVectorStringを取得
        if 'baseMetricV3' in vuln['impact']:
            cvssv3_base_score = vuln['impact']['baseMetricV3']['cvssV3']['baseScore']
            cvssv3_vector_string = vuln['impact']['baseMetricV3']['cvssV3']['vectorString']

        else:
            cvssv3_base_score = None
            cvssv3_vector_string = None

        #  CVSS v2のBaseScoreとVectorStringを取得
        cvssv2_base_score = vuln['impact']['baseMetricV2']['cvssV2']['baseScore']
        cvssv2_vector_string = vuln['impact']['baseMetricV2']['cvssV2']['vectorString']

        #  出力
        print('---------')
        text = textwrap.dedent('''
        CVE-ID:{cve_id}
        CWE-ID:{cwe_id}
        CVSSv3 BaseScore:{cvssv3_base_score} CVSSv3 VectorString:{cvssv3_vector_string}
        CVSSv2 BaseScore:{cvssv2_base_score} CVSSv2 VectorString: {cvssv2_vector_string}
        Current Description:
        {current_description}
        ''')
        print(text.format(cve_id=cve_id, cwe_id=cwe_id, cvssv3_base_score=cvssv3_base_score, cvssv3_vector_string=cvssv3_vector_string,
                          cvssv2_base_score=cvssv2_base_score, cvssv2_vector_string=cvssv2_vector_string, current_description=current_description))
        print('---------')


main()

下記のように実行できます。

python get_software_vulns.py <CPE>

例:openssl 1.1.1cについて実行する場合

python get_software_vulns.py cpe:2.3:a:openssl:openssl:1.1.1c:*:*:*:*:*:*:*

GitHub

https://github.com/riikunn1004/NVDAPI
にてコードを公開しています。参考まで。

下記のPythonプログラムを公開しています。
get_software_vuln.py:上記のプログラム
get_cve_info.py : 指定したCVEの情報を取得するプログラム

まとめ

REST APIを用いて脆弱性情報を取得する方法について述べました。
今回記載した内容は一部なので、詳しくはNVDが公開しているドキュメントをご覧ください。
脆弱性情報を管理するためのツールはいろいろとありますが、
このREST APIを活用して自身のニーズにあったツールを自分で開発するのも有りですね。

16
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
9