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・固定の調査記録
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
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
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
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
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
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
演習完了