一覧SP-02-1 · EDR:端末の観測・検知・対応

EDRのプロセス・ファイル・通信ログ

端末、中央記録、対応処理を照合し、記録から言える範囲を判断します。

説明

EDRは端末の活動を集め、関連するイベントを分析して対応につなげます。記録の収集、検知、端末への措置は別々の処理です。

Sysmonのプロセス識別子という考え方に沿った固定記録を、SQLで調べます。時刻は開始からの秒、guidは短縮した教材用識別子です。PIDの再利用と中央への転送遅延を順に確かめます。

端末、中央記録、対応処理を照合し、記録から言える範囲を判断します。

1端末とイベントの種類を確認する

5行の記録を発生順に並べます。

from sp02 import * db = connect() show端末とイベントの種類を確認する(db,"SELECT record,host,t,kind,guid,pid FROM event ORDER BY t")

ws7のPID 410とws8のPID 410は別の端末です。PIDだけでは結び付けられません。

2同じPIDが再利用される

ws7だけに絞っても開始時刻が異なるプロセスが残ります。

show同じPIDが再利用される(db,"SELECT t,guid,pid,image FROM event WHERE host='ws7' AND kind='process' ORDER BY t")

0秒のg1と70秒のg2は別プロセスです。プロセス識別子と端末を組み合わせて追います。

3一つのプロセスの活動を並べる

g1に属するファイルと通信を取得します。

show一つのプロセスの活動を並べる(db,"SELECT t,kind,detail FROM event WHERE host='ws7' AND guid='g1' ORDER BY t")

起動、report.txtの生成、443番ポートへの接続が同じg1に対応します。この並びだけで悪意までは断定できません。

4起動元を確認する

プロセス起動の行には親の識別子もあります。

show起動元を確認する(db,"SELECT guid,parent,image FROM event WHERE kind='process' ORDER BY t")

g1とg2の親はg0です。ただし今回の資料にはg0の起動行がありません。親の実行ファイル名まで推測で補いません。

5実行ファイルとハッシュを対応付ける

公開試料のSHA-256を計算し、記録値と照合します。

print(hashlib.sha256実行ファイルとハッシュを対応付ける(PAYLOAD).hexdigest()) show(db,"SELECT e.guid,e.image,i.sha256 FROM event e JOIN image i USING(guid) WHERE e.kind='process'")

ハッシュはファイル内容の同一性を確認する値です。同じ名前の別ファイルや、同じファイルから起動した別プロセスを区別する材料になります。

6中央へ届くまでの時間を求める

ingestedは中央へ取り込んだ時刻です。

show中央へ届くまでの時間を求める(db,"SELECT record,t,ingested,ingested-t AS delay_seconds FROM event ORDER BY record")

接続記録3は発生8秒、取込20秒で12秒遅れています。中央で10秒時点に検索してもまだ見えません。

7検索時点による差を確認する

10秒までに中央へ届いたg1の記録だけを取得します。

show検索時点による差を確認する(db,"SELECT record,kind FROM event WHERE guid='g1' AND ingested<=10 ORDER BY t")

起動とファイルだけが出ます。検索結果に接続がないことと、端末が接続しなかったことは同じではありません。

まとめ

端末とプロセス識別子で活動を結び付けます。

ファイルのハッシュとプロセスの識別子は用途が異なります。

発生時刻と取込時刻を分けて調べます。

操作履歴と解説を振り返れます。

Python 3.15 / SQLite / 固定の端末記録

1
from sp02 import * db = connect() show(db,"SELECT record,host,t,kind,guid,pid FROM event ORDER BY t")
record | host | t | kind | guid | pid
1 | ws7 | 0 | process | g1 | 410
5 | ws8 | 3 | network | g3 | 410
2 | ws7 | 4 | file | g1 | 410
3 | ws7 | 8 | network | g1 | 410
4 | ws7 | 70 | process | g2 | 410
2
show(db,"SELECT t,guid,pid,image FROM event WHERE host='ws7' AND kind='process' ORDER BY t")
t | guid | pid | image
0 | g1 | 410 | diagnostic.exe
70 | g2 | 410 | viewer.exe
3
show(db,"SELECT t,kind,detail FROM event WHERE host='ws7' AND guid='g1' ORDER BY t")
t | kind | detail
0 | process | start
4 | file | report.txt
8 | network | 192.0.2.20:443
4
show(db,"SELECT guid,parent,image FROM event WHERE kind='process' ORDER BY t")
guid | parent | image
g1 | g0 | diagnostic.exe
g2 | g0 | viewer.exe
5
print(hashlib.sha256(PAYLOAD).hexdigest()) show(db,"SELECT e.guid,e.image,i.sha256 FROM event e JOIN image i USING(guid) WHERE e.kind='process'")
6beeffaa5d98894a2b7169eb6616bd5ac650232ed1cda83be77ae1c213d03001
guid | image | sha256
g1 | diagnostic.exe | 6beeffaa5d98894a2b7169eb6616bd5ac650232ed1cda83be77ae1c213d03001
6
show(db,"SELECT record,t,ingested,ingested-t AS delay_seconds FROM event ORDER BY record")
record | t | ingested | delay_seconds
1 | 0 | 1 | 1
2 | 4 | 6 | 2
3 | 8 | 20 | 12
4 | 70 | 71 | 1
5 | 3 | 4 | 1
7
show(db,"SELECT record,kind FROM event WHERE guid='g1' AND ingested<=10 ORDER BY t")
record | kind
1 | process
2 | file

演習完了