Nmap は、あなたが要求したと思っているパケットを常に送るわけではない。raw ソケットが使えないとき、Nmap は既定の SYN スキャンを TCP connect スキャンに置き換えるため、通常ユーザーとしての実行と sudo 下での実行は、同じポートについて別の問いに答えることがある。結果は権限の問題に見えるが、たいていは手法の変更である。手法を明示することだけが、二つの実行をそもそも比較する唯一の方法だ。

二つのプローブ、一つのポート、異なるコード経路

SYN スキャンは TCP ヘッダーを自ら組み立て、raw ソケットから送り、カーネルの TCP スタックが完了した接続を記録する前にネットワークから応答を読む。connect スキャンは OS に通常のソケットを開かせ、connect() が返すものを報告する。どちらもポートを開いていると判定できるが、半開の試行を残すのは一方だけで、送信元プロセスに結び付いたソケット層のポリシーに従うのも一方だけだ。

raw 経路には CAP_NET_RAW が必要で、その能力がないとき既定のスキャンは静かに劣化する。Nmap 自身の スキャン手法のリファレンス はこの置き換えを記録している:connect スキャンは特別な権限をまったく必要としないため、代替なのだ。置き換えはほとんどの実行で警告なしに起こり、それがこの挙動を誤読しやすくしている。代替があると知っていれば、「root では開いていてユーザーでは閉じている」は権限の不具合には聞こえず、二つの異なる測定に聞こえ始める。

スキャンで sudo が実際に変えるもの

権限を上げると raw ソケットの経路が戻り、それは判定以上のものを変える。送信元ポートの選択、パケットのタイミング、再送の挙動、フィルターされた応答の分類の仕方は、いずれも異なるコードから来る。SYN のレートを制限する中間装置は、宛先ホストが同一でも二つの実行を不平等に扱う。

宛先側のログも異なる。完了したハンドシェイクはサービスにもソケット会計にも見えるが、リセットされた半開のプローブは多くの場合見えない。ログも読むホストに対して実行を比較するなら、見える差異の一部は観測そのものだ。

両方の実行で手法を一定に保つ

両方の呼び出しで同じ手法を強制すれば、比較は意味を持ち始める。-sT は connect スキャンを明示的に要求し、-Pn はホスト探索を、-n は名前解決を除き、単一の -p はポート一覧の違いを除く。

nmap -sT -Pn -n -p 8443 198.51.100.77
sudo nmap -sT -Pn -n -p 8443 198.51.100.77

アドレスとポートは、テストを許可されたホストに置き換える(上のドキュメント用レンジは例であり、対象ではない)。二つの実行が一致すれば、元の差異はスキャン手法から来ていた。それでも一致しなければ、何か別のものが通信を分類しており、次の節が確認すべき場所だ。

アカウント文脈は権限フラグだけではない

VPN の配備では、パケットを UID で、cgroup で、あるいはソケットを開いたプロセスに結び付いたルールで分類できる。そうなると root の通信とユーザーの通信は異なるルーティングテーブルから出るか、異なるルール集合に当たり、スキャン手法の規律では到底そろわない。

特権ではなくポリシーを調べる。Linux では nftables が meta skuid で、iptables が所有者マッチでこれを表現し、どちらも一つの UID をトンネルに流す一方で、もう一方は物理的な既定経路を使い続けられる。

sudo nft list ruleset | grep -n skuid
sudo iptables-save | grep -n uid-owner
ip rule show
ip route get 198.51.100.77

結果を比べる前にプローブを確認する

結論を出す前に、どのプローブが動いたかを証明する。Nmap は自身の出力で手法を名指しし、--reason は各状態の根拠を加え、これが、ハンドシェイクが実際に完了したかどうかを教えてくれる部分だ。

nmap --reason -sT -Pn -n -p 8443 198.51.100.77
sudo nmap --reason -sS -Pn -n -p 8443 198.51.100.77
getcap "$(command -v nmap)"
capsh --print | grep -i net_raw

同じ状態を別の理由で報告する二つの実行は矛盾ではなく、同じ理由で異なる状態を報告する二つの実行は、スキャンではなくルーティングかポリシーを示す。宛先、ポート、名前空間、タイミングを同一に保ち、一度に一つの変数だけを変える。サービスを root で実行することはポリシーの不一致の修理ではなく、意図したポリシーをサービスの所有者を通じて表現することが修理だ。

関連記事