使用 Bash /dev/tcp 在不使用 Curl 的情況下發送 HTTP 請求
使用 Bash /dev/tcp 進行網路連線
Bash 可以直接開啟 TCP socket,讓你在不依賴 curl 或 wget 等外部工具的情況下,透過網路傳送與接收資料。這在精簡的 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 或安全稽核時,當
curl與nc不在系統中時,用來建立初始 shell 或與本機 Web 伺服器互動。 - 繞過限制: 當安全政策禁止安裝網路工具時,仍能與服務互動。
替代方法
根據可用工具的不同,其他方案可能更可靠:
- Netcat (nc): 更強大的原始 socket 互動工具。
- Perl/Python: 使用 Perl 的
IO::Socket或 Python 的socket模組,以程式方式處理網路請求。 - nsenter: 若主機上已安裝
curl,可使用nsenter進入容器的 network namespace,從主機上下達curl。
"這就是我們在 2026 年都應該得到的內容,而這也是我在面試時會要求解釋 HTTP 協定中 cookie 如何表示的原因。" — @dennis16384