
MyJVN APIを利用した脆弱性情報収集
はじめに
藤本です。
のんのんびよりは二周目でも癒やされます。
概要
みなさん、ソフトウェアの脆弱性対応をどのように取り組んでいますか? ソフトウェアの多くには潜在的なバグが内在していて、そのバグ、脆弱性を利用されることでシステムが停止したり、情報を漏洩したり、最悪、システムが乗っ取られると言った様々なリスクを抱えています。これらのバグ、脆弱性の情報は様々な組織が運営する脆弱性情報データベースにより公開されることで私達は情報を知ることができます。ただ、脆弱性情報は多くのソフトウェアで小さいものから大きいものまであり、過去3ヶ月で1,790件(NVD検索結果)が報告されています。このように日々報告される情報から必要な情報を探すのは大変な労力を要します。
脆弱性データベース
有名な脆弱性データベースは以下のようなものがあります。
- CVE(Common Vulnerabilities and Exposures) MITRE社が管理する脆弱性情報データベースです。脆弱性情報の詳細を調べる時にCVE-西暦年-XXXX形式の番号を見たことがある方も多いのではないでしょうか。この番号はCVEによって発行されています。
-
NVD(National Vulnerability Database) NISTが管理する脆弱性情報データベースです。Common Vulnerability Scoring System(通称CVSS)という危険度を定量的な値で公開しています。
-
JVN(Japan Vulnerability Notes) JPCERT/CCとIPAが共同で管理する脆弱性情報データベースです。日本語で公開されているのでまずはJVNを確認される方も多いと思います。
-
JVN iPedia JVNと同じ団体により管理する脆弱性情報データベースです。JVNの管理団体に加えて、CERT/CC、CPNI、NVDといったところからも情報を収集しており、JVNより多い情報が収集されています。
MyJVN API
JVN iPediaは脆弱性情報を収集、検索可能なWEB APIを用意しています。WEB APIを利用することで製品ID、公開日、更新日といった検索情報を与えることで脆弱性情報の一覧や詳細をXML形式で受け取ることができます。APIの種類は多くなく、非常に簡単に扱えます。
以下に代表的なAPIをご紹介します。
getVendorList
ベンダ名の一覧を取得します。ベンダ名によりフィルタリングが可能です。
例
# curl -s http://jvndb.jvn.jp/myjvn\?method\=getVendorList <!--?xml version="1.0" encoding="UTF-8" ?--> 〜〜〜 省略 〜〜〜
今現在、11628件のベンダが登録されています。
getProductList
製品名の一覧を取得します。ベンダIDや製品名によってフィルタリングが可能です。
例
curl -s http://jvndb.jvn.jp/myjvn\?method\=getProductList |head -20 <!--?xml version="1.0" encoding="UTF-8" ?--> 〜〜〜 省略 〜〜〜
現在、24670件の製品が登録されています。
getVulnOverviewList
脆弱性情報の一覧を取得します。デフォルトでは脆弱性情報の発見日、更新日、発行日が過去1週間以内の情報のみを表示します。ベンダID、製品ID、CVSS深刻度、発見日/更新日/発行日からフィルタリングが可能です。
例
# curl -s http://jvndb.jvn.jp/myjvn\?method\=getVulnOverviewList <!--?xml version="1.0" encoding="UTF-8" ?--> http://jvndb.jvn.jp/apis/myjvn JVNDB 脆弱性対策情報 2015-10-03T20:12:29+09:00 2015-10-03T20:12:29+09:00 http://jvndb.jvn.jp/ja/contents/2015/JVNDB-2015-000149.html gollum は、git リポジトリを使った wiki システムです。gollum には、サーバ上の任意のファイルを閲覧される脆弱性が存在します。 この脆弱性情報は、情報セキュリティ早期警戒パートナーシップに基づき下記の方が IPA に報告し、JPCERT/CC が開発者との調整を行いました。 報告者: 馬場 将次 氏 Information-technology Promotion Agency, Japan Information-technology Promotion Agency, Japan JVNDB-2015-000149 https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-7314 http://jvndb.jvn.jp/ja/cwe/CWE-20.html https://jvn.jp/jp/JVN27548431/index.html gollum gollum 2015-10-02T12:02:52+09:00 2015-10-02T12:02:52+09:00 2015-10-02T12:02:52+09:00 〜〜〜 省略 〜〜〜 http://jvndb.jvn.jp/ja/contents/2015/JVNDB-2015-000142.html Apache Software Foundation が提供する cordova-plugin-file-transfer は、Apache Cordova を使って開発されるアプリケーションにファイルのアップロードおよびダウンロードを可能にする機能を提供するプラグインです。また、HTTP ヘッダを追加する機能も提供しています。 cordova-plugin-file-transfer を使用した Android アプリケーションには、ファイル名の処理に起因する HTTP ヘッダインジェクションの脆弱性が存在します。 この脆弱性情報は、情報セキュリティ早期警戒パートナーシップに基づき下記の方が IPA に報告し、JPCERT/CC が開発者との調整を行いました。 報告者: ソニーデジタルネットワークアプリケーションズ株式会社 西村 宗晃 氏 Information-technology Promotion Agency, Japan Information-technology Promotion Agency, Japan JVNDB-2015-000142 https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-5204 http://jvndb.jvn.jp/ja/cwe/CWE-20.html https://jvn.jp/jp/JVN21612597/index.html Apache Software Foundation cordova-plugin-file-transfer 2015-09-29T12:03:12+09:00 2015-09-29T12:03:12+09:00 2015-09-29T12:03:12+09:00
今週一週間で新たに発見された脆弱性情報は17件となります。
getVulnDetailInfo
脆弱性情報の詳細を取得します。getVulnOverviewListで取得した脆弱性対策情報ID(JVNDB-西暦年-XXXXXX)を指定する必要があります。
例
# curl http://jvndb.jvn.jp/myjvn\?method\=getVulnDetailInfo\&vulnId\=JVNDB-2015-000149 <!--?xml version="1.0" encoding="UTF-8" ?--> JVNDB-2015-000149 gollum は、git リポジトリを使った wiki システムです。gollum には、サーバ上の任意のファイルを閲覧される脆弱性が存在します。 この脆弱性情報は、情報セキュリティ早期警戒パートナーシップに基づき下記の方が IPA に報告し、JPCERT/CC が開発者との調整を行いました。 報告者: 馬場 将次 氏 gollum gollum v4.0.0 およびそれ以前 Medium 4.3 (AV:N/AC:M/Au:N/C:P/I:N/A:N) 遠隔の第三者によって、サーバ上の任意のファイルを閲覧される可能性があります。 [アップデートする] 開発者が提供する情報をもとに、最新版へアップデートしてください。 GitHub Issue#1070: [SECURITY] [FIXED] Information disclosure vulnerability, please update! https://github.com/gollum/gollum/issues/1070 Common Vulnerabilities and Exposures (CVE) CVE-2015-7314 https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2015-7314 JVN JVN#27548431 https://jvn.jp/jp/JVN27548431/index.html 共通脆弱性タイプ一覧 (CWE) 不適切な入力確認(CWE-20) http://jvndb.jvn.jp/ja/cwe/CWE-20.html 不適切な入力確認 CWE-20 http://jvndb.jvn.jp/ja/cwe/CWE-20.html 2015-10-02T12:02:52+09:00 2015-10-02T12:02:52+09:00 2015-10-02T00:00:00+09:00
AWS Lambdaで実装するMyJVN APIを利用した脆弱性情報収集
これら紹介したAPIを利用して、定期的にシステムに導入したアプリケーションの脆弱性情報を収集して、メール通知するシステムを作ります。仕組みは簡単です。Lambda Function(Python)を作成します。Eventはスケジュール実行です。MyJVN APIの仕様上、最小検索の単位が日となりますので、日次や週次、月次のいずれかで指定します。通知はSNSでメール通知します。
スクリプトはGistに公開しました(やっつけ)
現在は5つのパラメータを設定可能です。コメントアウトで括られた変数に設定してください。
- SEVERITY
- 収集する脆弱性の危険度
- low / middle / high から選択可能
- lowを選択した場合は全て、middleを選択した場合はmiddle、high、highを選択した場合はhighのみ収集
- INTERVAL
- 収集する期間(Lambda Functionのスケジュールに合わせないと重複したり、漏れたり)
- 現状は発行日でフィルタ
- daily / weekly / monthly から選択可能
- dailyの場合は前日の脆弱性、weeklyの場合は7日前〜前日、monthlyの場合は1ヶ月前同日〜前日の脆弱性情報を収集
- TOPIC_ARN
- 通知するSNS TopicのARN
- 試験はしていないが、メール通知以外にも使えます
- NOTIFY_SUBJECT
- 通知のSubject
- SNS TopicのPublishの引数で渡すSubject
- Pythonのスクリプトなので日付入れたりもできます
- PRODUCTS
- 脆弱性を収集する製品(リスト定義)
- JVN iPediaで管理されている製品名で入力する必要あり
- 製品名はgetProductList APIで要確認
##################################################################################### | |
### Select from low, middle, high. If you choice middle, script collect middle and high. | |
SEVERITY = "middle" | |
### Select from daily, weekly, monthly | |
INTERVAL = "daily" | |
### Specify Publish Topic Arn for SNS | |
TOPIC_ARN = "arn:aws:sns:*******:************:**********" | |
### Specify notify email subject | |
NOTIFY_SUBJECT = "[ALERT] Found Vulnerability Information" | |
### Select from product name from result to call getProductList API (http://jvndb.jvn.jp/myjvn?method=getProductList) | |
PRODUCTS = [ | |
"Linux Kernel", | |
"OpenSSL", | |
"Apache HTTP Server", | |
"Apache Tomcat" | |
] | |
##################################################################################### | |
import datetime | |
from dateutil import relativedelta | |
import boto3 | |
import urllib2 | |
from xml.dom import minidom | |
URL_PATH = "http://jvndb.jvn.jp/myjvn" | |
PRODUCT_LIST_METHOD = "getProductList" | |
VULN_OVERVIEW_LIST_METHOD = "getVulnOverviewList" | |
NS_STATUS = "http://jvndb.jvn.jp/myjvn/Status" | |
NS_SEC = "http://jvn.jp/rss/mod_sec/" | |
NS = "http://purl.org/rss/1.0/" | |
NS_PRODUCT = "http://jvndb.jvn.jp/myjvn/Results" | |
def lambda_handler(event, context): | |
product_ids = get_product_ids(PRODUCTS) | |
print product_ids | |
vulnerabilities = [] | |
for product in product_ids: | |
vuls = get_vulnerability_list(product, SEVERITY, INTERVAL) | |
if vuls: | |
vulnerabilities.extend(vuls) | |
if not vulnerabilities: | |
print "don't exist vulnerability." | |
return | |
notify(vulnerabilities, TOPIC_ARN) | |
def get_product_ids(product_names): | |
product_ids = set() | |
for name in product_names: | |
url = URL_PATH + "?method=" + PRODUCT_LIST_METHOD + "&keyword=" + name | |
res = urllib2.urlopen(url).read().encode("utf-8") | |
dom = minidom.parseString(res) | |
for product in dom.getElementsByTagNameNS(NS_PRODUCT, "Product"): | |
if product.getAttribute("pname") == name: | |
product_ids.add(product.getAttribute("pid")) | |
return product_ids | |
def get_vulnerability_list(product, severity, interval): | |
vulnerabilities = [] | |
if severity == "low": | |
severity = "l" | |
elif severity == "middle": | |
severity = "m" | |
elif severity == "high": | |
severity = "h" | |
start = None | |
now = datetime.datetime.now() | |
end = now - datetime.timedelta(days=1) | |
if interval == "daily": | |
start = end | |
elif interval == "weekly": | |
start = now - datetime.timedelta(days=7) | |
elif interval == "monthly": | |
start = now - relativedelta.relativedelta(months=1) | |
url = URL_PATH + "?method=" + VULN_OVERVIEW_LIST_METHOD \ | |
+ "&productId=" + product \ | |
+ "&severity=" + severity \ | |
+ "&dateFirstPublishedStartY=" + str(start.year) \ | |
+ "&dateFirstPublishedStartM=" + str(start.month) \ | |
+ "&dateFirstPublishedStartD=" + str(start.day) \ | |
+ "&dateFirstPublishedEndY=" + str(end.year) \ | |
+ "&dateFirstPublishedEndM=" + str(end.month) \ | |
+ "&dateFirstPublishedEndD=" + str(end.day) \ | |
+ "&rangeDatePublished=n&rangeDatePublic=n" | |
res = urllib2.urlopen(url).read().encode("utf-8") | |
dom = minidom.parseString(res) | |
total = dom.getElementsByTagNameNS(NS_STATUS, "Status")[0].getAttribute("totalRes") | |
for num in range(1, int(total), 50): | |
url = url + "&startItem=" + str(num) | |
res = urllib2.urlopen(url) | |
dom = minidom.parseString(res.read().encode("utf-8")) | |
for item in dom.getElementsByTagNameNS(NS, "item"): | |
vul = {} | |
if len(item.getElementsByTagNameNS(NS_SEC, "identifier")) == 1: | |
vul["id"] = item.getElementsByTagNameNS(NS_SEC, "identifier")[0].childNodes[0].nodeValue | |
if len(item.getElementsByTagNameNS(NS, "link")) == 1: | |
vul["link"] = item.getElementsByTagNameNS(NS, "link")[0].childNodes[0].nodeValue | |
if len(item.getElementsByTagNameNS(NS, "title")) == 1: | |
vul["title"] = item.getElementsByTagNameNS(NS, "title")[0].childNodes[0].nodeValue | |
if len(item.getElementsByTagNameNS(NS, "description")) == 1: | |
vul["description"] = item.getElementsByTagNameNS(NS, "description")[0].childNodes[0].nodeValue | |
if len(item.getElementsByTagNameNS(NS_SEC, "cvss")) == 1: | |
cvss = item.getElementsByTagNameNS(NS_SEC, "cvss")[0] | |
vul["cvss_score"] = cvss.getAttribute("score") | |
vul["cvss_severity"] = cvss.getAttribute("severity") | |
vulnerabilities.append(vul) | |
return vulnerabilities | |
def notify(vulnerabilities, topic_arn): | |
message = "" | |
for vul in vulnerabilities: | |
if vul.get("title"): | |
message += "Title: " + vul["title"] + "\n" | |
if vul.get("id"): | |
message += "ID: " + vul["id"] + "\n" | |
if vul.get("cvss_score"): | |
message += "CVSS Score: " + vul["cvss_score"] + ", Severity: " + vul["cvss_severity"] + "\n" | |
if vul.get("description"): | |
message += "Detail: " + vul["description"] + "\n" | |
if vul.get("link"): | |
message += "URL: " + vul["link"] + "\n" | |
message += "\n" | |
client = boto3.client("sns") | |
print( | |
client.publish(TopicArn=topic_arn, | |
Subject=NOTIFY_SUBJECT, | |
Message=message | |
)) |
AWS Lambdaを実行すると、以下のようなメールが飛んできました。
うん、見栄えいくない。
【追加実装したい機能】
- 表示の工夫
- エラー処理
- 通知に含むパラメータ選択可
- 通知方法をSNSだけでなく、SESによる通知も選択可(文字数が多くなりそうな時はSES)
- 収集期間を発行日だけでなく、更新日でも通知可(選択式)
- 収集期間を日数などもう少し細かな指定可
まとめ
1システムに1Lambda Functionいかがでしょうか。 今までマンパワーで定期的に脆弱性情報を収集していた方は取り込みたくなったのではないでしょうか。MyJVN APIはシンプルな分、一つ一つのAPIの使い方も分かりやすくラーニングコストはかなり低いです。その逆で細かいクエリを発行できないため、複雑な作りをしたい場合、プログラムで工夫する必要があります。レスポンスもスキーマ定義されたXMLでプログラムに取り込みやすいです。 今回のサンプルではAWS Lambdaのスケジュール実行がリリースされたことで仕組みに取り込みましたが、lambda_handlerメソッドをmainメソッドに置き換え、notifyメソッドをsnsの利用からSMTPを実行するようにすれば、AWSに限らずどこでもcronなどで実行が可能です。
楽できるところは楽して、やるべきことに時間を使っていきましょう。