少し間が空きましたが、シリーズを再開します。 第1〜3回でデータの取り込み・最初のルール作成・Timelineでの 攻撃チェーン調査まで進みました。第4回はその続きです。 EQLのsequenceで「失敗のあとに成功した」攻撃パターンを 自動検出し、ES|QLで「どのIPが最も危険か」「何バイト送られたか」 を数値で把握する方法を紹介します。CaseとNotesを使った 調査記録の残し方も扱います。
シリーズのこれまでの流れ:
※本シリーズで使用するデータセットは、第1回の記事からダウンロードできます。
この記事を読むと何ができるか
- EQLのsequence構文で攻撃の順序パターンを自動検出できる
- ES|QLでIPごとのログイン失敗件数・大容量通信を集計できる
- CaseとNotesで調査内容をチームで共有・記録できる
EQL(Correlation)で攻撃の「順序」を自動検出する
なぜ使うのか
第3回では「目でイベントを追って」攻撃の流れを確認しました。しかし実務では、1日に何千件ものイベントが流れる中から「特定の順番で起きたイベントの組み合わせ」を手動で見つけるのは現実的ではありません。
EQL(Event Query Language)の sequence 構文を使うと、「AのあとにBが起きた組み合わせ」を自動で検出できます。
EQLのsequenceクエリを書く
このサンプルデータでは
event.categoryの値が"iam"です。Elastic SecurityのEQLパーサーが期待するauthenticationカテゴリとは名称が異なるため、any where event.category == "iam"という書き方を使います。
TimelineのCorrelationタブを開き、次のクエリを入力します。
sequence by source.ip
[ any where event.category == "iam"
and event.action == "user-login"
and event.outcome == "failure" ]
[ any where event.category == "iam"
and event.action == "user-login"
and event.outcome == "success"
and user.name in ("admin_root", "admin", "root", "administrator") ]

このクエリの意味を整理します。確認ポイント:
source.ip: 192.168.1.100のシーケンスが検出されているか- 1つ目のイベント(failure)と2つ目のイベントが時系列順に並んでいるか
45.33.21.11のシーケンスも検出されているか
EQLでできること・できないことを整理しておきます。
| できること | できないこと |
|---|---|
| イベントの順序を条件にする | 集計・合計値の計算 |
| 複数イベントをまとめて1件として扱う | フィールドの加工・変換 |
| 時間的な近接(maxspan)を条件にする | 複数インデックスにまたがる複雑な結合 |
集計や加工が必要なときは、次のES|QLを使います。
ES|QLで集計・分析する
KQLは「条件に一致するイベントを見つける」検索ツールです。一方ES|QLは「見つけたイベントを集計・加工・ランキングする」分析ツールです。「どのIPが一番多く失敗しているか」「何バイト送られたか」を素早く把握したいときに使います。
ES|QLはパイプ(|)でコマンドをつなげる構造です。「FROMでデータを取り出し、WHEREで絞り込み、STATSで集計し、SORTで並べる」という順番で読むと理解しやすいです。
TimelineのES|QLタブを開きます。
クエリ1:ログイン失敗をIPごとに集計する
FROM training-security-logs
| WHERE event.action == "user-login" AND event.outcome == "failure"
| STATS failure_count = COUNT(*) BY source.ip
| SORT failure_count DESC期待される結果:
| source.ip | failure_count |
|---|---|
| 45.33.21.11 | 8 |
| 192.168.1.100 | 4 |

45.33.21.11 が外部から8回、192.168.1.100 が内部から4回失敗していることが一目でわかります。
クエリ2:攻撃対象のユーザー名を集計する
FROM training-security-logs
| WHERE event.action == "user-login" AND event.outcome == "failure"
| STATS attempt_count = COUNT(*) BY user.name
| SORT attempt_count DESC
期待される結果:
| user.name | attempt_count |
|---|---|
| guest | 5 |
| admin_root | 3 |
| administrator | 2 |
| admin | 1 |
| root | 1 |

guest が最も多く試されています。攻撃者が「存在しそうなデフォルトアカウント名」から順番に試している典型的なパターンです。
クエリ3:大容量通信を探す
FROM training-security-logs
| WHERE event.action == "data-transfer"
| STATS max_bytes = MAX(network.bytes),
total_bytes = SUM(network.bytes)
BY source.ip, destination.ip
| WHERE max_bytes > 10000000
| SORT max_bytes DESC
期待される結果:
| source.ip | destination.ip | max_bytes | total_bytes |
|---|---|---|---|
| 192.168.1.100 | 103.10.10.100 | 85,000,000 | 85,000,000以上 |

