やりたいこと
Google が運営する「VirusTotal」の無償公開 API を使って、セキュリティインシデント発生時の調査として Proxy や FW/UTM のログから URL や IP 一式を抽出し、その URL/IP の中から問題ないものと危険なものに振り分けたい。
制約
VirusTotal は無償版の API を使う関係で、調べられるURL/IP は 1 分に 4 回までとなる。
準備
API キー
VirusTotal にアカウント登録 (Sign Up) して API キーを取得する。
Linux サーバ + Python 仮想環境の準備
Rocky Linux 8.6 (minimal install) にて以下を実施
[test@localhost ~]$ sudo dnf install -y python39 [test@localhost ~]$ python3.9 -m venv .venv39 [test@localhost ~]$ source .venv39/bin/activate (.venv39) [test@localhost ~]$ pip install --upgrade pip (.venv39) [test@localhost ~]$ pip install vt-py (.venv39) [test@localhost ~]$ export apikey=<your api key>
URL/IP のリストファイル domainlist.txt
形式は比較的自由だった。http(s):// が有っても無くてもよくて、IP アドレスも検査してくれた。
milestone-of-se.nesuke.com http://027.ru 1.1.1.1 103.9.36.172
実行ファイル vt-request.py
import sys import os import vt import time from datetime import datetime, timezone def main(argv): apikey = os.environ['apikey'] listfile = sys.argv[1] with open(listfile, "r", encoding="utf-8") as f: domains = list(filter(None, f.read().split("\n"))) client = vt.Client(apikey) for domain in domains: domain = domain.replace('\r', '') analysis = client.scan_url(domain) time.sleep(15) while True: analysis = client.get_object("/analyses/{}", analysis.id) if analysis.status == "completed": print("Completed!") break else: print(".", end="", flush=True) time.sleep(1) # print(analysis.to_dict()) vtdate = analysis.get("date") vtstats = analysis.get("stats") dt = datetime.fromtimestamp(vtdate).isoformat() if vtstats.get("malicious") + vtstats.get("suspicious") > 0: with open('bad-domain.txt', 'a') as f: print(dt, domain, vtstats, file=f) else: with open('good-domain.txt', 'a') as f: print(dt, domain, vtstats, file=f) client.close() if __name__ == "__main__": sys.exit(main(sys.argv))
実行してみる
(.venv39) [test@localhost ~]$ python vt-request.py domainlist.txt
悪意ある IP/URL は bad-domain.txt に保存され、問題ない IP/URL は good-domain.txt に保存される。
動作概要
VirusTotal の公式ライブラリ vt-py を pip でインストールし、import vt でインポートしている。
"client = vt.Client(apikey)" でオブジェクトを生成し、"analysis = client.scan_url(domain)" でリクエストを投げる。
リクエスト後は VirusTotal 側で処理を行うため、取得は別のリクエストで取りに行く。どうせ 15 秒に1回しかリクエスト投げられないのでまずは 15 秒待って、"analysis = client.get_object("/analyses/{}", analysis.id)" で状態確認を行う。
completed になるまで while ループで 1 秒待ち、completed になったら " analysis.get("stats")" で結果をもらう。以下のような形式。
{'harmless': 67, 'malicious': 11, 'suspicious': 0, 'undetected': 10, 'timeout': 0}
malicious (悪意ある)、suspicious (疑わしい) が 1 以上であった場合は bad-domain.txt に出力し、これらが 0 なら good-domain.txt に出力する。検査時刻とドメインも一緒に出力する。bad-domain.txt は以下のようになる。
(.venv39) [test@localhost ~]$ cat bad-domain.txt 2022-09-29T23:40:55 http://027.ru {'harmless': 77, 'malicious': 0, 'suspicious': 1, 'undetected': 10, 'timeout': 0} 2022-09-29T23:41:12 1.1.1.1 {'harmless': 76, 'malicious': 4, 'suspicious': 0, 'undetected': 8, 'timeout': 0} 2022-09-29T23:41:28 103.9.36.172 {'harmless': 67, 'malicious': 11, 'suspicious': 0, 'undetected': 10, 'timeout': 0}
1.1.1.1 が malicious になっているが、、、個人的に推測したのは「公開されたキャッシュ DNS は危険、という思想の元?もしくは実際にポイズニングされたことがある?」「社内で使っている 1.1.1.1 が変な挙動をしてそれがレピュテーションとして報告された?」くらいだがどちらもうーん、、という感じ。
コメント