什么是变量覆盖?
变量覆盖指的是可以用我们的传参值替换程序原有的变量值
比如
<?php
$ a=1;
$ a=2;
echo $a;
?>
像这个输出是2因为a=2覆盖了a=1
基本概念
- 程序未正确验证用户输入
- 用户输入被直接赋值给变量
- 缺乏适当的过滤和验证机制
怎么去寻找变量覆盖?
经常导致变量覆盖漏洞场景有:$$使用不当,extract()函数使用不当,parse_str()函数使用不当import_request_variables()使用不当,开启了全局变量注册等,还是要多多代码审计
手动测试
- 识别接收用户输入的入口点
- 尝试覆盖已知变量
- 观察程序行为变化
自动化工具
- 使用Burp Suite等工具进行参数模糊测试
- 自定义扫描规则检测危险函数
变量覆盖漏洞有的时候可以直接让我们获取Webshell,拿到服务器的权限
什么是变量覆盖漏洞
变量覆盖指的是用我们自定义的参数值替换程序原有的变量值,一般变量覆盖漏洞需要结合程序的其它功能来实现完整的攻击。变量覆盖漏洞大多数由函数使用不当导致,$$使用不当,extract()函数使用不当,parse_str()函数使用不当,import_request_variables()使用不当,开启了全局变量注册等。
危害
变量覆盖漏洞的危害非常广泛,因为攻击者能够改变程序的行为,绕过安全机制,甚至获取系统权限。以下是一些主要的危害:
1.认证绕过:攻击者可以覆盖用于认证的变量,例如将`$is_admin`覆盖为`true`,从而获得管理员权限。
2.权限提升:通过覆盖角色或权限变量,攻击者可以提升自己的权限,执行未授权的操作。
3.业务逻辑绕过:例如在电子商务网站中,覆盖价格变量,从而以低价购买商品。
4.信息泄露:通过覆盖配置变量,开启调试模式,从而泄露敏感信息,如数据库连接字符串、系统路径等。
5.任意文件读取:覆盖文件路径变量,读取任意文件,包括系统文件、配置文件等。
6.任意代码执行:通过覆盖变量,导致包含恶意文件或执行任意命令。
7.数据库操作:覆盖数据库连接变量,导致连接攻击者控制的数据库,或者覆盖SQL查询条件,导致数据泄露或篡改。
8.会话劫持:覆盖会话变量,冒充其他用户。
函数解析
经常引发变量覆盖漏洞的函数有:extract() parse_str() import_request_variables()
extract()函数(作用:将数组中将变量导入到当前的符号表)
PHP中的extract()函数
extract() 函数从数组中将变量导入到当前的符号表。
该函数使用数组键名作为变量名,使用数组键值作为变量值。针对数组中的每个元素,将在当前符号表中创建对应的一个变量。
该函数返回成功设置的变量数目。
实例:
<?php
$a = "1";
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse");
extract($my_array);
echo "$a = $a; $b = $b; $c = $c";
?>
运行结果:$a = Cat; $b = Dog; $c = Horse
// 漏洞代码
extract($_GET);
if ($is_admin) {
// 执行管理员操作
}
// 攻击:?is_admin=1
extract()函数(作用:从数组中将变量导入到当前的符号表) 一道CTF题目

