**連接埠掃描(port scanning)**是找出哪些連接埠正在監聽並接受連線的方法。由於多數服務跑在標準、有文件記載的連接埠上,這資訊可用來判斷正在執行哪些服務。
最簡單的掃描是對目標每個可能的連接埠嘗試開 TCP 連線。這雖有效,卻吵雜且易被偵測——連線建立時服務通常會記錄 IP 位址。為避免這點,人們發明了幾種巧妙技術。
Fyodor 寫的 nmap 實作了以下所有掃描技術,已成為最受歡迎的開源連接埠掃描工具之一。
隱蔽 SYN 掃描#
SYN 掃描又稱半開掃描(half-open scan),因為它不真的開完整的 TCP 連線:
回想三向交握。SYN 掃描只送初始 SYN 封包,然後檢視回應:
- 收到 SYN/ACK → 該埠正接受連線,記錄下來,並送 RST 拆掉連線(避免不小心 DoS 服務)。
- 沒收到 → 該埠沒在監聽。
nmap 用 -sS 執行 SYN 掃描(需 root,因為它用原始網路存取而非標準 socket)。
FIN、X-mas、Null 掃描#
為對抗 SYN 掃描,出現了偵測半開連線的工具,於是又演化出更隱蔽的一組技術。它們都送一個無意義的封包到目標每個埠:
- FIN 掃描:送 FIN 封包。
- X-mas 掃描:送 FIN、URG、PUSH 都開的封包(旗標亮得像聖誕樹,故名)。
- Null 掃描:送不設任何 TCP 旗標的封包。
若埠在監聽,這些封包會被忽略;若埠關閉且實作遵循 RFC 793,就會回 RST。這個差異能判斷哪些埠接受連線、又不真的開任何連線。
但這些掃描也可能不可靠:例如微軟的 TCP 實作不按規矩送 RST,使這類掃描失效。(nmap 選項分別為
-sF、-sX、-sN。)
隱藏於誘餌之中#
另一種避偵測法是藏在多個誘餌(decoy)之間:在每個真正的掃描連線之間,偽造來自各個誘餌 IP 的連線。偽造連線的回應不需要(它們只是誤導),但誘餌位址必須用活躍主機的真實 IP,否則可能不小心把目標 SYN flood 了。(nmap 選項 -D。)
閒置掃描#
**閒置掃描(idle scanning)**利用一個閒置主機的偽造封包來掃描目標,靠觀察閒置主機的變化推斷結果。
攻擊者需要一個可用的閒置主機:沒有其他網路流量,且 TCP 實作產生可預測的 IP ID(每個封包以已知增量遞增)。可預測的 IP ID 從未被真正視為安全風險,閒置掃描正利用這個誤解。較新的作業系統(近期 Linux 核心、OpenBSD、Windows Vista)會隨機化 IP ID,但舊系統與硬體(如印表機)通常不會。
步驟:
- 先用 SYN 或非請求的 SYN/ACK 封包接觸閒置主機、觀察回應的 IP ID,重複幾次以確定每封包的遞增量。
- 攻擊者以閒置主機的 IP 送偽造 SYN 給目標某埠:
- 埠在監聽 → 目標回 SYN/ACK 給閒置主機;對閒置主機而言這是非請求的,於是它回一個 RST(IP ID +1)。
- 埠沒在監聽 → 目標不回,閒置主機也不回。
- 再次接觸閒置主機看 IP ID 遞增了多少:只遞增一個間隔 → 埠關閉;遞增兩個間隔(閒置主機送了 RST)→ 埠開放。

圖 0x400-11:閒置掃描——目標埠開放與關閉時,閒置主機 IP ID 的不同變化
若閒置主機不是真的閒置,結果會偏差;輕微流量可對每個埠送多個封包來抵消。用得當時,攻擊者能掃描任何目標而從不洩露自己的 IP 位址。(nmap 選項
-sI。)
主動式防禦:shroud#
連接埠掃描常用於攻擊前刺探系統。許多 IDS 能偵測掃描,但那時資訊已經洩露。駭客的本質是想出新點子——這裡呈現一個新開發的、在掃描發生前就先防禦的方法。
先擋掉 FIN / Null / X-mas#
這三種掃描靠 RST 封包,只要核心永不送 RST它們就一無所獲。在核心的 tcp_v4_send_reset() 函式開頭加一行 return;,重編譯後核心就不再送 RST,避免資訊洩露。
用 SYN/ACK 洪水淹沒掃描器#
SYN 掃描與全連線掃描較難防:為維持功能,開放埠必須回 SYN/ACK。
但若所有關閉的埠也都回 SYN/ACK,攻擊者能從掃描取得的有用資訊就被降到最低。直接開啟每個埠會嚴重拖累效能,所以理想上這一切應不透過 TCP 堆疊完成。
shroud.c 正是這麼做——它修改自 rst_hijack.c,用更複雜的 BPF 只過濾送往關閉埠的 SYN 封包,回呼函式對每個通過的 SYN 偽造一個看似合法的 SYN/ACK:
// BPF:目標 IP 的 SYN(非 ACK),排除既有的開放埠
"dst host <ip> and tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack = 0 and not (dst port 22 or dst port 80)"
// 回呼:偽造 SYN/ACK
libnet_build_tcp(..., htonl((TCPhdr->th_seq) + 1), // ack = SYN 的 seq + 1
TH_SYN | TH_ACK, ...);執行後,任何掃描都會看到每個埠都開放:
reader@hacking:~/booksrc $ sudo ./shroud 192.168.42.72 22 80
真正在跑的只有埠 22 的 ssh,卻藏在一片誤報的汪洋中。專注的攻擊者仍可 telnet 到每個埠檢查橫幅(banner),但這技術也能輕易擴充成偽造橫幅。