6月中旬、カザフスタンでのコロナウイルスとの戦いが本格化しました。事件の増加に警鐘を鳴らし(その後、Nursultan Nazarbayev前大統領でさえ感染した)、地方自治体はすべてのショッピングおよびエンターテイメントセンター、チェーンストア、市場、バザールを再び閉鎖することを決定しました。当時、サイバー犯罪者はこの状況を利用して、ロシアや国際的な企業に悪意のあるメールを送信していました。
カザフスタン共和国の保健大臣の訴えを装った危険な手紙は、脅威検出システム(TDS)グループIBによって傍受されました。添付ファイルには、起動時に、感染したコンピューターからログインとパスワードを盗むように設計されたLoki PWS(Password Stealer)ファミリーの悪意のあるプログラムをインストールしたドキュメントが含まれていました。将来的には、攻撃者はそれらを使用して、金融詐欺やスパイ行為のために電子メールアカウントにアクセスしたり、ハッカーフォーラムで販売したりする可能性があります。
この記事では、CERT-GIBのアナリストであるNikita Karpovが、現在最も人気のあるデータスティーラーの1つであるLokiのインスタンスを調べます。
今日は、ボットの人気のあるバージョンの1つである1.8について検討します。それは活発に販売されており、管理パネルはパブリックドメインにもあります:ここ。
管理パネルの例:
LokiはC ++で記述されており、感染したコンピューターからユーザー情報を盗むために使用される最も人気のあるマルウェアの1つです。私たちの時代の惨劇-身代金ウイルス-DataStealerは、被害者のコンピューターに攻撃された後、非常に高速でタスクを実行します-足場を築いてシステムでの特権を増やす必要はなく、攻撃から身を守る時間はほとんどありません。したがって、ユーザーデータを盗むマルウェアのイベントでは、インシデントの調査が主な役割を果たします。
実行可能なマルウェアダンプを解凍して取得する
ほとんどの場合、配布はメーリングリストの添付ファイルを介して行われます。正当なファイルを装ったユーザーが添付ファイルをダウンロードして開き、マルウェアを起動します。
注入マーカーは、ローダーの存在を示唆しています。
DIEの助けを借りて、ソースファイルがVB6で書き込まれているという情報を取得します。
エントロピーグラフは、大量の暗号化されたデータを示しています。
起動されると、最初のプロセスは子プロセスを作成し、それを注入して終了します。2番目のプロセスは、マルウェアの動作を担当します。しばらくすると、プロセスを停止してメモリダンプを保存します。Lokiがダンプ内にあることを確認するには、コマンドセンターのURL内を調べます。これは、ほとんどの場合、fre.phpで終わります。
Lokiを含むメモリフラグメントをダンプし、PEヘッダーを修正します。
TDSハントボックスシステムを使用して、ダンプのパフォーマンスを確認します。
ボット機能
逆コンパイルされたマルウェアコードを調べる過程で、操作に必要なライブラリの初期化の直後に実行される4つの関数を含む部分が見つかりました。それらのそれぞれを内部で分解した後、マルウェアの目的と機能を決定します。
関数名は、わかりやすくするために名前が変更されました。
ボットの機能は、次の2つの主要な機能によって決定されます。
- Data Stealerは、101個のアプリケーションからデータを盗んでサーバーに送信する最初の機能です。
- ダウンローダー-実行のためのCnC(コマンド&コントロール)コマンドからの要求。
便宜上、次の表に、調査対象のLokiインスタンスがデータを盗もうとするすべてのアプリケーションを示します。
機能ID | 応用 | 機能ID | 応用 | 機能ID | 応用 |
---|---|---|---|---|---|
1 | Mozilla Firefox | 35 | FTPInfo | 69 | ClassicFTP |
2 | コモドアイスドラゴン | 36 | LinasFTP | 70 | PuTTY / KiTTY |
3 | アップルサファリ | 37 | FileZilla | 71 | サンダーバード |
4 | K-メレオン | 38 | スタッフ-FTP | 72 | Foxmail |
五 | 海猿 | 39 | BlazeFtp | 73 | ポコメイル |
6 | 群れ | 40 | NETFile | 74 | IncrediMail |
7 | NETGATE BlackHawk | 41 | GoFTP | 75 | Gmail通知プロ |
8 | ルナスケープ | 42 | ALFTP | 76 | メールをチェックする |
ナイン | グーグルクローム | 43 | DeluxeFTP | 77 | WinFtp |
十 | オペラ | 44 | 総司令官 | 78 | マーティン・プリクリル |
十一 | QTWebブラウザ | 45 | FTPGetter | 79 | 32BitFtp |
12 | QupZilla | 46 | WS_FTP | 80 | FTPナビゲーター |
13 | インターネットエクスプローラ | 47 | メールクライアント構成ファイル | 81 | 郵送
(softwarenetz) |
14 | オペラ2 | 48 | フルチルトポーカー | 82 | オペラメール |
15 | Cyberfox | 49 | ポーカースター | 83 | ポストボックス |
16 | 淡い月 | 50 | ExpanDrive | 84 | FossaMail |
17 | ウォーターフォックス | 51 | スティード | 85 | ベッキー! |
18 | ピジン | 52 | FlashFXP | 86 | POP3 |
19 | SuperPutty | 53 | NovaFTP | 87 | 見通し |
20 | FTPShell | 54 | NetDrive | 88 | Ymail2 |
21 | NppFTP | 55 | トータルコマンダー2 | 89 | Trojitá |
22 | MyFTP | 56 | SmartFTP | 90 | TrulyMail |
23 | FTPBox | 57 | FARマネージャー | 91 | .spnファイル |
24 | シェロッドFTP | 58 | Bitvise | 92 | To-Doデスクリスト |
25 | 今すぐFTP | 59 | RealVNC
TightVNC |
93 | スティッキー |
26 | NexusFile | 60 | mSecure Wallet | 94 | NoteFly |
27 | Xftp | 61 | 同期性 | 95 | NoteZilla |
28 | EasyFTP | 62 | FreshFTP | 96 | ポストイット |
29 | SftpNetDrive | 63 | BitKinex | 97 | KeePass |
30 | AbleFTP | 64 | UltraFXP | 98 | エンパス |
31 | JaSFtp | 65 | FTP Now 2 | 99 | 私のRoboForm |
32 | 自動化 | 66 | Vandyk SecureFX | 100 | 1パスワード |
33 | サイバーダック | 67 | Odin Secure FTP Expert | 101 | Mikrotik WinBox |
34 | フルシンク | 68 | フリング |
ネットワーキング
ネットワークの相互作用を記録するために対処する必要がある2つの問題があります。
- コマンドセンターは、攻撃時にのみ使用できます。
- Wiresharkはループバックでボット通信を記録しないため、他の手段を使用する必要があります。
最も簡単な解決策は、Lokiが通信するCnCアドレスをlocalhostに転送することです。ボットの場合、サーバーは応答しませんが、いつでも使用できるようになりましたが、ボットの通信を記録する必要はありません。2番目の問題を解決するために、RawCapユーティリティを使用します。これにより、pcapに必要な通信を記述できます。次に、Wiresharkで記録されたpcapを解析します。
各通信の前に、ボットはCnCの可用性をチェックし、利用可能な場合はソケットを開きます。すべてのネットワーク通信は、TCPプロトコルを使用してトランスポートレベルで行われ、アプリケーションレベルではHTTPが使用されます。
次の表は、Lokiが標準で使用するパケットヘッダーを示しています。
フィールド | 値 | 説明 |
---|---|---|
ユーザーエージェント | Mozilla / 4.08(Charon; Inferno) | Lokiの典型的なユーザーエージェント |
受け入れる | * / * | |
コンテンツタイプ | アプリケーション/オクテットストリーム | |
コンテンツエンコーディング | バイナリ | |
コンテンツキー | 7DE968CC | 以前のヘッダーのハッシュ結果(ハッシュは、多項式0xE8677835を使用したカスタムCRCアルゴリズムによって実行されます) |
接続 | 閉じる |
- 記録されるデータの構造はボットのバージョンによって異なり、以前のバージョンでは、暗号化と圧縮のオプションを担当するフィールドはありません。
- サーバーは、要求のタイプによって、受信したデータの処理方法を決定します。サーバーが読み取ることができるデータには、次の7種類があります。
- 0x26盗まれたウォレットデータ
- 0x27盗まれたアプリケーションデータ
- 0x28サーバーからのコマンド要求
- 0x29盗まれたファイルのアンロード
- 0x2A POS
- 0x2Bキーロガーデータ
- 0x2Cスクリーンショット
- 調べた例では、0x27、0x28、および0x2Bのみが存在していました。
- 各リクエストには、ボットと感染したシステムに関する一般的な情報が含まれています。これにより、サーバーは1台のマシンのすべてのレポートを識別し、リクエストのタイプに応じた情報があります。
- ボットの最新バージョンでは、データ圧縮のみが実装されており、暗号化されたフィールドは将来のために準備され、サーバーによって処理されません。
- オープンソースのAPLibライブラリは、データの圧縮に使用されます。
盗まれたデータを使用してリクエストを作成する場合、ボットはサイズ0x1388(5000バイト)のバッファーを割り当てます。0x27リクエストの構造を次の表に示します。
バイアス | サイズ | 値 | 説明 |
---|---|---|---|
0x0 | 0x2 | 0x0012 | ボットバージョン |
0x2 | 0x2 | 0x0027 | リクエストタイプ(盗まれたデータの送信) |
0x4 | 0xD | ckav.ru | バイナリID(XXXXX11111も発生します) |
0x11 | 0x10 | - | ユーザー名 |
0x21 | 0x12 | - | コンピュータネーム |
0x33 | 0x12 | - | コンピューターのドメイン名 |
0x45 | 0x4 | - | 画面の解像度(幅と高さ)
|
0x49 | 0x4 | - | |
0x4D | 0x2 | 0x0001 | ユーザー権限フラグ(管理者の場合は1) |
0x4F | 0x2 | 0x0001 | SIDフラグ(設定されている場合は1) |
0x51 | 0x2 | 0x0001 | システムビットネスフラグ(x64の場合は1) |
0x53 | 0x2 | 0x0006 | Windowsバージョン(メジャーバージョン番号) |
0x55 | 0x2 | 0x0001 | Windowsバージョン(マイナーバージョン番号) |
0x57 | 0x2 | 0x0001 | 追加のシステム情報(1 = VER_NT_WORKSTATION) |
0x59 | 0x2 | - | |
0x5B | 0x2 | 0x0000 | 盗まれたデータは送信されましたか |
0x5D | 0x2 | 0x0001 | データ圧縮が使用されました |
0x5F | 0x2 | 0x0000 | 圧縮タイプ |
0x61 | 0x2 | 0x0000 | データ暗号化が使用されました |
0x63 | 0x2 | 0x0000 | 暗号化の種類 |
0x65 | 0x36 | - | MachineGuidレジスタ値からのMD5 |
0x9B | - | - | 圧縮された盗難データ |
バッファーサイズ:0x2BC(700バイト)
バイアス | サイズ | 値 | 説明 |
---|---|---|---|
0x0 | 0x2 | 0x0012 | ボットバージョン |
0x2 | 0x2 | 0x0028 | リクエストタイプ(コマンドセンターからのコマンドリクエスト) |
0x4 | 0xD | ckav.ru | バイナリID(XXXXX11111も発生します) |
0x11 | 0x10 | - | ユーザー名 |
0x21 | 0x12 | - | コンピュータネーム |
0x33 | 0x12 | - | コンピューターのドメイン名 |
0x45 | 0x4 | - | 画面の解像度(幅と高さ) |
0x49 | 0x4 | - | |
0x4D | 0x2 | 0x0001 | ユーザー権限フラグ(管理者の場合は1) |
0x4F | 0x2 | 0x0001 | SIDフラグ(設定されている場合は1) |
0x51 | 0x2 | 0x0001 | システムビットネスフラグ(x64の場合は1) |
0x53 | 0x2 | 0x0006 | Windowsバージョン(メジャーバージョン番号) |
0x55 | 0x2 | 0x0001 | Windowsバージョン(マイナーバージョン番号) |
0x57 | 0x2 | 0x0001 | 追加のシステム情報(1 = VER_NT_WORKSTATION) |
0x59 | 0x2 | 0xFED0 | |
0x5B | 0x36 | - | MachineGuidレジスタ値からのMD5 |
バッファサイズ:パケット内の各コマンドに対して0x10(16バイト)+ 0x10(16バイト)。
HTTPヘッダー(データの開始) | \ r \ n \ r \ n | [0D 0A 0D 0A] | 4バイト | ||
- | - | 4 | |||
2 | [00 00 00 02] | 4 | |||
4 |
4 |
4 |
4 |
() |
|
---|---|---|---|---|---|
#0
EXE- |
[00 00 00 00] | [00 00 00 00] | [00 00 00 00] | [00 00 00 23] | www.notsogood.site/malicious.exe |
#1
DLL |
[00 00 00 00] | [00 00 00 01] | [00 00 00 00] | [00 00 00 23] | www.notsogood.site/malicious.dll |
#2
EXE- |
[00 00 00 00] | [00 00 00 02] | [00 00 00 00] | [00 00 00 23] | www.notsogood.site/malicious.exe |
#8
(HDB file) |
[00 00 00 00] | [00 00 00 08] | [00 00 00 00] | [00 00 00 00] | - |
#9
|
[00 00 00 00] | [00 00 00 09] | [00 00 00 00] | [00 00 00 00] | - |
#10
|
[00 00 00 00] | [00 00 00 0A] | [00 00 00 00] | [00 00 00 00] | - |
#14
Loki |
[00 00 00 00] | [00 00 00 0E] | [00 00 00 00] | [00 00 00 00] | - |
#15
Loki |
[00 00 00 00] | [00 00 00 0F] | [00 00 00 00] | [00 00 00 23] | www.notsogood.site/malicious.exe |
#16
サーバーからの応答をチェックする頻度を変更する |
[00 00 00 00] | [00 00 00 10] | [00 00 00 00] | [00 00 00 01] | 五 |
#17
ロキを削除して終了します |
[00 00 00 00] | [00 00 00 11] | [00 00 00 00] | [00 00 00 00] | - |
ネットワークトラフィックパーサー
この分析のおかげで、Lokiのネットワーク相互作用を解析するために必要なすべての情報が得られました。
パーサーはPythonで実装され、入力としてpcapファイルを受け取り、その中のLokiに属するすべての通信を検索します。
まず、dkptライブラリを使用してすべてのTCPパケットを検索しましょう。httpパケットのみを受信するために、使用するポートにフィルターを設定しましょう。受信したhttpパケットの中から、よく知られているLokiヘッダーを含むパケットを選択し、読み取り可能な形式で情報を抽出するために解析する必要のある通信を取得します。
for ts, buf in pcap:
eth = dpkt.ethernet.Ethernet(buf)
if not isinstance(eth.data, dpkt.ip.IP):
ip = dpkt.ip.IP(buf)
else:
ip = eth.data
if isinstance(ip.data, dpkt.tcp.TCP):
tcp = ip.data
try:
if tcp.dport == 80 and len(tcp.data) > 0: # HTTP REQUEST
if str(tcp.data).find('POST') != -1:
http += 1
httpheader = tcp.data
continue
else:
if httpheader != "":
print('Request information:')
pkt = httpheader + tcp.data
httpheader = ""
if debug:
print(pkt)
req += 1
request = dpkt.http.Request(pkt)
uri = request.headers['host'] + request.uri
parsed_payload['Network']['Source IP'] = socket.inet_ntoa(ip.src)
parsed_payload['Network']['Destination IP'] = socket.inet_ntoa(ip.dst)
parsed_payload_same['Network']['CnC'] = uri
parsed_payload['Network']['HTTP Method'] = request.method
if uri.find("fre.php"):
print("Loki detected!")
pt = parseLokicontent(tcp.data, debug)
parsed_payload_same['Malware Artifacts/IOCs']['User-Agent String'] = request.headers['user-agent']
print(json.dumps(parsed_payload, ensure_ascii=False, sort_keys=False, indent=4))
parsed_payload['Network'].clear()
parsed_payload['Compromised Host/User Data'].clear()
parsed_payload['Malware Artifacts/IOCs'].clear()
print("----------------------")
if tcp.sport == 80 and len(tcp.data) > 0: # HTTP RESPONCE
resp += 1
if pt == 40:
print('Responce information:')
parseC2commands(tcp.data, debug)
print("----------------------")
pt = 0
except(dpkt.dpkt.NeedData, dpkt.dpkt.UnpackError):
continue
すべてのLokiリクエストでは、最初の4バイトがボットのバージョンとリクエストタイプを担当します。これらの2つのパラメーターを使用して、データの処理方法を決定します。
def parseLokicontent(data, debug):
index = 0
botV = int.from_bytes(data[0:2], byteorder=sys.byteorder)
parsed_payload_same['Malware Artifacts/IOCs']['Loki-Bot Version'] = botV
payloadtype = int.from_bytes(data[2:4], byteorder=sys.byteorder)
index = 4
print("Payload type: : %s" % payloadtype)
if payloadtype == 39:
parsed_payload['Network']['Traffic Purpose'] = "Exfiltrate Application/Credential Data"
parse_type27(data, debug)
elif payloadtype == 40:
parsed_payload['Network']['Traffic Purpose'] = "Get C2 Commands"
parse_type28(data, debug)
elif payloadtype == 43:
parsed_payload['Network']['Traffic Purpose'] = "Exfiltrate Keylogger Data"
parse_type2b(lb_payload)
elif payloadtype == 38:
parsed_payload['Network']['Traffic Purpose'] = "Exfiltrate Cryptocurrency Wallet"
elif payloadtype == 41:
parsed_payload['Network']['Traffic Purpose'] = "Exfiltrate Files"
elif payloadtype == 42:
parsed_payload['Network'].['Traffic Purpose'] = "Exfiltrate POS Data"
elif payloadtype == 44:
parsed_payload['Network']['Traffic Purpose'] = "Exfiltrate Screenshots"
return payloadtype
次の行は、サーバーからの応答を解析することです。有用な情報のみを読み取るには、\ r \ n \ r \ nシーケンスを探します。これは、パケットヘッダーの終わりとサーバーからのコマンドの始まりを定義します。
def parseC2commands(data, debug):
word = 2
dword = 4
end = data.find(b'\r\n\r\n')
if end != -1:
index = end + 4
if (str(data).find('<html>')) == -1:
if debug:
print(data)
fullsize = getDWord(data, index)
print("Body size: : %s" % fullsize)
index += dword
count = getDWord(data, index)
print("Commands: : %s" % count)
if count == 0:
print('No commands received')
else:
index += dword
for i in range(count):
print("Command: %s" % (i + 1))
id = getDWord(data, index)
print("Command ID: %s" % id)
index += dword
type = getDWord(data, index)
print("Command type: %s" % type)
index += dword
timelimit = getDWord(data, index)
print("Command timelimit: %s" % timelimit)
index += dword
datalen = getDWord(data, index)
index += dword
command_data = getString(data, index, datalen)
print("Command data: %s" % command_data)
index += datalen
else:
print('No commands received')
return None
これでパーサーのアルゴリズムの主要部分の分析が終了し、出力で得られる結果に移ります。すべての情報はjson形式で表示されます。
以下は、さまざまなボットの通信から取得され、さまざまなCnCを使用し、さまざまな環境で記録されたパーサーの作業結果の画像です。
Request information:
Loki detected!
Payload type: 39
Decompressed data:
{'Module': {'Mozilla Firefox'}, 'Version': {0}, 'Data': {'domain': {'https://accounts.google.com'}, 'username': {'none@gmail.com'}, 'password': {'test'}}}
{'Module': {'NppFTP'}, 'Version': {0}, 'Data': {b'<?xml version="1.0" encoding="UTF-8" ?>\r\n<NppFTP defaultCache="%CONFIGDIR%\\Cache\\%USERNAME%@%HOSTNAME%" outputShown="0" windowRatio="0.5" clearCache="0" clearCachePermanent="0">\r\n <Profiles />\r\n</NppFTP>\r\n'}}
{
"Network": {
"Source IP": "-",
"Destination IP": "185.141.27.187",
"HTTP Method": "POST",
"Traffic Purpose": "Exfiltrate Application/Credential Data",
"First Transmission": true
},
"Compromised Host/User Data": {},
"Malware Artifacts/IOCs": {}
}
上記は、サーバー0x27(アプリケーションデータのアップロード)への要求の例です。テストのために、アカウントはMozilla Firefox、NppFTP、FileZillaの3つのアプリケーションで作成されました。Lokiには、アプリケーションデータを記録するための3つのオプションがあります。
- SQLデータベースの形式(パーサーはデータベースを保存し、その中の使用可能なすべての行を表示します)。
- 例のFirefoxのように、開いた形式で。
- NppFTPやFileZillaのようなxmlファイルとして。
Request information:
Loki detected!
Payload type: 39
No data stolen
{
"Network": {
"Source IP": "-",
"Destination IP": "185.141.27.187",
"HTTP Method": "POST",
"Traffic Purpose": "Exfiltrate Application/Credential Data",
"First Transmission": false
},
"Compromised Host/User Data": {},
"Malware Artifacts/IOCs": {}
}
2番目の要求はタイプ0x28で、サーバーからのコマンドを要求します。
Responce information:
Body size: 26
Commands: 1
Command: 1
Command ID: 0
Command type: 9
Command timelimit: 0
Command data: 35
キーロガーの開始に応答して1つのコマンドを送信したCnCからの応答の例。そして、その後のキーロガーデータのアンロード。
Request information:
Loki detected!
Payload type: : 43
{
"Network": {
"Source IP": "-",
"Destination IP": "185.141.27.187",
"HTTP Method": "POST",
"Traffic Purpose": "Exfiltrate Keylogger Data"
},
"Compromised Host/User Data": {},
"Malware Artifacts/IOCs": {}
}
作業の最後に、パーサーはボットからの各要求に含まれる情報(ボットとシステムに関する情報)、およびLokiに関連付けられた要求と応答の数をpcapファイルに出力します。
General information:
{
"Network": {
"CnC": "nganyin-my.com/chief6/five/fre.php"
},
"Compromised Host/User Description": {
"User Name": "-",
"Hostname": "-",
"Domain Hostname": "-",
"Screen Resolution": "1024x768",
"Local Admin": true,
"Built-In Admin": true,
"64bit OS": false,
"Operating System": "Windows 7 Workstation"
},
"Malware Artifacts/IOCs": {
"Loki-Bot Version": 18,
"Binary ID": "ckav.ru",
"MD5 from GUID": "-",
"User-Agent String": "Mozilla/4.08 (Charon; Inferno)"
}
}
Requests: 3
Responces: 3
完全なパーサーコードは、github.com / Group-IB / LokiParserで入手できます。
結論
この記事では、Lokiマルウェアを詳しく調べ、その機能を分解し、インシデント分析プロセスを大幅に簡素化し、感染したコンピューターから何が盗まれたかを正確に理解するのに役立つネットワークトラフィックパーサーを実装しました。Lokiの開発はまだ進行中ですが、バージョン1.8(およびそれ以前)のみがマージされています。これは、セキュリティの専門家が毎日遭遇するバージョンです。
次の記事では、別の人気のあるデータスティーラーであるポニーを分析し、これらのマルウェアを比較します。
侵害の指標(IOC):
ウルス:
- nganyin-my.com/chief6/five/fre.php
- wardia.com.pe/wp-includes/texts/five/fre.php
- broken2.cf/Work2/fre.php
- 185.141.27.187/danielsden/ver.php
- MD5ハッシュ:B0C33B1EF30110C424BABD66126017E5
- User-Agent String: «Mozilla/4.08 (Charon; Inferno)»
- Binary ID: «ckav.ru»