Virus TotalのAPIを利用してハッシュ値から検体のレポートを取得する方法

#はじめに
手元に検体のハッシュ値情報はあるが，多すぎて手作業で調査するのは大変なので，自動的にVirus Total からレポート情報を取得するプログラムを作成しました．プログラムはPython3 で作成および動作確認しています．
同じような記事が多数ありますが，Python2系で記述されていたり，公式ドキュメントのサンプルと微妙に異なる書き方してあったりするので，記事を作成することにしました．
<b>【追記1】取得結果がjson ではなかったので，jsonで保存する実装に変更しました</b>
<b>【追記2】取得結果からマルウェア名を抽出する方法を追記しました</b>
<b>【追記3】APIの注意書きについて引用を追記しました</b>

Virus Total は参考文献にある通り，API を提供しているので，情報の取得にはAPIを使用するのが効率的です．
Virus Total のAPI key は，アカウントを作成することで無料で入手できます．
第三者に漏洩すると悪用される恐れがあるので，プログラムにハードコーディングする際は公開や共有時に注意ください．
(自分しか使わないならハードコーディングしても良いかと思いますが，外部公開を考えると外部入力したほうが無難かもしれません)

#プログラム

公式ドキュメントに沿って，以下の内容で作成しました．
apikey の部分はあなた自身のapi key の値を記述してください．(前述した通り外部入力の形でも結構です．)

また，本プログラムは検体のハッシュ値情報をほかのテキストファイルから受け取る形で実装しています．こちらもハードコーディングするか，実行時の引数で取得するかはやりやすい方法で実装してください．

```python:get_vt.py
import sys
import json
import time
import requests

url = 'https://www.virustotal.com/vtapi/v2/file/report'

count = 0
file = open("hash.txt", 'r')
for hash in file:
    params = {'apikey': 'your api key value', 'resource': hash}
    response = requests.get(url, params=params)
    json_result = json.dumps(response.json())
    with open(sys.argv[1], 'a') as outfile:
        print(json_result, file=outfile)
    count = count + 1
    if count % 4 == 0:
        time.sleep(65)
file.close()
```

Virus Total のPublic APIは，1日当たり500 アクセス，1分当たり4 アクセスの制限があります．
したがって，4回アクセスを実施したのち，念を含めて65秒動作をストップするようにしてアクセス制限に引っかからないような処理を実装しています．(以下，注意書きの引用)

> Notice

><b>The Public API is limited to 500 requests per day and a rate of 4 requests per minute.</b>
The Public API must not be used in commercial products, services or business workflows.
The Private API returns more threat data and exposes more endpoints.
The Private API is governed by an SLA that guarantees readiness of data.


#実行方法

ただのPythonスクリプトなので，以下の方法で実行します．結果はJSON フォーマットで出力されます。結果の出力先のファイルパスを指定してください。

```sh:get_vt.pyの実行方法
$ python3 get_vt.py [file path]
```

以下は，あるハッシュ値で検索した場合の取得結果です(一部省略)．アンチウイルス製品名に続いて，マルウェア判定の結果がdetected にTrue or False で記述されるみたいです．マルウェア名も取得できるようで，見る限りはMirai っぽいです．

