NVDのAPIを叩いて脆弱性情報を自動収集してみる

# 背景

　私の勤めている会社では脆弱性の情報収集はメーリングリストベースのサービスを利用しているが、提供される形がテキストベースであり、自動化には即していない形となっていて効率化が難しい状態。脆弱性情報の収集自動化のための試みとして、NVDのAPIを叩いてCVE情報を自動取得する。
（もうやられている方いるのは知っていますが勉強のため自分でやる）

# NVDのAPI

　NVDのデータを自動取得するための手段は[NVD Data Feeds](https://nvd.nist.gov/vuln/data-feeds)に記載されている。その中でAPIの情報は[こちら](https://nvd.nist.gov/vuln/data-feeds#APIS)にある。APIは主にCVEとCPEの2つが用意されている。共通プラットフォーム一覧CPE(Common Platform Enumeration)は脆弱性をさすものではないので、今回はCVEのAPIを利用する。
# CVEのAPI

　CVEのAPIはhttps://nvd.nist.gov/developers/vulnerabilities にまとめられている。この中で用意されているAPIは2つ。

## Retrieve a specific CVE
　個別のCVE番号の情報を取得するためのAPI。今回は脆弱性情報の収集を目的にしているため、こちらは使わない。

## Retrieve a collection of CVE
　条件にマッチした範囲のCVEをjsonでまとめて返してくれるAPI。今回はこちらを使う。パラメータは以下の通り。（とりあえず自分が使うパラメータだけ記載）
パラメータはすべてGETで投げればよいらしい。

| Parameter | Description |
| :--- | :--- |
| apiKey | APIキーなのだが、NVDのAPIの利用には必要とされていない。いつ使うんだろう。 |
| pubStartDate | CVEがリリースされた日で検索する際の、検索期間の開始日。これを設定すると後述のpubEndDateも必須になる。 |
| pubEndDate | CVEがリリースされた日で検索する際の、検索期間の終了日。これを設定すると前述のpubStartDateも必須になる。|
| resultsPerPage | 1回のリクエストで返ってくるCVEの最大件数を設定する。デフォルトは20で最大は2000が設定される。 |
| startIndex | 検索を開始するインデックスを指定する。検索件数がresultsPerPageを超える場合に、2回目のリクエストで指定する。 |

# サンプルスクリプト

　1日1回実行して新規に発行されたCVE情報を収集することをイメージして以下のサンプルスクリプトを作ってみた。検索範囲は日本時間でスクリプト実行の前日の中で発行されたCVEを検索する

```python:nvd_apitest.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import requests
import json
import datetime

def callCvesApi(pubStartDate=None,pubEndDate=None,resultsPerPage=2000):
    url=u'https://services.nvd.nist.gov/rest/json/cves/1.0/'
    if pubStartDate==None or pubEndDate==None:
        now=datetime.datetime.now()
        pubStartDate=datetime.datetime(now.year,now.month,now.day-1,0,0,0)
        pubStartDate=datetime.datetime(now.year,now.month,now.day,0,0,0)
    pubStartDateStr=pubStartDate.sptftime('%Y-%m-%d 00:00:00 000 UTC+09:00')
    pubEndDateStr=pubEndDate.sptftime('%Y-%m-%d 00:00:00 000 UTC+09:00')
    urlstr=u'{0}?modStartDate={1}&modEndDate={2}'.format(url,pubStartDateStr,pubEndDateStr)
    response=requests.get(urlstr)
    values=json.loads(response.text)
    return values

if __name__ == '__main__':
    today_CVEs=callCvesApi()
    print(today_CVEs)
```

レスポンスは以下のようになる。レスポンスの見方はまた調べてまとめる。キーポイントあたりだけ//から始まるコメントを入れてます。

```json
{
  'resultsPerPage': 3, //検索数
  'startIndex': 0,
  'totalResults': 3,
  'result': {
    'CVE_data_type': 'CVE',
    'CVE_data_format': 'MITRE',
    'CVE_data_version': '4.0',
    'CVE_data_timestamp': '2021-11-15T07:50Z',
    'CVE_Items': [{
      'cve': {
        'data_type': 'CVE',
        'data_format': 'MITRE',
        'data_version': '4.0',
        'CVE_data_meta': {
          'ID': 'CVE-2021-42739', //CVE番号 
          'ASSIGNER': 'cve@mitre.org'
        },
        'problemtype': {
          'problemtype_data': [{
            'description': [{
              'lang': 'en',
              'value': 'CWE-787'
            }]
          }]
        },
        'references': {
（中略（
        },
        'description': {
          'description_data': [{ //説明
            'lang': 'en',
            'value': 'The firewire subsystem in the Linux kernel through 5.14.13 has a buffer overflow related to drivers/media/firewire/firedtv-avc.c and drivers/media/firewire/firedtv-ci.c, because avc_ca_pmt mishandles bounds checking.'
          }]
        }
      },
      'configurations': {
        'CVE_data_version': '4.0',
        'nodes': [{
          'operator': 'OR',
          'children': [],
          'cpe_match': [{
            'vulnerable': True,
            'cpe23Uri': 'cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*', //脆弱性の対象プラットフォーム
            'versionEndIncluding': '5.14.13',
            'cpe_name': []
          }]
        }]
      },
      'impact': { //脆弱性のCVSS値等
        'baseMetricV3': {
          'cvssV3': {
            'version': '3.1',
            'vectorString': 'CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H',
            'attackVector': 'LOCAL',
            'attackComplexity': 'LOW',
            'privilegesRequired': 'LOW',
            'userInteraction': 'NONE',
            'scope': 'UNCHANGED',
            'confidentialityImpact': 'HIGH',
            'integrityImpact': 'HIGH',
            'availabilityImpact': 'HIGH',
            'baseScore': 7.8,
            'baseSeverity': 'HIGH'
          },
          'exploitabilityScore': 1.8,
          'impactScore': 5.9
        },
        'baseMetricV2': {
          'cvssV2': {
            'version': '2.0',
            'vectorString': 'AV:L/AC:L/Au:N/C:P/I:P/A:P',
            'accessVector': 'LOCAL',
            'accessComplexity': 'LOW',
            'authentication': 'NONE',
            'confidentialityImpact': 'PARTIAL',
            'integrityImpact': 'PARTIAL',
            'availabilityImpact': 'PARTIAL',
            'baseScore': 4.6
          },
          'severity': 'MEDIUM',
          'exploitabilityScore': 3.9,
          'impactScore': 6.4,
          'acInsufInfo': False,
          'obtainAllPrivilege': False,
          'obtainUserPrivilege': False,
          'obtainOtherPrivilege': False,
          'userInteractionRequired': False
        }
      },
      'publishedDate': '2021-10-20T07:15Z',
      'lastModifiedDate': '2021-11-14T06:15Z'
    }
（以下略）
```