
New OWASP category today: Command Injection. One clean solve, one long, genuinely exhausting debugging session that ended without a final clean win — and a good closing tangent connecting all of this back to why I'm actually doing it.
The Core Idea
Same root pattern as every SQL injection lab, just aimed at a different target. Instead of user input reaching a database query, it reaches an operating system command:
exec(`check_stock.sh ${productId} ${storeId}`);
If storeId isn't validated, a shell metacharacter like ; lets an attacker chain on a second, completely unrelated command:
storeId=1;whoami
becomes
check_stock.sh 1 1;whoami
Both commands run — the shell doesn't know or care that only the first one was intended.
Lab 1: Simple Case — Solved
Found the vulnerable "check stock" form on a product page. Since it was a POST request (not GET), the parameters weren't visible in the URL at all — a good reminder that GET puts data in the URL, POST puts it in the request body, and you have to go find the actual <form> HTML or the Network tab's request payload to see what's really being sent.
Built the request with curl, matching the form's method, action, and field names exactly:
curl -X POST "https://[lab-url]/product/stock" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "productId=1&storeId=1;whoami"
The response returned the server's actual username — direct, visible proof of command execution. Straightforward once the request was built correctly.
Lab 2: Blind OS Command Injection — The Real Session
This one had no visible output at all — same "blind" concept as blind SQLi, just applied to shell commands. The only available signal: response timing, using sleep 10 as the injected payload instead of a boolean condition.
Where most of the time actually went
This lab had more moving pieces than the first: a CSRF token, a session cookie, five separate form fields, and no visible clue about which specific field was vulnerable. Several real mistakes stacked up in sequence:
Malformed request body — used
key:value key:valueformatting instead of properkey=value&key=valueform-urlencoded syntax. Basic but easy to slip into when typing quickly.Malformed Cookie header — wrote
Cookie session=...instead ofCookie: session=..., missing the colon.Guessing the vulnerable field — tried
messagefirst, thenemail, without a systematic process, wasting several roundsUnencoded space in the payload —
sleep 10contains a literal space, which isn't strictly valid insidex-www-form-urlencodeddata without encoding it as%20. This was likely the actual reason a payload that should have worked kept returning "Could not save" instead of triggering a delay.
What actually got confirmed
After systematically isolating variables — confirming the CSRF/session pairing worked with a clean baseline request first, then testing one field at a time with the same simple payload — a timing delay genuinely appeared when injecting into the email field. That's real, valid confirmation the vulnerability exists and where it lives, even though the final clean "lab solved" run got tripped up again by the space-encoding issue before I stopped for the day.
Being honest about this one: I checked the official solution partway through, after roughly an hour of debugging. Worth saying plainly rather than glossing over — sometimes the useful move is confirming your own reasoning against a reference rather than grinding indefinitely, especially after real, substantial debugging work had already been done. The vulnerability was genuinely found through my own testing; the exact clean payload formatting is what needed a check.
curl vs. Burp, Revisited
A lot of today's friction was curl-specific: manually retyping full commands for every small edit, hand-encoding special characters, easy-to-miss syntax slips (a missing colon, wrong separator). Burp Suite's Repeater tool solves exactly this class of problem — it captures a real request your browser already sent correctly, then lets you edit one field at a time visually and resend, without re-deriving cookies, CSRF tokens, or content-type headers by hand each time. Burp was laggy on this hardware a couple weeks ago, but given today's specific pain points, it's worth revisiting despite the performance cost — the tradeoff might be worth it for this exact kind of iterative field-testing workflow.
Testing performed exclusively against PortSwigger's own intentionally vulnerable lab environments as part of self-directed learning on the Web Security Academy.
About the author
- just a simple guy
Comments
Join the discussionNo comments yet. Be the first to comment.




