一覧OP-05-1 · 通信ログを用いた事実確認

DNS・NAT・接続ログによる端末の特定

同じ外部IPとポートが時間をずらして再利用された例を調べます。DNS応答、接続の状態、HTTP応答を順に確認し、名前解決、許可、接続、応答という異なる事実を説明します。

説明

外部側で見えた送信元IPがNAT装置のアドレスなら、それだけでは内部端末を特定できません。ポート番号、宛先、通信した時刻をNAT対応表へ照合する必要があります。

同じ外部IPとポートが時間をずらして再利用された例を調べます。DNS応答、接続の状態、HTTP応答を順に確認し、名前解決、許可、接続、応答という異なる事実を説明します。

1名前解決をした端末を探す

同じ名前を引いた端末が複数あるか確認します。

from oplab import * db=connect(5) show(db, 'SELECT名前解決をした端末を探す * FROM dns ORDER BY id')

10.0.0.7と10.0.0.8が同じIPを得ています。DNS記録だけではどちらが実際に接続したかは決まりません。

2外部側の接続を確認する

送信元ポートと接続状態を合わせて読みます。

show(db, 'SELECT外部側の接続を確認する * FROM flow ORDER BY t')

f1とf2は同じアドレスとポートですが、時刻と接続状態が違います。allowはポリシーの判定であり、通信相手からの応答を保証しません。

3時刻なしのNAT照合を試す

まずポートと宛先だけで候補を検索します。

show(db, 'SELECT時刻なしのNAT照合を試す f.id,n.id,n.inside FROM flow f JOIN nat n ON f.src=n.public AND f.sport=n.pport AND f.dst=n.dst AND f.dport=n.dport ORDER BY f.id,n.id')

各接続に2端末が対応してしまいます。NATの割当が再利用されたため、時間条件が必要です。

4有効時間を含めて端末を特定する

対応表のstart以上end未満という時間条件を加えます。

show(db, 'SELECT有効時間を含めて端末を特定する f.id,n.inside,n.sport,f.state FROM flow f JOIN nat n ON f.src=n.public AND f.sport=n.pport AND f.dst=n.dst AND f.dport=n.dport AND f.t>=n.start AND f.t<n.end ORDER BY f.id')

f1は10.0.0.7、f2は10.0.0.8に対応しました。境界の時刻20は、片方の割当てだけに含めます。

5応答を受け取った根拠を見る

接続ログよりアプリケーションに近いHTTP記録を照合します。

show(db, 'SELECT応答を受け取った根拠を見る d.client,d.name,h.id,h.status,h.bytes FROM dns d JOIN http h ON d.client=h.client AND d.name=h.host AND h.t BETWEEN d.t AND d.t+5')

10.0.0.7にはHTTP 200と900バイトの応答記録があります。HTTPS本文の内容をこの表だけから判断することはできません。

6許可されたが応答のない接続を探す

許可判定と接続成立を分けて抽出します。

show(db, 'SELECT許可されたが応答のない接続を探す id,action,state,sent,received FROM flow WHERE action="allow" AND received=0')

f2はSYN送信のみです。「通信が許可された」と「相手への接続に成功した」を同じ意味で使わないようにします。

まとめ

f2はSYN送信のみです。「通信が許可された」と「相手への接続に成功した」を同じ意味で使わないようにします。

表示された証拠IDと検索条件を残すと、調査結果を元の記録までたどれます。

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

Python 3.15 / SQLite・固定の調査記録

1
from oplab import * db=connect(5) show(db, 'SELECT * FROM dns ORDER BY id')
id | t | client | name | answer
d1 | 10 | 10.0.0.7 | files.example.test | 203.0.113.7
d2 | 10 | 10.0.0.8 | files.example.test | 203.0.113.7
2
show(db, 'SELECT * FROM flow ORDER BY t')
id | t | src | sport | dst | dport | action | state | sent | received
f1 | 12 | 192.0.2.1 | 61000 | 203.0.113.7 | 443 | allow | established | 400 | 1200
f2 | 22 | 192.0.2.1 | 61000 | 203.0.113.7 | 443 | allow | syn_only | 60 | 0
3
show(db, 'SELECT f.id,n.id,n.inside FROM flow f JOIN nat n ON f.src=n.public AND f.sport=n.pport AND f.dst=n.dst AND f.dport=n.dport ORDER BY f.id,n.id')
id | id | inside
f1 | n1 | 10.0.0.7
f1 | n2 | 10.0.0.8
f2 | n1 | 10.0.0.7
f2 | n2 | 10.0.0.8
4
show(db, 'SELECT f.id,n.inside,n.sport,f.state FROM flow f JOIN nat n ON f.src=n.public AND f.sport=n.pport AND f.dst=n.dst AND f.dport=n.dport AND f.t>=n.start AND f.t<n.end ORDER BY f.id')
id | inside | sport | state
f1 | 10.0.0.7 | 51000 | established
f2 | 10.0.0.8 | 52000 | syn_only
5
show(db, 'SELECT d.client,d.name,h.id,h.status,h.bytes FROM dns d JOIN http h ON d.client=h.client AND d.name=h.host AND h.t BETWEEN d.t AND d.t+5')
client | name | id | status | bytes
10.0.0.7 | files.example.test | h1 | 200 | 900
6
show(db, 'SELECT id,action,state,sent,received FROM flow WHERE action="allow" AND received=0')
id | action | state | sent | received
f2 | allow | syn_only | 60 | 0

演習完了