分析源码我们可以知道,
1、文件将get方法传输进来的值通过extrace()函数处理。
2、通过两个if语句分别判断是否存在gift变量,和变量gift的值和变量content的值是否相等。变量content的值是通过读取变量test的值获取到的。如果两个变量相等输出flag。如果不相等,输出错误。
似乎逻辑上没啥问题,但是如果我们传参了test呢?
第一开始test在php中已经定义了,但是因为extrace()函数,我传参test时相当于重新给test赋值对不对?因为php执行语句是自上而下,那我传的参数完全可以覆盖掉之前所定义的
那么当我传参gift=a&test=a,相当于$gift=a;$test=a
那么这里是不是就直接输出flag了呢 (因为$content是由$test决定,$gift和$test都是我可以决定的)
parse_str()函数
parse_str函数的作用就是解析字符串并注册成变量,在注册变量之前不会验证当前变量是否存在,所以直接覆盖掉已有变量
注意:如果未设置 array 参数,由该函数设置的变量将覆盖已存在的同名变量。
parse_str() 将查询字符串解析到变量中:
<?php
parse_str("name=zkaq&&age=60"); // test=123&gift=123
echo $name."<br>";
echo $age;
?>
输出了zkaq和60
那么parse_str("name=Bill&age=60") 相当于完成了$name ='zkaq'和$age ='60'
那么如果在parse_str中可以直接传参的话,那么是不是也可以覆盖变量呢。
// 漏洞代码
parse_str($_SERVER['QUERY_STRING']);
if ($authenticated) {
// 访问敏感功能
}
// 攻击:?authenticated=1
什么是$$
$$这种写法称为可变变量,一个可变变量获取了一个普通变量的值作为这个可变变量的变量名。
不仅仅是函数会导致变量覆盖,有些特殊符号的特殊搭配也会引起变量覆盖漏洞,比如$$
$$ 导致的变量覆盖问题在CTF代码审计题目中经常在foreach中出现,如以下的示例代码,使用foreach来遍历数组中的值,然后再将获取到的数组键名作为变量,数组中的值作为变量的值。因此就产生了变量覆盖漏洞。请求?name=test 会将$name的值覆盖,变为test。
上一个例题:
<?php
$a = 1;
foreach(array('_COOKIE','_POST','_GET') as $_request) {
foreach($_request as $_key=>$_value)
{$_key=addslashes($_value);}}
echo $a;
?>
这个代码会接受我们的GET提交、POST提交、COOKIE参数,将这个接受来的参数依次放入$_request
$_key=>$_value 这是个数组解析,实际上就是键值分离
正常而言$a = 1是一个定值,但是因为$_key的缘故,当我传参a=2;那么$_key=addslashes($_value);就变为了$a = 2 .
// 基本动态变量
$var_name = "username";
$var_name = "John Doe"; // 创建变量 $username = "John Doe"
// 在循环中使用
foreach ($_REQUEST as $key => $value) {
$key = $value; // 极度危险!
}
import_request_variables()
import_request_variables—将 GET/POST/Cookie 变量导入到全局作用域中
import_request_variables()函数就是把GET、POST、COOKIE的参数注册成变量,用在register_globals被禁止的时候
// 漏洞代码(PHP < 5.4)
import_request_variables('G');
if ($admin_flag) {
// 管理员权限
}
此函数在 PHP 5.4.0 中已被移除
漏洞分类
直接变量覆盖
// 示例代码
$is_admin = false;
extract($_POST);
if ($is_admin) {
// 执行管理员操作
}
攻击者可通过提交 is_admin=1 来提升权限。
全局变量覆盖
// 旧版PHP中register_globals开启时
// 用户提交 ?admin=1 会自动创建 $admin 变量
if ($admin) {
// 管理员功能
}
数组键名覆盖
$config = array(
'debug' => false,
'security_level' => 'high'
);
foreach($_GET as $key => $value) {
$config[$key] = $value;
}
漏洞利用技术
基础利用
GET /vulnerable.php?is_admin=1&username=attacker HTTP/1.1
条件竞争利用
// 存在条件竞争的场景
if (isset($_POST['token']) && $_POST['token'] === $secret_token) {
$authorized = true;
}
// 稍后检查
if ($authorized) {
// 执行敏感操作
}
对象属性覆盖
class User {
public $is_admin = false;
public $username = 'guest';
}
$user = new User();
extract($_POST); // 可能覆盖$user对象的属性
案例分析
WordPress插件漏洞 (CVE-2018-12895)
// 漏洞代码片段
extract(shortcode_atts(array(
'count' => 10,
'type' => 'post'
), $atts));
// 攻击者可以通过shortcode属性覆盖任意变量
// [shortcode count=10 type=post _wpnonce=invalid]
知名CMS配置覆盖漏洞
// 安装脚本中的漏洞
$db_host = 'localhost';
$db_user = 'root';
$db_pass = '';
// 从配置文件读取
if (file_exists('config.php')) {
include 'config.php';
}
// 攻击者可以在包含前覆盖变量
// 请求:?db_host=attacker-server.com&db_user=hacker
漏洞演示环境
<?php
// vulnerable_demo.php
error_reporting(0);
$is_authenticated = false;
$user_role = 'guest';
// 危险代码 - 存在变量覆盖漏洞
if (isset($_GET['action']) && $_GET['action'] == 'login') {
extract($_POST);
if ($is_authenticated && $user_role == 'admin') {
echo "Welcome Admin! Flag: FLAG{sanjiu6666flag}";
} else {
echo "Access Denied!";
}
}
?>
<form method="post" action="?action=login">
<input type="text" name="username" placeholder="Username">
<input type="password" name="password" placeholder="Password">
<input type="submit" value="Login">
</form>
代码审计
代码逻辑
初始化两个变量:$is_authenticated 设置为 false,$user_role 设置为 'guest'。
检查GET参数 action 是否为 login,如果是,则执行登录逻辑。
在登录逻辑中,使用 extract($_POST) 将POST数组中的键值对转换为变量。例如,如果POST数据中有 username=test,那么就会创建一个变量 $username 并赋值为 'test'。
然后检查 $is_authenticated 是否为真,并且 $user_role 是否为 'admin'。如果两个条件都满足,就输出管理员欢迎信息和flag;否则,输出“Access Denied!”。
漏洞产生原因
漏洞在于使用了 extract($_POST) 函数。这个函数会将POST请求中的每个参数都转换为变量,并且如果已经存在同名的变量,则会覆盖原有的变量。
在代码中,原本已经定义了 $is_authenticated 和 $user_role 两个变量。但是,通过 extract($_POST),攻击者可以通过POST请求提交同名的参数来覆盖这两个变量的值。
利用原理
攻击者可以构造一个POST请求,其中包含两个参数:is_authenticated 和 user_role,并分别设置值为 1(或任何非零值,在条件判断中为真)和 'admin'。这样,当执行 extract($_POST) 时,原本的 $is_authenticated 和 $user_role 就会被覆盖为攻击者指定的值。
因此,在后续的条件判断中:
if ($is_authenticated && $user_role == 'admin')
条件就会成立,因为现在 $is_authenticated 为真(被覆盖为1),并且 $user_role 为 'admin'(被覆盖为'admin')。
流程详解
1:GET参数触发条件
GET参数: ?action=login
满足条件 isset($_GET['action']) && $_GET['action'] == 'login'
进入if语句块,执行 extract($_POST)
2:extract()函数执行
extract($_POST); // 将POST数组转换为变量
POST数据解析为:
$_POST = array(
'is_authenticated' => '1',
'user_role' => 'admin',
'username' => 'test',
'password' => 'test'
);
extract()执行后相当于:
$is_authenticated = '1'; // 覆盖了原来的false
$user_role = 'admin'; // 覆盖了原来的'guest'
$username = 'test';
$password = 'test';
3:条件检查
if ($is_authenticated && $user_role == 'admin') {
现在变量的值:
$is_authenticated = '1'(布尔值为true)$user_role = 'admin'(等于’admin’)
条件判断:
$is_authenticated→ true$user_role == 'admin'→ true- 整体条件:true && true → true

Payload
POST /vulnerable_demo.php?action=login HTTP/1.1
Content-Type: application/x-www-form-urlencoded
is_authenticated=1&user_role=admin&username=test&password=test

总结
变量覆盖漏洞是一个严重的安全威胁,主要源于:
- 危险函数的不当使用 – extract(), parse_str()等
- 缺乏输入验证 – 直接使用用户输入赋值变量
- 动态变量滥用 – $$var形式的变量操作
- 配置不当 – register_globals等危险配置
防护核心原则:
- 避免使用危险函数 – 如extract()、parse_str()等
- 实施输入验证 – 使用白名单机制
- 明确变量初始化 – 避免使用未初始化的变量
- 代码安全审查 – 定期检查代码中的安全隐患

