小白入门系列 || PHP代码审计之WEB安全系列基础文章(四)之代码注入篇
2022-11-2 18:2:8 Author: 玄魂工作室(查看原文) 阅读量:4 收藏

嗨,朋友你好,我是闪石星曜CyberSecurity创始人Power7089。

今天为大家带来PHP代码审计基础系列文章第四篇之代码注入篇

这是【炼石计划@PHP代码审计】知识星球第二阶段的原创基础系列文章,拿出部分课程分享给零基础的朋友学习。本系列原创基础文章涵盖了PHP代码审计中常见的十余种WEB漏洞,是匹夫老师精心的创作,欢迎关注我的公众号跟着一起学习。

【炼石计划@PHP代码审计】是一个系统化从入门到提升学习PHP代码审计的成长型知识星球。这里不仅注重夯实基础,更加专注实战进阶。强烈推荐加入我们,一起来实战提升PHP代码审计。

24套PHP相关系统正在如火如荼进行中,抓紧参与呀!

进入正文

1.代码执行原理

为了代码的灵活性与简洁性,会适当调用PHP中的一些代码执行函数来完成一些系统的功能,而代码执行的函数相当于可以直接调用PHP中任意代码来执行,更重要的是代码执行可以通过调用命令执行的函数来执行系统命令,来达到控制后台甚至我们的服务器,这就是我们所说的RCE(远程代码/命令执行漏洞)的由来。

1.1 代码执行示例代码

eval.php

以最常见的一句话木马为例,我们通过代码执行函数eval()来进行演示,假设我们eval()中的参数可控,我们就可以控制我们传入的参数来造成RCE。

<?php
highlight_file(__FILE__);         //为了方便演示我们使用highlight_file()函数将源码高亮显示在前端

$arg = $_REQUEST['value'];
eval($arg);
?>

由于这里的value参数可控,我们传入PHP中的phpinfo()函数即可输出PHP配置信息。值得注意的是eval()中传入的函数必须要用分号来结尾,否则会报出致命性错误,导致执行中断。

也可以传入我们PHP中命令执行的函数来执行系统命令,这就是我们上篇所说的代码执行也可以当作命令执行的原理。

eval()函数中可以传入多个函数来执行,每个函数也必需要用分号进行分割。

2.代码执行相关函数

eval()

将传入的字符串当作PHP代码来执行,代码示例如上图。

assert()

其与eval()类似,传入的内容会被当做代码来执行,不同的是eval中传入的值可以不用分号来结尾。

示例代码:

<?php
highlight_file(__FILE__);

$arg = $_REQUEST['value'];
assert($arg);

?>

同样也可以调用命令执行函数来执行系统命令

assert()函数也可进行拆分调用,这是与eval()函数很大的不同点,有时可以用来绕过一些WAF的防御。

示例代码:

<?php
highlight_file(__FILE__);


$a='ass';
$b='ert';
$c=$a.$b;
@$c($_REQUEST['value']);

?>

preg_replace()

该函数有三个参数,用于执行一个正则表达式的搜索和替换

搜索 subject 中匹配pattern 的部分, 如果匹配成功以replacement 进行替换

语法:

 preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
$pattern 存在 /e 模式修正符,允许代码执行
/e 模式修正符,是 preg_replace()将$replacement参数值当做php代码来执行

示例代码:

<?php
highlight_file(__FILE__);

$arg = $_REQUEST['value'];
@preg_replace("/123/e",$arg,"1234567");  //在字符串1234567中匹配123并用传入的值进行替换,并且传入的值被当作代码来执行

?>

由于使用/e传入的phpinfo()会被当做代码来执行

create_function()

PHP中的匿名函数,相当于定义了一个没有名字的函数,该函数直接用变量进行调用。

第一个参数参数是函数传递的参数,第二个参数相当于函数中的函数体。

语法:

create_function ( string $args , string $code )

代码示例:

第二个参数会在内部执行eval(),在这里也就是执行后面的return语句

<?php
highlight_file(__FILE__);
$func = create_function('$a,$b', 'return $a + $b;');

echo $func(2, 3) . "\n";

?>

上面的代码可以等价于以下代码只不过匿名函数是用变量来调用的,而非匿名变量是通过函数名来调用的。

<php
highlight_file(__FILE__);
function func($a,$b){
return $a+$b;
}
echo func(2,3)
?>

显然他们的计算结果是相同的

代码示例:

create_function()中传入的参数可控时就会造成代码执行

<?php
highlight_file(__FILE__);

$a = create_function('$arg',$_REQUEST['x']);

?>

由于$a在被调用时才会触发执行匿名函数,所以我们通过传入的参数闭合该匿名函数的},就造成了代码执行,实际情况中要根据具体闭合条件进行闭合,从而使我们想要执行的代码独立出来。

//     注释当前行代码(单行注释)

