变量覆盖漏洞产生的原因 :
$$使用不当,extract()函数使用不当,parse_str()函数使用不当,import_request_variables()使用不当,开启了全局变量注册等(全局变量:register_globals的意思就是注册为全局变量,所以当On的时候,传递过来的值会被直接的注册为全局变量直接使用,而Off的时候,我们需要到特定的数组里去得到它。)
$$
$$是可变变量:
$a = 'b';
$b = 'kuaikuai';
echo $$a; //kuaikuai
这里$$a就是$b
1、foreach()
foreach()
是 PHP 中的循环语句,用于遍历数组或可迭代对象。
一般的使用形式是:
foreach ($array as $value) {
// 执行代码
}
其中,$array
是要遍历的数组,$value
是在每次循环中获取的数组元素的值。
除了遍历值之外,还可以通过以下形式获取键和值:
foreach ($array as $key => $value) {
// 执行代码
}
这样,在每次循环中,$key
变量将获取到当前元素的键,而 $value
变量将获取到当前元素的值。
引入一道例题:
<?php
include “flag.php”;
$_403 = “Access Denied”;
$_200 = “Welcome Admin”;
if ($_SERVER["REQUEST_METHOD"] != “POST”)
die(“BugsBunnyCTF is here :p…”);
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 . "\n";
die($_200);
}
?>
题目分析:
foreach 循环都使用动态变量赋值的方式将数组的键值对分别赋值给对应的变量。
在这个特定的代码中,如果在请求的 POST 数据中存在名为 "flag" 的键值对,那么执行第二个 foreach 循环时,会生成一个名为 $flag 的动态变量,并将其赋值为 $_POST["flag"]。然后,在条件判断语句中,检查 $_POST["flag"] 的值是否与先前包含的 $flag 变量的值完全相等。如果两者不相等,则会调用 die 函数终止程序执行,并输出 $_403 变量的值。
解题思路:首先将$flag的值赋给$_200或$_403变量,然后利用die($_200)或 die($_403)将flag打印出来。拿_200举例,先把flag赋值给$_200,flag的值随便输入,在最后一个if中你POST的flag值就是你输入的值恒成立,通过else把_200的值输出,这个输出就是原本flag的值,因为前面_200的值已经被flag的值覆盖掉了。
那么payload就有:
GET /?_200=flag
post请求体:flag=1
2、extract()
extract()
是 PHP 中的一个函数,用于将数组的键名作为变量名,键值作为变量值,将数组中的元素导入到当前的符号表中。
使用 extract()
函数的基本语法如下:
extract($array);
其中 $array
是要导入的数组。该函数会将数组中的每个元素解析为一个变量,并将其添加到当前的符号表中。
例如,对于以下数组和 extract()
函数:
$array = array("name" => "Alice", "age" => 25);
extract($array);
echo $name; // 输出:Alice
echo $age; // 输出:25
extract()函数上的变量覆盖漏洞如下:
$a = "abc";
extract($_GET();
echo $a;
?>
GET /?a=1,由于传入了a=1,所以此时$a的值abc就会被覆盖成1
但是如果我们写成extract($_GET,EXTR_SKIP);那就不会覆盖原有的变量
一道例题引用:
<?php
$flag = 'xxx';
extract($_GET);
if (isset($gift)) {
$content = trim(file_get_contents($flag));
if ($gift == $content) {
echo 'flag{This_is_flag}';
} else {
echo 'Oh..no..flag';
}
}
?>
-
file_get_contents($flag)
:这个函数用于读取文件的内容并返回字符串。$flag
是文件路径的变量或字符串,用于指定要读取的文件。 -
trim()
函数是用来去除字符串两端的空白字符(空格、制表符、换行符等)。它接受一个字符串作为参数,并返回去除空白字符后的字符串。
所以,trim(file_get_contents($flag))
的作用是读取指定文件的内容,并删除内容两端的空白字符,返回的结果是经过去除空白字符处理后的字符串。
这里的payload是:
GET /?gift= &flag=
extract()会将flag和gift的原本的值覆盖为空。$content = file_get_contens()的文件为空或不存在时则返回空值,即可以满足条件$gift == $content。
3、parse_str()
parse_str() :把查询字符串解析到变量中。(parse_str()类似的函数还有mb_parse_str(),用法基本一致)
注释:如果未设置 array 参数,则由该函数设置的变量将覆盖已存在的同名变量。
注释:php.ini 文件中的 magic_quotes_gpc 设置影响该函数的输出。如果已启用,那么在 parse_str() 解析之前,变量会被 addslashes() 转换。
示例:
$query = "a=1&b=2&c=3";
// 将查询字符串解析到变量中
parse_str($query);
echo $a; // 输出:1
echo $b; // 输出:2
echo $c; // 输出:3
在上述代码中,parse_str()
函数将查询字符串 $query
解析为变量,然后我们可以直接访问这些变量。
如果当前作用域已经存在与查询字符串中的参数名相同的变量,那么该变量的值将被覆盖:
$query = "a=1&b=2&c=3";
$a = 10;
// 将查询字符串解析到变量中
parse_str($query);
echo $a; // 输出:1,而不是 10
在这个示例中,由于已经存在 $a
变量且其初始值为 10,parse_str()
函数会将查询字符串中的 a=1
的值 1 覆盖了原来的值。
4、import_request_variables
它用于将 GET、POST 和 Cookie 变量导入到全局作用域中。在 PHP 5.4.0 版本之后,该函数已被弃用。
当 register_globals
设置为 off
(默认情况下应该是 off
)时,超全局变量(如 $_GET
、$_POST
、$_COOKIE
等)不会自动导入到全局作用域中。这是一种安全性改进,防止未经意的全局变量污染。
使用 import_request_variables()
函数可以绕过 register_globals
的限制,它可以将 GET、POST 和 Cookie 变量一次性导入到全局作用域中。这意味着可以直接使用这些变量而无需使用超全局变量数组。
以下是一个示例:
// 将 GET、POST 和 Cookie 变量导入到全局作用域中
import_request_variables("GPC");
// 可以直接使用 GET、POST 和 Cookie 中的变量
echo $a; // 相当于 $_GET['a']
echo $b; // 相当于 $_POST['b']
echo $cookie; // 相当于 $_COOKIE['cookie']