python

VirusTotal+PythonでURL/IPリストから危険なものを検出する

やりたいこと

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 が変な挙動をしてそれがレピュテーションとして報告された?」くらいだがどちらもうーん、、という感じ。

コメント

タイトルとURLをコピーしました