103.10.10.100 への85MB超の転送が浮かび上がります。このようなデータ量ベースの異常検知はKQLでは書けず、ES|QLが得意とする用途です。
Caseに調査内容を登録する
セキュリティ調査は個人作業ではありません。「何を見つけたか」「誰が対応しているか」「どう判断したか」をチームで共有し、記録として残すことが実務では不可欠です。
Security → Alerts を開き、source.ip: 192.168.1.100 のアラートをクリックして flyout を開きます。「Add to existing case → Add to new case」を選択します。

Case の記入例:
| 項目 | 入力例 |
|---|---|
| タイトル | 内部ブルートフォース攻撃 from 192.168.1.100 |
| Severity | Medium(ログイン成功・データ持ち出しが確認されているため) |
| Tags | brute-force, data-exfiltration, prod-srv-01 |
説明欄のテンプレート(初動調査メモ):
## 概要
2025-03-18 UTC 10:01〜10:10 の間に、192.168.1.100 から
PROD-SRV-01 に対する一連の攻撃を確認。
## 確認された活動
- 10:01台:ポートスキャン(445, 139, 80, 3389, 22)
- 10:03台:admin_root へのブルートフォース → 10:03:06 に成功
- 10:03〜10:04台:whoami, net user, net localgroup による偵察
- 10:05〜10:07台:スケジュールタスク・レジストリによる永続化
- 10:06〜10:10台:103.10.10.100 への C2 接続・85MB のデータ持ち出し
## 次のアクション
- [ ] 192.168.1.100 の所有者・用途を確認する
- [ ] admin_root アカウントのパスワードを即時変更する
- [ ] 103.10.10.100 への通信をブロックする
- [ ] PROD-SRV-01 のスケジュールタスク・レジストリを精査する

CaseのSeverityをアラートのSeverityより高くした理由:第2回で作ったルールはSeverity: Low(単純なログイン失敗の検知)でした。しかしTimelineで調査した結果、ログイン成功・永続化・データ持ち出しまで確認できました。ルールのSeverityはイベントの性質、CaseのSeverityは調査で判明した実際の影響度を反映させます。
Notesで調査メモを残す
Timelineは分析ツールですが、「自分が何を考えながら調査したか」はTimelineには残りません。Notesを使うことで、「事実(ログ)」「解釈(何が起きているか)」「仮説(次に何を確認すべきか)」を時系列のイベントに紐づけて残せます。
Timelineで気になるイベントの行にカーソルを合わせ、行左端のアクションアイコンから「Add note」をクリックします。

良いNotesには3つの要素があります。
| 要素 | 内容 | 例 |
|---|---|---|
| 事実 | ログから読み取れること | 10:03:06 に admin_root で user-login success |
| 解釈 | そのイベントが意味すること | ブルートフォース成功。この時点から侵害が始まっている |
| 仮説・次のアクション | 次に確認すること | 10:03:06 以降の admin_root の行動を追う |
実際のメモ例(10:03:06の成功イベントに紐づけて記録):
【事実】
192.168.1.100 から admin_root での user-login が success。
直前の 10:03:00〜10:03:05 に同 IP から失敗が4件続いている。
【解釈】
ブルートフォース攻撃が成功し、PROD-SRV-01 への侵入が完了した
と判断できる。この時点が攻撃の「侵入成功ライン」。
【次のアクション】
admin_root として実行されたプロセスを 10:03:06 以降で追う。
特に cmd.exe, powershell.exe, schtasks.exe の実行を確認する。

ノートを追加しているものに赤い点が付きます。

この章のまとめ
| 機能 | 使う場面 |
|---|---|
| KQL(第3回) | イベントを絞り込んで目で読む |
| Correlation(EQL) | イベントの順序パターンを自動検出する |
| ES|QL | 集計・ランキング・異常値の発見 |
| Case | 調査内容をチームで共有・記録する |
| Notes | 調査の思考プロセスを時系列に紐づける |
第4回チェックリスト
- [ ] EQLのsequenceクエリを実行し、
192.168.1.100のブルートフォースシーケンスが検出されている - [ ] ES|QLでIPごとのログイン失敗件数を集計し、
45.33.21.11(8件)と192.168.1.100(4件)が確認できている - [ ] 調査内容をCaseに登録し、タイトル・説明・次のアクションが入力されている
- [ ]
10:03:06の成功イベントにNotesを追加し、事実・解釈・次のアクションが記録されている
次回は: ルールを「作る」だけで終わらせない最終回です。Alert suppressionとExceptionsを正しく使い分けてノイズを減らし、ルールタイプ(Custom query / Threshold / ES|QL)を目的に応じて使い分ける方法を学びます。


