Web安全之命令执行(Command Injection)
当应用需要调用一些外部程序去处理内容的情况下,就会用到一些执行系统命令的函数。如 PHP 中的 system、exec、shell_exec 等,当用户可以控制命令执行函数中的参数时,将可以注入恶意系统命令到正常命令中,造成命令执行攻击。本文只介绍 PHP 有关的命令执行漏洞。
直接执行代码
PHP 中有不少可以直接执行代码的函数,如:
1 | eval(); 将参数字符串作为php程序代码执行 |
代码示例:
1 | system("ls -al"); |
值得注意的是,system是命令执行函数,eval是代码执行函数
preg_replace()代码执行
自PHP5.5.0版本起 /e
修饰符被弃用
函数说明:preg_replace — 执行一个正则表达式的搜索和替换1
2mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
搜索subject中匹配pattern的部分,以replacement进行替换。
在preg_replace()函数的第一个参数中使用/e修正符,会使引擎在完成替换后,将结果字符串作为php代码使用eval方式进行评估,并将返回值作为最终参与替换的字符串。
但是要确保replacement构成一个合法的php代码字符串,并符合eval语法,否则代码会在preg_replace()这一行出现语法解析错误。
如果没有/e
修饰符,可以尝试 %00 截断。 —wiki
eg-1:
1 | <?php |
payload: ?h = hpinfo()
eg-2:1
2
3
4
5<?php
$var = "<tag>phpinfo()</tag>";
preg_replace("/<tag>(.*?)<\/tag>/e", "addslashes(\\1)", $var);
?>
// 其中的 \\1 实际上就是 \1,指的是第一个子匹配项,还可以使用 $1 来匹配
eg-3:
1 | <?php |
payload:?h=[php]{${phpinfo()}}[/php]
此时提交 ?h=[php]phpinfo()[/php]
,phpinfo()不会被执行,因为经过正则匹配后, replacement 参数变为’test(“phpinfo”)’,此时phpinfo仅仅是被当做一个字符串参数。
提交 ?h=[php]{${phpinfo()}}[/php]
phpinfo()会被执行,因为在php中,双引号里面如果包含有变量,php会将其解析,而单引号中的变量不会被处理。
在这里我们通过 {${}} 构造出了一个特殊的变量'test("{${phpinfo()}}")'
,达到让函数被执行的效果 (${phpinfo()}会被解释执行)。
关于{${}}这种形式涉及到了php Complex (curly) syntax.
测试代码:1
echo "{${phpinfo()}}";
在本例中将’test(“\1”)’修改为”test(‘\1’)”,这样’${phpinfo()}’就会被当做一个普通的字符串处理。
动态函数执行
用户自定义的函数可以导致代码执行。
eg-1:1
2
3
4
5<?php
$dyn_func = $_GET["dyn_func"];
$argument = $_GET["argument"];
$dyn_func($argument);
?>
payload:?dyn_func=phpinfo & argument=1
payload:?dyn_func=system & argument=ipconfig
eg-2:1
2
3
4
5
6
7
8
9
10
11
12
13<?php
function A() {
echo "aaa";
}
function B() {
echo "bbb";
}
if (isset($_GET["func"])) {
$myfunc = $_GET["func"];
echo $myfunc();
}
?>
payload:
?func = A
—> 输出:aaa
?func = phpinfo
—> 漏洞产生
反引号命令执行
1 | <?php |
Curly Syntax
前文提到的 {${}} 属于这一部分
PHP的 Complex Syntax (Curly Syntax) 也能导致代码执行,它将执行花括号间的代码,并将结果替换回去。
1 | <?php |
1 | <?php |
回调函数
很多函数都可以执行回调函数,当回调函数用户可控时,将导致代码执行。
1 | <?php |
payload:?callback=phpinfo
反序列化
如果 unserialize() 在执行时定义了 __destruct()
或 __wakeup()
函数,则有可能导致代码执行。
1 | <?php |
payload:?saved_code=O:7:"Example":1:{s:3:"var";s:10:"phpinfo();";}
防范方法
尽量不使用执行命令的函数或在disable_functions中禁用
在进入执行命令的函数/方法之前,对参数进行过滤,对敏感字符进行转义
对于可控点是程序参数的情况下,使用escapeshellcmd函数进行过滤,对于可控点是程序参数值的情况下,使用escapeshellarg函数进行过滤
参数的值尽量使用引号包裹,并在拼接前调用addslashes进行转义
针对由第三方组件引发的漏洞,要及时打补丁,修改安装时的默认配置。
- 使用safe_mode_exec_dir指定可执行文件的路径,把可能使用的命令提前放入此路径内,限制外部程序的执行(推荐不执行任何程序指向网页目录)
safe_mode = On
safe_mode_exec_dir = /usr/local/php/bin/
附: &、||、| 命令连接符
windows:
| 直接执行后面的语句 ping 127.0.0.1 | whoami
|| 前面为假再执行后面 ping 2 || whoami
& 前面可真可假(后面一定会执行) ping 127.0.0.1 & whoami
&& 前面只能为真(前面为假导致出错,后面不执行) ping 127.0.0.1 && whoami
linux:
; 前面的执行完执行后面的 ping 127.0.0.1 ; whoami
| 管道符,显示后面的执行结果 ping 127.0.0.1 | whoami
|| 前面为假再执行后面 ping 1 || whoami
& 前面可真可假(后面一定会执行) ping 127.0.0.1 & whoami
&& 前面只能为真(前面为假导致出错,后面不执行) ping 127.0.0.1 && whoami