使用 Bash /dev/tcp 在不使用 Curl 的情況下發送 HTTP 請求

使用 Bash /dev/tcp 進行網路連線

Bash 可以直接開啟 TCP socket,讓你在不依賴 curlwget 等外部工具的情況下,透過網路傳送與接收資料。這在精簡的 Docker 容器或最小化環境中特別有用,因為這類環境往往不允許安裝新套件。

要執行一個基本的 HTTP GET 請求,請使用以下指令序列:

exec 3<>/dev/tcp/service/8642
printf 'GET /health HTTP/1.1\r\nHost: service\r\nConnection: close\r\n\r\n' >&3
cat <&3

在此範例中,service 為主機名稱,8642 為埠號。exec 3<> 指令會開啟一個指向指定主機與埠的 socket,並將其指派給檔案描述符 3。printf 送出原始的 HTTP 請求,cat <&3 則會讀取 socket 的回應,直到連線關閉為止。

處理標頭與驗證

若要加入額外的標頭(例如 Authorization token),請在結束 HTTP 請求的空白行之前插入該標頭行:

exec 3<>/dev/tcp/service/8642
printf 'GET /v1/models HTTP/1.1\r\nHost: service\r\nAuthorization: Bearer %s\r\nConnection: close\r\n\r\n' "$API_KEY" >&3
cat <&3

技術實作與限制

/dev/tcp 的本質

表面上看起來,/dev/tcp 不是磁碟上的真實裝置檔案。它是 Bash 內部處理的重定向機制。當 Bash 遇到符合 /dev/tcp/host/port 的路徑時,會嘗試使用 connect(2) 系統呼叫開啟相對應的 TCP socket。之所以採用此設計,是因為沒有任何標準的 Unix 系統會有 /dev/tcp/dev/udp 階層,避免與實際檔案產生衝突。

主要限制

雖然此方法對除錯相當有力,但相較於完整的 HTTP 客戶端仍有顯著限制:

  • 不支援 TLS/SSL: /dev/tcp 只開啟原始 socket,無法處理 HTTPS。若需加密流量,必須使用 openssl s_client 等工具。
  • 不進行 HTTP 解析: Bash 不會解析 HTTP 協定,亦不會處理重新導向、分塊傳輸編碼、壓縮或重試。它僅傳送與接收原始位元組。
  • 連線管理: Connection: close 標頭相當重要。若未加上此標頭,伺服器可能會保持連線開啟(HTTP/1.1 的預設),導致 cat 無限等待更多資料而卡住。
  • Shell 相容性: 這是 Bash 專屬功能,並非 POSIX 標準。它在 dash(Debian 預設的 /bin/sh)或 zsh 中無法使用。使用此功能的腳本必須明確以 bash 執行。
  • 編譯時需求: 必須在編譯 Bash 時啟用 --enable-net-redirections 旗標。雖然大多數主流發行版都有此功能,但某些極簡或較舊的建置(包括部分歷史版的 Debian)可能未啟用。

實務應用與替代方案

除錯與安全測試

此技巧常在環境高度受限的情況下使用:

  • 極簡容器: 在已移除所有非必要二進位檔的映像中檢查健康端點。
  • 滲透測試: 在 CTF 或安全稽核時,當 curlnc 不在系統中時,用來建立初始 shell 或與本機 Web 伺服器互動。
  • 繞過限制: 當安全政策禁止安裝網路工具時,仍能與服務互動。

替代方法

根據可用工具的不同,其他方案可能更可靠:

  • Netcat (nc): 更強大的原始 socket 互動工具。
  • Perl/Python: 使用 Perl 的 IO::Socket 或 Python 的 socket 模組,以程式方式處理網路請求。
  • nsenter: 若主機上已安裝 curl,可使用 nsenter 進入容器的 network namespace,從主機上下達 curl

"這就是我們在 2026 年都應該得到的內容,而這也是我在面試時會要求解釋 HTTP 協定中 cookie 如何表示的原因。" — @dennis16384

Sources