**TCP/IP 劫持(hijacking)**是一種用偽造封包接管受害者與主機之間連線的巧妙技術。
為什麼劫持特別有用#
這技術在受害者使用一次性密碼(one-time password)連線時尤其有用。一次性密碼只能認證一次,所以嗅探認證對攻擊者毫無用處——但劫持繞過了認證本身,直接接管已建立的連線。
執行劫持,攻擊者必須與受害者在同一網路上:
- 嗅探本地網段,從標頭取得所有開放 TCP 連線的細節。
- 每個 TCP 封包標頭都含序號(隨每個送出封包遞增,確保順序)。嗅探時,攻擊者能取得受害者(系統 A)與主機(系統 B)之間連線的序號。
- 攻擊者從受害者的 IP 位址送出偽造封包給主機,用嗅到的序號提供正確的確認號。
主機收到帶正確確認號的偽造封包,沒有理由相信它不是來自受害機器。

圖 0x400-10:攻擊者用嗅到的序號偽造封包,劫持受害者與主機之間的 TCP 連線
RST 劫持#
最簡單的一種形式:注入一個看似合法的重置(RST)封包。
若來源被偽造、確認號正確,接收端就會相信來源真的送了重置封包,連線隨即被重置。
rst_hijack.c 用 libpcap 嗅探、用 libnet 注入 RST,只需看送往目標 IP 的已建立 TCP 連線。
Berkeley Packet Filter(BPF)#
許多用 libpcap 的程式不需看每個封包,所以 libpcap 提供告訴核心只送符合過濾條件封包的方法——這就是 BPF,很像一支程式。
過濾規則需先編譯才送進核心。以「已建立連線」為例,這類連線都設了 ACK 旗標:
TCP 旗標位於 TCP 標頭的第 13 個八位元組,由左到右為 URG、ACK、PSH、RST、SYN、FIN。ACK 開啟時第 13 個八位元組是二進位
00010000(十進位 16)。用位元 AND 忽略其他位元:tcp[13] & 16 == 16就能匹配 ACK 開啟的封包。
以具名值改寫更易讀:
sudo tcpdump -nl "tcp[tcpflags] & tcp-ack != 0 and dst host 192.168.42.88"
rst_hijack.c 用相同規則過濾,收到封包後用其標頭資訊偽造 RST 封包:
// caught_packet 回呼中,偽造 RST:來源與目的對調,用嗅到的 ack 當序號
libnet_build_ip(..., *((u_long *)&(IPhdr->ip_dst)), // 假裝我們是 dst
*((u_long *)&(IPhdr->ip_src)), ...); // 送回給 src
libnet_build_tcp(htons(TCPhdr->th_dport), htons(TCPhdr->th_sport),
htonl(TCPhdr->th_ack), // 用先前的 ack 當序號
..., TH_RST, ...); // 只設 RST 旗標
持續劫持#
偽造封包不必是 RST——當偽造封包帶資料時更有趣:
- 主機收到偽造封包、遞增序號、回應受害者的 IP。
- 受害機器不知道偽造封包的存在,所以主機的回應對它而言序號不對,於是忽略。
- 受害者忽略了主機的回應,於是受害者的序號計數也偏掉——它之後送給主機的任何封包序號也會錯,主機也忽略。
此時連線兩端合法方的序號都不對,進入**去同步(desynchronized)**狀態。而攻擊者送出了引發這一切的第一個偽造封包,所以它能持續追蹤序號、繼續從受害者 IP 偽造封包給主機——讓攻擊者繼續與主機通訊,而受害者的連線卡死。