/*    注释之后所有代码(多行注释)

array_map()

array_map() 函数返回用户自定义函数作用后的数组。回调函数接受的参数数目应该和传递给 array_map() 函数的数组数目一致。

语法:

array_map(function,array1,array2,array3...)

代码示例:

array_map()将调用sum_num()函数,去执行该函数,这也就是上面所说的函数回调。且后面传入的参数要与回调的函数参数数目保持一致

<?php
highlight_file(__FILE__);
function sum_num($v,$s)
{
return $v+$s;
}
$a=array(45,33);
$b=array(45,33);
var_dump(array_map("sum_num",$a,$b)); ;
?>

代码示例:

当传入的参数可控时就会造成代码执行

<?php 
highlight_file(__FILE__);
$cmd = array_map($_REQUEST['arg1'],array($_REQUEST['arg2']));
?>

当我们传入assert,array_map()函数就会回调assert()函数去执行我们后面传入的参数phpinfo(),相当于assert(phpinfo())

call_user_func()

第一个参数作为回调函数调用, 其余参数是回调函数的参数:

语法:

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

代码示例:

第一个参数welcome作为回调函数进行调用,后面则需要传入我们回调函数所需的参数,如下面的$city

<?php
highlight_file(__FILE__);
function welcome($city)
{
echo "Welcome to $city !<br>";
}
call_user_func('welcome', "BeiJing");
call_user_func('welcome', "ShangHai");

?>

示例代码:

当我们传入的参数可控时就会造成RCE

<?php 
highlight_file(__FILE__);
call_user_func($_GET['a1'],$_GET['a2']);
?>

call_user_func_array()

把第一个参数作为回调函数(callback)调用,把参数数组作(param_arr)为回调函数的的参数传入,与call_user_func()函数不同的是,该函数传入的参数是以数组的形式传入的。

语法

call_user_func_array ( callable $callback , array $param_arr )

示例代码:

这里需要注意的是参数必须以数组形式传入,如下面的$a

<?php
highlight_file(__FILE__);
function porduct_num($v,$s)
{
return $v*$s;
}
$a=array(2,3);
echo call_user_func_array("product_num",$a);
?>

示例代码:

当传入的参数可控时就会造成RCE

<?php 
highlight_file(__FILE__);
call_user_func_array($_GET['arg1'],$_GET['arg2']);
?>

这里的arg2传入的参数就是以数组形式就行传递的

如果直接不以数组形式就行传递就会导致致命错误

array_filter()

把输入数组中的每个键值传给回调函数,与call_user_func_array()不同的是,该函数的第一个参数为回调函数的参数,而第二个参数则是传入的回调函数。

代码示例:

当传入的参数可控时就会造成RCE

<?php 
highlight_file(__FILE__);
array_filter(array($_REQUEST['arg1']),$_REQUEST['arg2']);
?>

ob_start()

用于打开输出控制缓冲,如果参数可控也可造成RCE。

<?php 
highlight_file(__FILE__);
$cmd = 'system';
ob_start($cmd); //打开输出区缓存
echo "$_REQUEST['arg']";
ob_end_flush(); //关闭缓存
?>

usort()

使用用户自定义的比较函数对数组中的元素进行排序,该函数第二个参数是用户自定义的回调函数。

语法:

usort ( array &$array , callable $value_compare_func )

代码示例:

<?php
highlight_file(__FILE__);

function my_sort($a, $b)
{
if ($a == $b) return 0;
return ($a < $b) ? -1 : 1;
}

$a = array(4, 2, 8, 6,10,21);
var_dump(usort($a, "my_sort"));

$arrlength=count($a);
for($x=0;$x<$arrlength;$x++)
{
echo $a[$x];
echo "<br>";
}
?>

回调用户自定义my_sort()函数对$a数组中的值进行排序

代码示例:

如果usort()函数参数可控,也可能可造成RCE。PHP版本>=5.6可实现

<?php
highlight_file(__FILE__);

var_dump(usort($_REQUEST,"assert"));

?>

array_walk()

使用用户自定义函数对数组中的每个元素做回调处理,该函数的第二个参数也为回调的自定义函数。

语法:

array_walk ( array &$array , callable $callback [, mixed $userdata = NULL ] )

代码示例:

<?php
highlight_file(__FILE__);

function myfunction($value,$key)
{
echo "The key $key has the value $value<br>";
}
$a=array("a"=>"red","b"=>"green","c"=>"blue");
array_walk($a,"myfunction");

?>

回调上面的myfunction()函数,对$a赋值的键值对进行处理。

代码示例:

如果array_walk()函数参数可控,也可能可造成RCE

<?php
highlight_file(__FILE__);

array_walk($_GET['arg1'],$_GET['arg2']);
?>

由于传入的参数为数组,所以形参需在后面加[]

动态函数

PHP函数直接由字符串拼接,且拼接内容可控就会导致RCE

示例代码:

<?php
highlight_file(__FILE__);

$_REQUEST['arg1']($_REQUEST['arg2']);
?>

这种动态函数代码执行的方式相信大家很好理解,传入的第一个参数则为函数名,而第二个参数则传入参数值。如下面的system为函数,calc.exe为函数值,从而拼接后执行system(calc.exe);弹出计算器。

3.代码执行代码审计总结

在代码审计中有很多关于代码执行的函数,新手期间大家不需要对函数进行深入的了解,只需要对上面的函数有一定的了解和印象,在实际中再次遇到上述函数时可以马上想起该函数和代码执行或者RCE有关,然后在进一步分析传入的参数是否可控,从而才有可能找到该漏洞。
首先要对函数有一定的了解,自己尝试手动敲代码,让自己对这些函数加深一些印象,从而对以后大家正式进行代码审计才有更大的帮助。

往期回顾

PHP代码审计之WEB安全系列基础文章(一)- 任意文件上传漏洞篇

PHP代码审计之WEB安全系列基础文章(二)- 任意文件上传漏洞篇

PHP代码审计之WEB安全系列基础文章(三)之命令执行篇

注意

后台回复以下关键字即可获取对应学习资料!

【navicat15】本节用到的Navicat 15 即破解教程【PHP代审录屏】往期PHP代码审计直播课0531】往期PHP代码审计直播课

文章来源: http://mp.weixin.qq.com/s?__biz=MzA4NDk5NTYwNw==&mid=2651429595&idx=1&sn=7b8d3a09d7562f38369207d8f5eb0e28&chksm=842381a3b35408b5f6bf449795aa6bdd5baa0c76f2210a48f55150bfc95af10408122f452117#rd
如有侵权请联系:admin#unsafe.sh