Making HTTP Requests Using Bash /dev/tcp
Bash includes a built-in capability to open TCP sockets using a virtual path, /dev/tcp/host/port. This allows developers and system administrators to perform basic network connectivity tests and send raw HTTP requests without needing external utilities like curl, wget, or netcat.
How to Perform an HTTP Request in Bash
To send a basic HTTP GET request using Bash, you can use file descriptor redirection to open a socket and write the HTTP protocol strings directly to it.
Basic GET Request
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
In this example, exec 3<> opens a bidirectional socket to the host service on port 8642 and assigns it to file descriptor 3. The printf command sends a properly formatted HTTP/1.1 request, and cat <&3 reads the response from the server.
Adding Custom Headers
To include additional headers, such as an authorization token, insert the header line before the final blank line that terminates the HTTP request:
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
Technical Constraints and Requirements
While powerful for debugging, the /dev/tcp feature is a low-level TCP client, not a full HTTP client. It has several critical limitations:
- No TLS/SSL Support:
/dev/tcpopens raw sockets. It cannot handle HTTPS. For encrypted traffic, tools likeopenssl s_clientare required. - Manual HTTP Handling: Bash does not parse HTTP. It cannot handle redirects, chunked transfer encoding, compression (gzip), or retries. The user is responsible for constructing the valid HTTP request string.
- Connection Management: In HTTP/1.1, connections remain open by default. The
Connection: closeheader is necessary to tell the server to close the connection, allowingcatto reach the end-of-file (EOF) and exit. Alternatively, using HTTP/1.0 avoids the need for this header. - Shell Compatibility: This is a Bash-specific feature and is not part of the POSIX standard. It will not work in
dash(the default/bin/shon Debian/Ubuntu) orzsh(thoughzshhas its own separate network modules). - Compile-time Option: The feature must be enabled during Bash compilation with the
--enable-net-redirectionsflag. While common in most mainstream distributions, some minimal or older versions of Debian had this disabled.
Use Cases and Alternatives
Common Applications
This technique is frequently used in constrained environments where installing new packages is impossible or forbidden:
- Minimal Docker Containers: Checking connectivity between services in stripped-down images that lack
curlorwget. - Embedded Systems: Implementing lightweight "heartbeat" services on devices with extremely limited flash memory.
- Security Auditing: Performing port knocking or interacting with local web servers during penetration tests or CTF challenges when only a basic shell is available.
Alternative Methods
Depending on the environment, other tools may be more appropriate:
nc(Netcat): A more robust tool for raw TCP/UDP interactions.nsenter: Ifcurlis installed on the host machine,nsentercan be used to enter the network namespace of a container and run the host'scurlagainst the container's network.- Perl/Python: Using
IO::Socketin Perl or thesocketmodule in Python to programmatically handle TCP connections.
Community Insights
Industry practitioners highlight that while /dev/tcp is a useful "hack," it exposes the inherent attack surface of feature-rich GNU utilities.
"This is an old post-compromise trick used when an attacker needs to download a payload or make a network connection and curl, wget and nc are all not available."
Others note that this functionality is a spiritual successor to the Plan 9 operating system's philosophy, where network resources are represented as files in a /net directory.