```json:取得結果
{"scans": {"Bkav": {"detected": false, "version": "1.3.0.9899", "result": null, "update": "20200819"}, "MicroWorld-eScan": {"detected": false, "version": "14.0.409.0", "result": null, "update": "20200820"}, "FireEye": {"detected": true, "version": "32.36.1.0", "result": "Trojan.Linux.Mirai.1", "update": "20200820"}, "CAT-QuickHeal": {"detected": false, "version": "14.00", "result": null, "update": "20200820"}, "McAfee": {"detected": true, "version": "6.0.6.653", "result": "RDN/Generic BackDoor", "update": "20200820"}, "Malwarebytes": {"detected": false, "version": "3.6.4.335", "result": null, "update": "20200820"}, "Zillya": {"detected": true, "version": "2.0.0.4158", "result": "Backdoor.Mirai.Linux.91998", "update": "20200820"}, "SUPERAntiSpyware": {"detected": false, "version": "5.6.0.1032", "result": null, "update": "20200814"}, "Sangfor": {"detected": false, "version": "1.0", "result": null, "update": "20200814"}, "K7AntiVirus": {"detected": false, "version": "11.131.35049", "result": null, "update": "20200820"}, "K7GW": {"detected": false, "version": "11.131.35050", "result": null, "update": "20200820"}, "Baidu": {"detected": false, "version": "1.0.0.2", "result": null, "update": "20190318"}, "F-Prot": {"detected": false, "version": "4.7.1.166", "result": null, "update": "20200820"}, "Symantec": {"detected": true, "version": "1.11.0.0", "result": "Trojan.Gen.NPE", "update": "20200820"}, "ESET-NOD32": {"detected": true, "version": "21852", "result": "a variant of Linux/Mirai.OX", "update": "20200820"}, "TrendMicro-HouseCall": {"detected": false, "version": "10.0.0.1040", "result": null, "update": "20200820"}, "Avast": {"detected": true, "version": "18.4.3895.0", "result": "Other:Malware-gen [Trj]", "update": "20200820"}, "ClamAV": {"detected": true, "version": "0.102.4.0", "result": "Unix.Dropper.Mirai-7135870-0", "update": "20200817"}, "Kaspersky": {"detected": true, "version": "15.0.1.13", "result": "HEUR:Backdoor.Linux.Mirai.b", "update": "20200820"}, "BitDefender": {"detected": true, "version": "7.2", "result": "Trojan.Linux.Mirai.1", "update": "20200820"}, "NANO-Antivirus": {"detected": true, "version": "1.0.134.25140", "result": "Trojan.Mirai.hrbzkk", "update": "20200820"}, "ViRobot": {"detected": false, "version": "2014.3.20.0", "result": null, "update": "20200820"}, "Tencent": {"detected": true, "version": "1.0.0.1", "result": "Backdoor.Linux.Mirai.wao", "update": "20200820"}, "Ad-Aware": {"detected": false, "version": "3.0.16.117", "result": null, "update": "20200820"}, "TACHYON": {"detected": false, "version": "2020-08-20.02", "result": null, "update": "20200820"}, "Comodo": {"detected": true, "version": "32668", "result": ".UnclassifiedMalware@0", "update": "20200728"}, "F-Secure": {"detected": true, "version": "12.0.86.52", "result": "Malware.LINUX/Mirai.lpnjw", "update": "20200820"}, "DrWeb": {"detected": true, "version": "7.0.46.3050", "result": "Linux.Mirai.671", "update": "20200820"}, "VIPRE": {"detected": false, "version": "86068", "result": null, "update": "20200820"}, "TrendMicro": {"detected": true, "version": "11.0.0.1006", "result": "Backdoor.Linux.MIRAI.USELVH120", "update": "20200820"}, "CMC": {"detected": false, "version": "2.7.2019.1", "result": null, "update": "20200820"}, "Sophos": {"detected": true, "version": "4.98.0", "result": "Linux/DDoS-CIA", "update": "20200819"}, "Cyren": {"detected": false, "version": "6.3.0.2", "result": null, "update": "20200820"}, "Jiangmin": {"detected": false, "version": "16.0.100", "result": null, "update": "20200820"}, "Avira": {"detected": true, "version": "8.3.3.8", "result": "LINUX/Mirai.lpnjw", "update": "20200820"}, "Fortinet": {"detected": true, "version": "6.2.142.0", "result": "ELF/DDoS.CIA!tr", "update": "20200820"}, "Antiy-AVL": {"detected": false, "version": "3.0.0.1", "result": null, "update": "20200820"}, "Kingsoft": {"detected": false, "version": "2013.8.14.323", "result": null, "update": "20200820"}, "Arcabit": {"detected": true, "version": "1.0.0.877", "result": "Trojan.Linux.Mirai.1", "update": "20200820"}, "AegisLab": {"detected": true, "version": "4.2", "result": "Trojan.Linux.Mirai.K!c", "update": "20200820"}, "AhnLab-V3": {"detected": false, "version": "3.18.1.10026", "result": null, "update": "20200820"}, "ZoneAlarm": {"detected": true, "version": "1.0", "result": "HEUR:Backdoor.Linux.Mirai.b", "update": "20200820"}, "Avast-Mobile": {"detected": false, "version": "200820-00", "result": null, "update": "20200820"}, "Microsoft": {"detected": true, "version": "1.1.17300.4", "result": "Trojan:Win32/Skeeyah.A!rfn", "update": "20200820"}, "Cynet": {"detected": true, "version": "4.0.0.24", "result": "Malicious (score: 85)", "update": "20200815"}, "TotalDefense": {"detected": false, "version": "37.1.62.1", "result": null, "update": "20200820"}, "BitDefenderTheta": {"detected": false, "version": "7.2.37796.0", "result": null, "update": "20200819"}, "ALYac": {"detected": false, "version": "1.1.1.5", "result": null, "update": "20200820"}, "MAX": {"detected": true, "version": "2019.9.16.1", "result": "malware (ai score=89)", "update": "20200820"}, "VBA32": {"detected": false, "version": "4.4.1", "result": null, "update": "20200819"}, "Zoner": {"detected": false, "version": "0.0.0.0", "result": null, "update": "20200819"}, "Rising": {"detected": true, "version": "25.0.0.26", "result": "Backdoor.Mirai/Linux!1.BAF6 (CLASSIC)", "update": "20200820"}, "Yandex": {"detected": false, "version": "5.5.2.24", "result": null, "update": "20200707"}, "Ikarus": {"detected": true, "version": "0.1.5.2", "result": "Trojan.Linux.Mirai", "update": "20200820"}, "MaxSecure": {"detected": false, "version": "1.0.0.1", "result": null, "update": "20200819"}, "GData": {"detected": true, "version": "A:25.26670B:27.19869", "result": "Trojan.Linux.Mirai.1", "update": "20200820"}, "AVG": {"detected": true, "version": "18.4.3895.0", "result": "Other:Malware-gen [Trj]", "update": "20200820"}, "Panda": {"detected": false, "version": "4.6.4.2", "result": null, "update": "20200819"}, "Qihoo-360": {"detected": true, "version": "1.0.0.1120", "result": "Linux/Backdoor.6f4", "update": "20200820"}}, ...
```

