Linux ELFマルウェアの解析
使用するタイミング
- Linuxサーバーまたはコンテナが侵害され、疑わしいELFバイナリが発見された場合
- Linuxボットネット(Mirai、Gafgyt、XorDDoS)、クリプトマイナー、ランサムウェアを解析する場合
- クラウドインフラ、Dockerコンテナ、Kubernetesポッドを標的とするマルウェアを調査する場合
- Linuxルートキットやカーネルモジュールをリバースエンジニアリングする場合
- x86_64、ARM、MIPSアーキテクチャ向けにコンパイルされたクロスプラットフォームマルウェアを解析する場合
使用しない場合: Windows PEバイナリ解析には使用しないでください。WindowsマルウェアにはPEStudio、Ghidra、IDAを使用してください。
前提条件
- 逆アセンブルと逆コンパイルのためのGhidraまたはIDA(Linux ELF対応)
- 開発ツールがインストールされたLinux解析VM(Ubuntu 22.04推奨)
- 動的解析とデバッグのためのstrace、ltrace、GDB
- 静的検査のためのreadelf、objdump、nm(GNU binutils)
- 迅速なバイナリトリアージとスクリプト解析のためのRadare2
- 隔離されたコンテナベースのマルウェア実行のためのDocker
ワークフロー
ステップ1: ELFバイナリのプロパティを特定する
ELFヘッダーと基本プロパティを調査します:
# ファイルタイプの識別
file suspect_binary
# 詳細なELFヘッダー解析
readelf -h suspect_binary
# セクションヘッダー
readelf -S suspect_binary
# プログラムヘッダー(セグメント)
readelf -l suspect_binary
# シンボルテーブル(ストリップされていない場合)
readelf -s suspect_binary
nm suspect_binary 2>/dev/null
# 動的リンク情報
readelf -d suspect_binary
ldd suspect_binary 2>/dev/null # アーキテクチャが一致する場合のみ!
# ハッシュの計算
md5sum suspect_binary
sha256sum suspect_binary
# パッキング/UPXの確認
upx -t suspect_binary
# PythonによるELF解析
from elftools.elf.elffile import ELFFile
import hashlib
with open("suspect_binary", "rb") as f:
data = f.read()
sha256 = hashlib.sha256(data).hexdigest()
with open("suspect_binary", "rb") as f:
elf = ELFFile(f)
print(f"SHA-256: {sha256}")
print(f"クラス: {elf.elfclass}-bit")
print(f"エンディアン: {'リトル' if elf.little_endian else 'ビッグ'}")
print(f"マシン: {elf.header.e_machine}")
print(f"タイプ: {elf.header.e_type}")
print(f"エントリポイント: 0x{elf.header.e_entry:X}")
# ストリップされているか確認
symtab = elf.get_section_by_name('.symtab')
print(f"ストリップ: {'はい' if symtab is None else 'いいえ'}")
# セクションエントロピー解析
import math
from collections import Counter
for section in elf.iter_sections():
data = section.data()
if len(data) > 0:
entropy = -sum((c/len(data)) * math.log2(c/len(data))
for c in Counter(data).values() if c > 0)
if entropy > 7.0:
print(f" [!] 高エントロピーセクション: {section.name} ({entropy:.2f})")
ステップ2: 文字列と指標を抽出する
埋め込まれたIOCと機能の手がかりを検索します:
# ASCII文字列
strings suspect_binary > strings_output.txt
# ネットワーク指標の検索
grep -iE "(http|https|ftp)://" strings_output.txt
grep -iE "([0-9]{1,3}\.){3}[0-9]{1,3}" strings_output.txt
grep -iE "[a-zA-Z0-9.-]+\.(com|net|org|io|ru|cn)" strings_output.txt
# シェルコマンドの検索
grep -iE "(bash|sh|wget|curl|chmod|/tmp/|/dev/)" strings_output.txt
# 暗号通貨マイニング指標の検索
grep -iE "(stratum|xmr|monero|pool\.|mining)" strings_output.txt
# SSH/認証情報窃取の検索
grep -iE "(ssh|authorized_keys|id_rsa|shadow|passwd)" strings_output.txt
# 永続化機構の検索
grep -iE "(crontab|systemd|init\.d|rc\.local|ld\.so\.preload)" strings_output.txt
# 難読化された文字列のFLOSS(利用可能な場合)
floss suspect_binary
ステップ3: システムコールとライブラリ使用を分析する
マルウェアが使用するシステムコールとライブラリを特定します:
# インポート関数のリスト(動的リンク)
readelf -r suspect_binary | grep -E "socket|connect|exec|fork|open|write|bind|listen"
# 実行中のシステムコールトレース(隔離VM内のみ)
strace -f -e trace=network,process,file -o strace_output.txt ./suspect_binary
# ライブラリコールのトレース
ltrace -f -o ltrace_output.txt ./suspect_binary
# 注目すべき主要システムコール:
# ネットワーク: socket, connect, bind, listen, accept, sendto, recvfrom
# プロセス: fork, execve, clone, kill, ptrace
# ファイル: open, read, write, unlink, rename, chmod
# 永続化: inotify_add_watch(ファイル監視)
ステップ4: GDBによる動的解析
マルウェアをデバッグして実行時動作を観察します:
# GDBでバイナリを起動
gdb ./suspect_binary
# 主要関数にブレークポイントを設定
(gdb) break main
(gdb) break socket
(gdb) break connect
(gdb) break execve
(gdb) break fork
# 実行して解析
(gdb) run
(gdb) info registers # レジスタ状態を表示
(gdb) x/20s $rdi # 文字列引数を調査
(gdb) bt # バックトレース
(gdb) continue
# ストリップされたバイナリの場合、エントリポイントにブレーク
(gdb) break *0x400580 # readelfから得たエントリポイント
(gdb) run
# 実行中のネットワーク接続監視
# 別のターミナルで:
ss -tlnp # 待受ソケット一覧
ss -tnp # 確立済み接続一覧
ステップ5: Ghidraでリバースエンジニアリング
ELFバイナリの深いコード解析を実行します:
GhidraによるLinux ELF解析:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. インポート: ファイル → インポート → ELFバイナリを選択
- GhidraがELF形式とアーキテクチャを自動検出
- デフォルトの解析オプションを受け入れる
2. 主要解析対象:
- main()関数(ストリップされている場合はエントリポイント)
- ソケット作成および接続関数
- コマンドディスパッチロジック(受信データに対するswitch/case)
- 暗号化/エンコードルーチン
- 永続化インストールコード
- 自己増殖/スキャン機能
3. Mirai系ボットネットの場合:
- 総当たり用の認証情報リスト(telnet/SSH)
- 攻撃モジュール選択(UDPフラッド、SYNフラッド、ACKフラッド)
- スキャナーモジュール(脆弱デバイスのポートスキャン)
- キラーモジュール(競合ボットネットの排除)
4. クリプトマイナーの場合:
- マイニングプール接続(stratumプロトコル)
- ウォレットアドレス文字列
- CPU/GPU使用率関数
- プロセス隠蔽技術
ステップ6: Linux固有の永続化を分析する
永続化機構を確認します:
# LD_PRELOADルートキットの確認
strings suspect_binary | grep "ld.so.preload"
# /etc/ld.so.preloadに書き込むマルウェアは全ての動的ライブラリコールをフック可能
# crontab永続化の確認
strings suspect_binary | grep -i "cron"
# systemdサービス作成の確認
strings suspect_binary | grep -iE "systemd|\.service|systemctl"
# initスクリプト作成の確認
strings suspect_binary | grep -iE "init\.d|rc\.local|update-rc"
# SSH鍵注入の確認
strings suspect_binary | grep -i "authorized_keys"
# カーネルモジュール(ルートキット)ロードの確認
strings suspect_binary | grep -iE "insmod|modprobe|init_module"
# プロセス隠蔽の確認
strings suspect_binary | grep -iE "proc|readdir|getdents"
主要概念
| 用語 | 定義 |
|---|---|
| ELF (Executable and Linkable Format) | Linux実行可能ファイル、共有ライブラリ、コアダンプの標準バイナリ形式。ヘッダー、セクション、セグメントを含む |
| ストリップ済みバイナリ | デバッグシンボルが削除されたELFバイナリ。関数名が失われるためリバースエンジニアリングが困難になる |
| LD_PRELOAD | 他のすべてのライブラリより先に読み込む共有ライブラリを指定するLinux環境変数。ルートキットによりシステムライブラリコールのインターセプトに悪用される |
| strace | プロセスが行ったすべてのシステムコールとシグナルを記録するLinuxシステムコールトレーサー。ファイル、ネットワーク、プロセス操作を明らかにする |
| GOT/PLT | Global Offset TableおよびProcedure Linkage Table。動的リンクのためのELF構造であり、関数フッキングのためにハイジャックされる可能性がある |
| 静的リンク | すべてのライブラリコードを含んでコンパイルされたバイナリ。IoTマルウェアでは、一致する共有ライブラリがないシステムで実行するために一般的 |
| Mirai | telnetブルートフォース経由でIoTデバイスを標的とする広範なLinuxボットネット。ソースコードが流出し、多数の亜種が生まれた |
ツールとシステム
- Ghidra: NSAリバースエンジニアリングツール。x86、x86_64、ARM、MIPSおよびその他のLinuxアーキテクチャ向けの完全なELFサポートを備える
- Radare2: コマンドラインインターフェースを備えたオープンソースのリバースエンジニアリングフレームワーク。迅速なバイナリ解析とスクリプティングに使用
- strace: ファイル、ネットワーク、プロセス操作を含むバイナリの動作を観察するLinuxシステムコールトレースツール
- GDB: GNUデバッガ。ブレークポイントの設定、メモリの調査、Linuxバイナリ実行のステップ実行に使用
- pyelftools: 自動化された解析パイプライン用にELFファイルをプログラムで解析するPythonライブラリ
一般的なシナリオ
シナリオ: 侵害されたLinuxサーバーで見つかったクリプトマイナーの解析
コンテキスト: クラウドサーバーがCPU使用率100%を示している。調査により、/tmpから不審な名前の未知のバイナリが実行されていることが判明。このバイナリがクリプトマイナーであることを確認し、攻撃者のウォレットとプールを特定する必要がある。
アプローチ:
- バイナリを解析VMにコピーし、SHA-256ハッシュを計算する
fileおよびreadelfを実行してアーキテクチャとリンクタイプを特定する- 文字列を抽出し、マイニングプールアドレス(stratum+tcp://)とウォレットアドレスを検索する
- サンドボックス内でstraceを実行し、ネットワーク接続(マイニングプール接続)を観察する
- Ghidraにインポートしてマイニングアルゴリズムと設定抽出を特定する
- 永続化機構(crontab、systemdサービス、SSH鍵)を確認する
- プールアドレス、ウォレット、更新用C2、永続化アーティファクトを含むすべてのIOCを文書化する
落とし穴:
- サンドボックス外でマルウェアに対して
lddを実行する(lddはバイナリ内のコードを実行する可能性がある) - x86_64で実行する前にARM/MIPSアーキテクチャを確認しない
- 永続化やクリーンアップを処理する可能性のある付属スクリプト(.shファイル)を見逃す
- 初期アクセスベクトル(マイナーがどのように展開されたか:SSHブルートフォース、Webエクスプロイト、コンテナエスケープ)を無視する
出力形式
LINUX ELFマルウェア解析レポート
====================================
ファイル: /tmp/.X11-unix/.rsync
SHA-256: e3b0c44298fc1c149afbf4c8996fb924...
タイプ: ELF 64-bit LSB executable, x86-64
リンク: 静的リンク(すべてのライブラリが埋め込まれている)
ストリップ: はい
サイズ: 2,847,232 bytes
パッカー: UPX 3.96(解析のために展開済み)
分類
ファミリ: XMRig クリプトマイナー(改変版)
亜種: C2更新機構を持つカスタムビルド
機能
[*] RandomXアルゴリズムによるXMR(Monero)マイニング
[*] 作業提出のためのStratumプール接続
[*] 設定更新のためのC2チェックイン
[*] プロセス名偽装(argv[0] = "[kworker/0:0]")
[*] 競合プロセスの強制終了(他のマイナーを停止)
[*] 再アクセス用のSSH鍵注入
ネットワーク指標
マイニングプール: stratum+tcp://pool.minexmr[.]com:4444
C2サーバー: hxxp://update.malicious[.]com/config
ウォレット: 49jZ5Q3b...Monero_Wallet_Address...
永続化
[1] Crontabエントリ: */5 * * * * /tmp/.X11-unix/.rsync
[2] SSH鍵が/root/.ssh/authorized_keysに追加
[3] Systemdサービス: /etc/systemd/system/rsync-daemon.service
[4] /etc/ld.so.preloadがプロセス隠蔽のために改変
プロセス隠蔽
LD_PRELOAD: /usr/lib/.libsystem.so
フック: readdir() /tmp/.X11-unix/.rsyncをlsから隠蔽
フック: fopen() /proc/*/mapsの読み取りから隠蔽