使用 Bash /dev/tcp 在不使用 Curl 的情况下发起 HTTP 请求

使用 Bash /dev/tcp 进行网络连接

Bash 可以直接打开 TCP 套接字,使您能够在不依赖 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<> 命令打开到指定主机和端口的套接字,并将其分配给文件描述符 3。printf 命令发送原始 HTTP 请求,cat <&3 则读取套接字的响应,直到连接关闭为止。

处理头部和身份验证

若要添加额外的头部(例如 Authorization 令牌),请在结束 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 套接字。之所以采用这种设计,是因为没有标准的 Unix 系统拥有 /dev/tcp/dev/udp 层级,避免与实际文件产生冲突。

关键限制

虽然在调试时非常强大,但与完整的 HTTP 客户端相比,此方法存在显著局限:

  • 不支持 TLS/SSL:/dev/tcp 打开的是原始套接字,无法处理 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):**更强大的原始套接字交互工具。
  • **Perl/Python:**使用 Perl 的 IO::Socket 或 Python 的 socket 模块以编程方式处理网络请求。
  • **nsenter:**如果宿主机上已安装 curl,可以使用 nsenter 进入容器的网络命名空间,从宿主机上下文执行 curl

"这就是我们在 2026 年都应该得到的内容,这也是我在面试时仍会让候选人解释 cookie 在 HTTP 协议中是如何表示的原因。" — @dennis16384

Sources