また，取得結果から判定したマルウェア名 (resultフィールドの値)のみを取得する場合は，取得結果のjsonファイルに対して以下のコマンドを実行します．

```bash:マルウェア名を抽出
$ cat [取得結果ファイル].json | jq -r '.scans[].result' | grep -v "null"
```

未検出の場合，result にはnullが記入されるので，nullを省いています．

上記の結果をコマンドでマルウェア名のみ抽出すると，下記のようになります．

```text:マルウェア名のみ抽出した結果
Trojan.Linux.Mirai.1
RDN/Generic BackDoor
Backdoor.Mirai.Linux.91998
Trojan.Gen.NPE
a variant of Linux/Mirai.OX
Other:Malware-gen [Trj]
Unix.Dropper.Mirai-7135870-0
HEUR:Backdoor.Linux.Mirai.b
Trojan.Linux.Mirai.1
Trojan.Mirai.hrbzkk
Backdoor.Linux.Mirai.wao
.UnclassifiedMalware@0
Malware.LINUX/Mirai.lpnjw
Linux.Mirai.671
Backdoor.Linux.MIRAI.USELVH120
Linux/DDoS-CIA
LINUX/Mirai.lpnjw
ELF/DDoS.CIA!tr
Trojan.Linux.Mirai.1
Trojan.Linux.Mirai.K!c
HEUR:Backdoor.Linux.Mirai.b
Trojan:Win32/Skeeyah.A!rfn
Malicious (score: 85)
malware (ai score=89)
Backdoor.Mirai/Linux!1.BAF6 (CLASSIC)
Trojan.Linux.Mirai
Trojan.Linux.Mirai.1
Other:Malware-gen [Trj]
Linux/Backdoor.6f4
```

この結果から、検出結果をカウントすると，以下のようになる．

```bash:検出結果カウント
$ cat [取得結果ファイル].json | jq -r '.scans[].result' | grep -v "null" | sort | uniq -c | sort -r
4 Trojan.Linux.Mirai.1
2 Other:Malware-gen [Trj]
2 HEUR:Backdoor.Linux.Mirai.b
1 malware (ai score=89)
1 a variant of Linux/Mirai.OX
1 Unix.Dropper.Mirai-7135870-0
1 Trojan:Win32/Skeeyah.A!rfn
1 Trojan.Mirai.hrbzkk
1 Trojan.Linux.Mirai.K!c
1 Trojan.Linux.Mirai
1 Trojan.Gen.NPE
1 RDN/Generic BackDoor
1 Malware.LINUX/Mirai.lpnjw
1 Malicious (score: 85)
1 Linux/DDoS-CIA
1 Linux/Backdoor.6f4
1 Linux.Mirai.671
1 LINUX/Mirai.lpnjw
1 ELF/DDoS.CIA!tr
1 Backdoor.Mirai/Linux!1.BAF6 (CLASSIC)
1 Backdoor.Mirai.Linux.91998
1 Backdoor.Linux.Mirai.wao
1 Backdoor.Linux.MIRAI.USELVH120
1 .UnclassifiedMalware@0
```

上記より，"Trojan.Linux.Mirai.1"が，4つ出力され最多なので、今回の対象としたハッシュ値を持つ検体がMiraiの一種であることが判明した。
※本当はマルウェア名を返すベンダーのみに選定したうえで、共通部分文字列の頻度を計算したうえで判定できたりすると良いのかもしれない．こちらは別途チャレンジ予定

#まとめ

Virus Total のAPI を利用してハッシュ値からレポート情報を取得するプログラムを作成した．今後は，得られた出力情報の利用方法やほかのAPI の利用方法などについて検討していく．

#参考文献
- https://developers.virustotal.com/reference#file-report
- https://developers.virustotal.com/reference#public-vs-private-api