1、漏洞原理
参数用户可控,程序将用户可控的恶意参数通过php可执行命令的函数中运行导致。
2、示例代码
<?php
echo'rec-test';
$command = 'ping -c 1 '.$_GET['ip'];
system($command); //system函数特性 执行结果会自动打印
?>
通过示例代码可知通过system函数执行ping命令,用户可通过控制参数ip传入域名或者是ip进行测试。
3、漏洞测试
那么攻击者可通过这个功能进行其他危险测试;
由于ip参数没有任何过滤限制;在审计时遇到输入可控时排查是否存在过滤是否能绕过;要检查是否存在escapeshellarg escapeshellcmd 函数转义 或者是其他的处理方法(如 强制类型转换 替换字符 等)
4、常见绕过手段
符号 | 描述 | 示例 |
<和> | 输入输出重定向 | echo abc >1.txt |
;分号 | 按照从左到右顺序执行命令 | id;whoami;ls |
| 管道符 | 将左侧命令的输出作为右侧命令的输入 | ps -aux|grep root |
&& | 按照从左到右顺序执行命令 只有执行成功才执行后面的语句 | |
|| | 按照从左到右顺序执行命令 只有执行失败才执行后面的语句 | 错误ip || whoami |
5、常见可执行命令的函数
函数/语法 | 描述 | 例子 |
system | 执行命令并输出结果 | system('id'); |
exec | 执行命令 只可获取最后一行结果 | exec('id',$a); print_r($a); |
passthru | 同 system | passthru('id'); |
shell_exec ` (反引号) | 执行命令并返回结果 | $a=shell_exec('id');print_r($a); $a=`id`;print_r($a); |
popen | 执行命令并建立管道 返回一个指针 使用fread等函数操作指针进行读写 | $a=popen("id", "r"); echo fread($a, 2096); |
proc_open | 同 popen (进程控制功能更强大) | 见PHP手册 |
pcntl_exec | 执行命令 只返回是否发生错误 | pcntl_exec('id'); |
exec函数示例:
该函数无回显需使用echo或var_dump进行输出,且只返回执行后的最后一行结果。
shell_exec()函数``反引号函数,同exec一样不在示例。(均为无回显)
<?php
$sys = $_REQUEST['cmd'];
$cmder = exec($sys);
echo $cmder;
?>
system函数示例:
该函数会将输入的参数当做命令执行,有回显且返回所有内容。在实战中也是最常见的造成命令执行漏洞的函数之一。
注意:如果目标是LInux则执行Bash命令,如果是Windows则执行cmd命令。
passthru()函数同system一样有回显
<?php
$sys = $_REQUEST['cmd'];
$cmder = system($sys);
?>
popen()函数示例:
该函数通常用于打开进程文件指针,但如果传入的参数可控也可造成命令执行,且该函数无回显,通过echo不会直接返回执行的结果,而是返回的是文件指针。
proc_open()
执行一个命令,并且打开用来输入/输出的文件指针。类似popen()函数,但是proc_open()所需参
数更多,且处理数据能力更强
<?php
$sys = $_REQUEST['value'];
$cmd = popen($sys,'r');
var_dump($cmd); ;
?>
rce代码示例:
ping.html
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="utf-8" /><title>系统命令执行demo</title>
</head>
<body>
<div id="main"><div class="title">Ping功能测试</div><form action="rce.php" method="post" onsubmit="return enter()"><label><input class="text" type="text" placeholder="请输入IP地址" name="ip"/> </label><label><input class="submit" type="submit" name="submit" value="测试" /></label></form>
</div>
</body>
</html>
rce.php代码示例:
<?php
header("content-type:text/html;charset=utf-8");
if(isset($_POST['submit'])){$target = $_REQUEST['ip'];
//php_uname()判断当前操作系统是否为Windows NTif(stristr(php_uname('s'),'Windows NT')){$cmd = shell_exec('ping '.$target);echo '<pre>'.$cmd.'<pre>';//shell_exec无回显需echo输出}else{$cmd = shell_exec('ping -c 3 '.$target);echo '<pre>'.$cmd.'<pre>';//shell_exec无回显需echo输出}
}
php_uname()函数使用:
<?php
// 获取完整的系统信息
echo php_uname(); // 相当于 php_uname("a")
// 获取操作系统名称
echo php_uname("s");
// 获取主机名
echo php_uname("n");
// 获取操作系统版本号
echo php_uname("r");
// 获取操作系统详细版本信息
echo php_uname("v");
// 获取机器硬件架构
echo php_uname("m");
?>
6、总结
在PHP中还有很多函数可以造成命令执行,但在代码审计中常见的命令执行危险函数已经给大家列出,PHP代码审计中只要多留意这些函数且该函数的参数是可以被我们所控制,这是命令执行漏洞存在的关键。其实命令执行和代码执行都是一样的,命令执行可以通过调用代码执行的函数实现代码执行,代码执行也可以通过调用命令执行的函数来实现命令执行