Virus Total から取得したレポート情報からマルウェア名を一意に判定してみた

# はじめに
以前に私が投稿した記事[Virus TotalのAPIを利用してハッシュ値から検体のレポートを取得する方法](https://t.co/p0g2c75MOR?amp=1 "Virus TotalのAPIを利用してハッシュ値から検体のレポートを取得する方法")で，Virus Total からマルウェア名を取得していましたが，そこからマルウェア名を一意に判定する一つの方法を本記事で紹介します．

完全なマルウェア名を判定したい場合は，前回のsort してuniq する方法でもよいのですが，この方法でさらにWeb などから情報を取得しようとすると，取得結果の文字列を分解してより大まかにマルウェア名が判定できるとよいと考えています．

今回の記事では，そんなときに役に立つかもしれない情報を記載しています．

# 対象となるデータ

今回の対象となるデータは，上記記事で取得した下記のマルウェア名たちです．

```text:VirusTotalから取得したマルウェア名
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
```

この情報から，マルウェア名を大まかに判定します．

# 期待される出力
今回のケースの場合，期待される出力は"Mirai"です．
本記事で得られた結果をもとにさらにWeb 検索を実施するなどして，マルウェアのハッシュ値からより詳細な情報を収集することができるようになります．


区切り文字で単語に分解して，単語の出現頻度を計算し，一番出現頻度の大きいものをマルウェア名として判定します．"Trojan.Linux.Mirai.1"などの文字列だと検索してもヒットする情報が少ない/ない 可能性があるため，ここでは大まかな判定結果が欲しいという所存です．

# 大まかに判定するために

結果を大まかな判定とするために，上記の取得結果を区切り文字で区切って，単語に分解します．

区切り文字は，以下の5文字を指定です．
- \s (スペース)
-  . (ピリオド)
-  / (スラッシュ)
-  : (コロン)
-  - (ハイフン)
- [, ] (カギカッコ)
# 判定ロジック
単語に分解したのち，単語の出現頻度を計算します．
今回の場合，出現頻度は下記のようになります．

```text:出現頻度の結果(Top10に限定)
[('Mirai', 17),
 ('Trojan', 9),
 ('Backdoor', 7),
 ('', 6),
 ('1', 4),
 ('Malware', 3),
 ('Other', 2),
 ('gen', 2),
 ('Trj', 2),
 ('HEUR', 2)]
```

"Mirai"が一番多く出現していることが分かりました．
このロジックを使用した場合，マルウェア名はMiraiであると判定されます．

# プログラム

本ロジックを記述したプログラムです．
今回の取得結果を第一引数に取り，一行ずつ取り込みます．
その後，上記の区切り文字で単語に分解したのち，単語をword_list に格納，word_list から collections.Counter() を使用することで，word_list 内の要素の数をカウントしています．

pprint.pprint(list(count.most_common(10))) で，先ほど紹介した出現頻度Top10の単語を出力しています．

最後の行のprint(list(count.most_common(1))[0][0]) では，最も出現頻度の大きかった単語を出力するようにしています．今回の場合は，"Mirai" です．

また，明らかにマルウェア名ではないが，頻出する単語はあらかじめリストから削除しておきます．

```py:マルウェア名判定プログラム
import sys
import json
import time
import requests
import numpy as np
import pandas as pd
import re
import collections
import pprint

file = sys.argv[1]
word_list = []
malware = []

with open(file) as f:
    malware = f.read().splitlines()

for i in range(len(malware)):
    word = re.split('[ \[\]./:-]', malware[i])
    #print(word)
    word_list += word
    #print(word_list)

for i in range(len(word_list)):
    try:
        word_list.remove('Linux') #マルウェア名でない頻出単語を削除
    except ValueError:
        pass

candidate = collections.Counter(word_list)
#pprint.pprint(candidate)
#pprint.pprint(list(candidate.most_common(10)))
print(candidate.most_common(1)[0][0]) # マルウェア名のみ出力
```

# おわりに

結果から逆順して判定ロジックを考えてみて，実装してみた．
実際のセキュリティアナリストなどは動的解析や静的解析ができればよいのだろうが，そんなことやっている余裕などないとき，またスキルがないとき，どのようにしてマルウェア名を判定しているのかが気になる．

もし，本記事で紹介するような方法で判定しているのであれば，マルウェアのハッシュ値さえ取得できれば，マルウェア名が判明し，その結果をもとにWeb 検索すればより詳細な情報が自動的に得られるのではないだろうか．と感じた．(実際，手で検索して目で情報を得て頭を使って判断は時間も体力も使うことが今回でよくわかった．やっていることは簡単だが)

今後は，本プログラムを利用してハッシュ値が取得できた場合自動的に様々な情報を検索して取得し、結合するシステムを作成していく．

# 参考文献
- https://docs.python.org/ja/3/library/collections.html?highlight=collections%20counter
- https://t.co/p0g2c75MOR?amp=1