Windowsログオンの追跡
ws7の成功・失敗記録から、同じ起動区間の権限付与とプロセスを検索します。ログオン種別3はネットワーク、2は対話型です。成功や特権の割当だけで、具体的なコマンドの実行まで断定しない調べ方を学びます。
説明
Windowsの4624はログオンの成功、4625は失敗を表す監査イベントです。Logon IDはログオンセッションを関連付ける値ですが、端末や再起動をまたいで一意な識別子ではありません。
ws7の成功・失敗記録から、同じ起動区間の権限付与とプロセスを検索します。ログオン種別3はネットワーク、2は対話型です。成功や特権の割当だけで、具体的なコマンドの実行まで断定しない調べ方を学びます。表のbootは再起動履歴から補った起動区間、privilegesは4672から抽出した特権の一覧です。
1イベントXMLの名前付き項目を読む
WindowsのイベントはSystemとEventDataに分かれます。短い固定試料で、イベント種別と項目名を確認します。
from oplab import ROOT
text=(ROOT/"OP-02/event.xml").read_textイベントXMLの名前付き項目を読む()
print(text,end="")EventIDは4624、Computerはws7です。TargetLogonId、LogonType、IpAddressはそれぞれ別の名前付きデータで、画面上の行順だけに依存せず取得できます。
2XMLから検索用の列を取り出す
名前空間を指定してEventDataのData要素を集めます。
import xml.etree.ElementTree as ET
event=ET.fromstring(text)
ns={"e":"http://schemas.microsoft.com/win/2004/08/events/event"}
fields={x.attrib["Name"]:x.text for x in event.findallXMLから検索用の列を取り出す("e:EventData/e:Data",ns)}
print(fields)
print(int(fields["TargetLogonId"],16),int(fields["LogonType"]))0x71は10進数113、LogonTypeは3です。以後のauth表はこれらをlogon、kind、srcなどの調査用の短い列名へ対応させた固定資料です。
3認証の成功と失敗を抽出する
失敗と成功を同じ表で並べ、端末とアカウントを確認します。
from oplab import *
db=connect(2)
show(db, 'SELECT認証の成功と失敗を抽出する id,t,host,event,account,logon,kind,status,privileges FROM auth WHERE event IN (4624,4625) ORDER BY t')a1はパスワード不一致、a2はネットワークログオンです。後の成功が、前の失敗と同じ人物の操作だったかは記録だけでは決まりません。
4番号だけで結合した候補を見る
0x71という値だけで検索すると何が混ざるか確認します。
show(db, 'SELECT番号だけで結合した候補を見る id,host,boot,account,event FROM auth WHERE logon="0x71" ORDER BY t')ws8と再起動後のbobまで現れます。同じ数値という理由だけで一つの活動にまとめると誤った時系列になります。
5端末と起動区間を指定する
対象はws7のb1で成立したセッションです。
show(db, 'SELECT端末と起動区間を指定する id,event,account,status,privileges FROM auth WHERE host="ws7" AND boot="b1" AND logon="0x71" ORDER BY t')4624と4672が残ります。4672は新しいログオンへの特別な特権の割当であり、各特権が実際に使用された証拠ではありません。
6同じセッションのプロセスを結合する
プロセス表は4688から調査用に抽出した項目です。端末、起動区間、ログオンIDを合わせます。
show(db, 'SELECT同じセッションのプロセスを結合する a.id AS logon_event,p.id AS process_event,p.image FROM auth a JOIN proc p USING(host,boot,logon) WHERE a.event=4624')p1のwhoami.exeがa2のセッションに結び付きます。プロセス生成の記録は実行開始の証拠ですが、成功した処理の内容は別途確認します。
7特権記録がない成功も残す
LEFT JOINで、特権の対応がないログオンを検索結果から落とさずに表示します。
show(db, 'SELECT特権記録がない成功も残す a.id,a.host,a.boot,b.id AS privilege_event FROM auth a LEFT JOIN auth b ON a.host=b.host AND a.boot=b.boot AND a.logon=b.logon AND b.event=4672 WHERE a.event=4624')a4とa5のNULLは、この試料に対応する4672がないことを表します。一般に権限が一切ないという意味ではありません。
8主張に対応する証拠を絞る
ws7/b1に絞った根拠をまとめて読み返します。
show(db, 'SELECT主張に対応する証拠を絞る id,event,account,status,privileges FROM auth WHERE host="ws7" AND boot="b1" ORDER BY t')ログオン成功はa2、特権割当はa3、プロセス生成はp1です。異なる意味を持つ証拠を、一つの「侵害成功」という結論へ省略しないようにします。
まとめ
ログオン成功はa2、特権割当はa3、プロセス生成はp1です。異なる意味を持つ証拠を、一つの「侵害成功」という結論へ省略しないようにします。
表示された証拠IDと検索条件を残すと、調査結果を元の記録までたどれます。
操作履歴と解説を振り返れます。
Python 3.15 / SQLite・固定の調査記録
from oplab import ROOT
text=(ROOT/"OP-02/event.xml").read_text()
print(text,end="")<Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
<System>
<Provider Name="Microsoft-Windows-Security-Auditing"/>
<EventID>4624</EventID>
<TimeCreated SystemTime="2026-01-07T01:00:12Z"/>
<EventRecordID>102</EventRecordID>
<Channel>Security</Channel><Computer>ws7</Computer>
</System>
<EventData>
<Data Name="TargetDomainName">CORP</Data>
<Data Name="TargetUserName">alice</Data>
<Data Name="TargetLogonId">0x71</Data>
<Data Name="LogonType">3</Data>
<Data Name="AuthenticationPackageName">Kerberos</Data>
<Data Name="IpAddress">192.0.2.9</Data>
</EventData>
</Event>import xml.etree.ElementTree as ET
event=ET.fromstring(text)
ns={"e":"http://schemas.microsoft.com/win/2004/08/events/event"}
fields={x.attrib["Name"]:x.text for x in event.findall("e:EventData/e:Data",ns)}
print(fields)
print(int(fields["TargetLogonId"],16),int(fields["LogonType"])){'TargetDomainName': 'CORP', 'TargetUserName': 'alice', 'TargetLogonId': '0x71', 'LogonType': '3', 'AuthenticationPackageName': 'Kerberos', 'IpAddress': '192.0.2.9'}
113 3from oplab import *
db=connect(2)
show(db, 'SELECT id,t,host,event,account,logon,kind,status,privileges FROM auth WHERE event IN (4624,4625) ORDER BY t')id | t | host | event | account | logon | kind | status | privileges a1 | 10 | ws7 | 4625 | CORP/alice | NULL | 3 | 0xC000006A | NULL a2 | 12 | ws7 | 4624 | CORP/alice | 0x71 | 3 | 0x0 | NULL a4 | 14 | ws8 | 4624 | LOCAL/alice | 0x71 | 2 | 0x0 | NULL a5 | 20 | ws7 | 4624 | CORP/bob | 0x71 | 2 | 0x0 | NULL
show(db, 'SELECT id,host,boot,account,event FROM auth WHERE logon="0x71" ORDER BY t')id | host | boot | account | event a2 | ws7 | b1 | CORP/alice | 4624 a3 | ws7 | b1 | CORP/alice | 4672 a4 | ws8 | b1 | LOCAL/alice | 4624 a5 | ws7 | b2 | CORP/bob | 4624
show(db, 'SELECT id,event,account,status,privileges FROM auth WHERE host="ws7" AND boot="b1" AND logon="0x71" ORDER BY t')id | event | account | status | privileges a2 | 4624 | CORP/alice | 0x0 | NULL a3 | 4672 | CORP/alice | NULL | SeDebugPrivilege
show(db, 'SELECT a.id AS logon_event,p.id AS process_event,p.image FROM auth a JOIN proc p USING(host,boot,logon) WHERE a.event=4624')logon_event | process_event | image a2 | p1 | C:/Windows/System32/whoami.exe
show(db, 'SELECT a.id,a.host,a.boot,b.id AS privilege_event FROM auth a LEFT JOIN auth b ON a.host=b.host AND a.boot=b.boot AND a.logon=b.logon AND b.event=4672 WHERE a.event=4624')id | host | boot | privilege_event a2 | ws7 | b1 | a3 a4 | ws8 | b1 | NULL a5 | ws7 | b2 | NULL
show(db, 'SELECT id,event,account,status,privileges FROM auth WHERE host="ws7" AND boot="b1" ORDER BY t')id | event | account | status | privileges a1 | 4625 | CORP/alice | 0xC000006A | NULL a2 | 4624 | CORP/alice | 0x0 | NULL a3 | 4672 | CORP/alice | NULL | SeDebugPrivilege
演習完了