盘点PHP中的变量覆盖漏洞
一、$$动态变量
1、基本语法
PHP动态变量是指一个变量名的变量名可以动态的设置和使用,一个变量获取另一个变量的值作为这个变量的变量名。
例如下面
$Hello = "World";
$a = "Hello";
echo $a; //hello
echo $$a; //World
# $a="hello",所以$$a=$hello=world
所以运行之后结果则为HelloWorld,其实就类似于C语言的指针
2、漏洞案例
$a = "ping 127.0.0.1";
$b = $_GET['1'];
$$b = $_GET['2'];
system($a);
分析题目,由于system函数执行的是$a,但现在$a的值是”ping 127.0.0.1”,所以我们需要想办法覆盖$a的值为我们指定的命令,这样才能造成命令执行漏洞,答案如下
?1=a&2=whoami
这样传参了之后就变为
$b = a
$$b = $a = $_GET['2'] = 我们传入的whoami
从而利用变量覆盖漏洞让system函数执行任何我们传入的语句,也就是命令执行漏洞
二、extract()
1、基本语法
PHP extract()函数用于将数组中的变量导入到当前的符号表中。 它需要一个关联数组数组,并将键作为变量名和值作为变量值。
例如下面
<?php
extract($_GET);
echo$name.'<br>';
echo$age.'<br>';
echo$id.'<br>';
?>
当以GET方式传入?name=tacoking&age=21&id=123456时,输出的结果就是$name=tacoking;$age=22;$id=123456
也就是name、age、id作为变量名,传进来的值作为了它们的变量值
2、漏洞案例
$a = 'ping 127.0.0.1';
$arr1 = [$_GET['1'] => $_GET['2'],'b'=>2];
extract($arr1);
system($a);
同理,我们也是需要想法设法覆盖掉$a的值,可以看到这里$_GET[‘1’]和$_GET[‘2’]的值作为键值对会被引入符号表,所以我们只需要进行如下传参
?1=a&2=whoami
这样就可以得到$a=whoami,从而覆盖掉$a原来的参数值,也就是变量覆盖漏洞
三、list()
1、基本语法
list() 函数用于将数组中的值赋给一组变量
$info = ['John', 'Doe', 30];
list($firstName, $lastName, $age) = $info;
echo $firstName; // 输出 "John"
echo $lastName; // 输出 "Doe"
echo $age; // 输出 30
2、漏洞案例
$a = 'ping 127.0.0.1';
$arr2 = array($_GET['1'],2);
list($a ,$b) = $arr2;
system($a);
这里的意思也就时$a的值会等于$_GET[‘1’],所以此处我们直接传入$a的值为我们的payload就可以了,传参如下
?1=whoami
四、parse_str()
1、基本语法
parse_str() 函数用于解析 URL 查询字符串,并将其中的参数赋值给相应的变量
$queryString = 'name=John&age=30';
2 parse_str($queryString, $params);
3
4 echo $params['name']; // 输出 "John"
5 echo $params['age']; // 输出 "30"
2、漏洞案例
示例代码,下面附上一段存在变量覆盖导致RCE的漏洞代码案例
$queryString = $_SERVER['QUERY_STRING'];
# 用于获取查询字符串,例如'a=170&b=241'
$com = array('cmd'=>'ping 127.0.0.1');
# parse_str(str,array)
# str:要解析的查询字符串
# array:存储的数组位置(可选,如果不填就解析为单独的变量,填了就作为键值队存储到该数组中)
parse_str($queryString,$$_GET[1]);
echo $com['cmd'];
system($com['cmd']);
payload传参如下
?cmd=whoami&1=com
那这里的parse_str就变为了下面这样
parse_str("cmd=whoami&1=com",$com);
所以此时$com[‘cmd’]的值就为whoami,自然也就造成了变量覆盖的任意命令执行
五、mb_parse_str()
**mb_parse_str()**PHP 中的函数用于解析 GET、POST 和 COOKIE 数据并设置全局变量。它解析 URL 编码的数据并检测编码。之后,它转换内部编码中的编码并设置全局变量的值。PHP 7 或更高版本支持此功能。
$str = 'email=49b1@0ms5vj6.site&city=shanghai&job=diger';
mb_parse_str($str, $result);
print_r($result);
//结果
Array
(
[email] => 49b1@0ms5vj6.site
[city] => shanghai
[job] => diger
)
mb_parse_str()函数和parse_str()类似,所以不再做累赘,感兴趣的可自行研究。
六、compact()
1、基本语法
compact() 函数用于将多个变量转换为关联数组,其中变量名将成为数组的键,变量的值将成为数组的值
$firstName = 'John';
$lastName = 'Doe';
$age = 30;
$info = compact('firstName', 'lastName', 'age');
print_r($info);
2、漏洞案例
示例代码
$com1 = 'ping 127.0.0.1';
$cmd2 = 'ping 127.0.0.1';
$cmd3 = 'whoami';
$info = compact($_GET['1'], 'cmd2');
# [cmd2] = > ping 127.0.0.1
foreach ($info as $a){
echo system($a);
}
传参如下
?1=cmd3
如果外部变量和compact参数可控,就可以利用这种方法进行变量覆盖
七、register_globals配置
当register_globals全局变量设置开启时,传递过来的值会被直接注册为全局变量而使用,这会造成全局变量覆盖
在PHP5.3之前 默认开启 PHP5.3默认关闭 PHP5.6及5.7已经被移除
例如下列漏洞代码
<?php
if ($num){
echo "flag{daylight-04-26}";
}
?>
//payload:http://127.0.0.1/test.php?num=1
这样传入的num=1就会被注册并使用,导致$num不为空,从而获得flag
八、import_request_variables()
(PHP 4 >= 4.1.0, PHP 5 < 5.4.0)
import_request_variables — 将 GET/POST/Cookie 变量导入到全局作用域中
将 GET/POST/Cookie 变量导入到全局作用域中。如果禁止了 register_globals,但又想用全局变量,就可以尝试该函数,简单来说就是用于手动注册传入的变量
例如下面这个漏洞代码案例
<?php
$num=0;
//include 'flag.php';
import_request_variables('gp'); //导入get和post中变量
if($num=="daylight"){
echo 'flag{daylight-2020-3-28}';
// echo $flag.php;
}else{
echo "NO!";
}
?>
//payload:http://127.0.0.1/test.php?num=daylight
//flag{daylight-2020-3-28}