FEATURED · 精选文章

深入解析php://filter死亡绕过:文件包含漏洞的高级利用与防御

发布时间 / 2026/8/3 15:19:25
来源 / 创域科博编辑部
栏目 / 资讯中心
深入解析php://filter死亡绕过:文件包含漏洞的高级利用与防御 1. 项目概述从一次渗透测试的“意外”说起那次渗透测试我遇到了一个典型的文件上传点。前端做了白名单校验只允许上传.jpg、.png等图片格式。我绕过了前端直接构造了一个包含PHP代码的图片文件上传服务器也成功接收了。但当我尝试访问这个文件时却返回了“Access Denied”。检查后发现目标服务器配置了严格的后缀名检测即便文件内容包含恶意代码只要后缀不是.php、.phtml等就不会被解析执行。这堵墙似乎很坚固直到我想起了php://filter这个奇妙的“协议包装器”。它本身并非用于攻击而是PHP提供的一个用于处理数据流的过滤器。但正是它的数据转换能力结合一些服务器配置特性可以构造出一种被称为“死亡绕过”的技巧让一个.txt文件最终被当作.php来执行。今天我们就来彻底拆解php://filter的机制并一步步还原如何利用它进行巧妙的文件包含与代码执行绕过。简单来说php://filter是PHP中一个用于在数据流打开时施加过滤的“包装器”。你可以把它想象成一个多功能的水管接头水流数据经过这个接头时可以被过滤、编码、解码或转换。而“死亡绕过”则是一种利用php://filter进行特定编码转换将恶意PHP代码“隐藏”在看似无害的文件中再通过包含函数解码并执行的高级技巧。这不仅是CTF比赛中的常客在真实的渗透测试中面对有限的文件上传和包含漏洞时它也可能成为打开突破口的那把钥匙。无论你是致力于提升防御深度的安全工程师还是对PHP底层机制充满好奇的开发者理解这套组合拳都至关重要。2. 核心原理php://filter 包装器的深度拆解要理解绕过必须先理解工具本身。php://filter并非一个真正的协议而是PHP流包装器Stream Wrapper的一种。流包装器允许你像操作普通文件一样操作各种数据源比如HTTP资源、压缩文件、或者经过转换的数据流。php://filter的核心价值在于“链式过滤”。2.1 基本语法与工作流程其基本语法结构为php://filter/过滤器链/resource目标资源。一个过滤器链可以包含多个过滤器用竖线|分隔数据会依次经过这些过滤器处理。例如// 读取/etc/passwd文件先经过base64编码再经过字符串旋转13位ROT13编码 $content file_get_contents(php://filter/readconvert.base64-encode|string.rot13/resource/etc/passwd);在这个例子中数据流的方向是读取/etc/passwd文件内容 → 进行base64编码 → 进行ROT13编码 → 最终输出给$content。关键在于这些过滤器是双向的。不仅可以在读取read时应用也可以在写入write时应用。这为数据变形提供了极大的灵活性。2.2 关键过滤器解析在死亡绕过中有几个过滤器扮演着核心角色convert.base64-encode/convert.base64-decode这是最常用的过滤器。Base64编码可以将任意二进制数据包括PHP代码转换为由64个字符A-Z, a-z, 0-9, , /和填充符组成的文本字符串。这使得PHP代码可以“伪装”成普通文本绕过基于内容关键词的检测。string.rot13ROT13是一种简单的字母替换密码将字母移动13位。例如?php经过ROT13编码后会变成?cuc。它有一个非常重要的特性对同一字符串进行两次ROT13编码会得到原字符串。因为字母表26个字母移动13位再移动13位相当于移动26位回到原点。这个特性在构造多级编码时很有用。convert.iconv.*这是一个强大的字符集转换过滤器。格式为convert.iconv.输入编码.输出编码。例如convert.iconv.UTF-8.UTF-16BE可以将UTF-8字符串转换为UTF-16BE编码。在死亡绕过中它常被用来制造特定字节序列或处理编码转换过程中的“副作用”。zlib.deflate/zlib.inflate和bzip2.compress/bzip2.decompress压缩与解压缩过滤器。它们可以改变数据的体积和格式有时用于配合其他过滤器制造特定条件。注意过滤器的顺序极其重要。数据像通过流水线一样依次处理顺序不同最终结果天差地别。在构造payload时必须逆向思考你想要最终得到什么然后反推需要经过哪些过滤器、以何种顺序处理原始数据。2.3 为什么叫“死亡绕过”这个名称听起来很骇人实际上它描述的是一种利用include、require等文件包含函数包含一个本身并非PHP文件但通过php://filter解码后能生成有效PHP代码的流程从而导致代码执行的方法。“死亡”可能意指这种方式的隐蔽性和致命性——防御者看到的可能是一个内容乱码或编码后的文本文件如.txt完全意识不到它被包含时会被“复活”为可执行的PHP代码。它不依赖于服务器错误配置如.php.jpg被解析而是纯粹利用PHP语言自身的特性。3. 经典死亡绕过场景与实操复现理论说得再多不如亲手试一次。我们搭建一个最简单的漏洞环境来演示。假设有一个文件上传功能服务器禁止上传.php后缀文件但存在一处本地文件包含LFI漏洞。3.1 场景搭建一个脆弱的文件包含点假设我们有如下脆弱的PHP脚本include.php?php // 从GET参数中获取文件名并包含这是非常危险的操作 $file $_GET[file]; include($file); ?同时我们只能上传一个内容为?php phpinfo(); ?但后缀必须为.txt的文件假设上传后文件路径为/uploads/malicious.txt。直接访问/include.php?file/uploads/malicious.txt是没用的因为服务器不会解析.txt文件中的PHP标签。3.2 利用php://filter进行Base64解码执行这时php://filter就可以登场了。我们的目标是让include函数读取malicious.txt文件但在数据流入之前先对其进行Base64解码。首先我们需要将PHP代码进行Base64编码?php phpinfo(); ?Base64编码后为PD9waHAgcGhwaW5mbygpOyA/Pg我们将这个编码后的字符串写入malicious.txt。现在这个txt文件里没有任何、?、php等敏感字符看起来就是一段普通的Base64字符串。接下来构造包含的Payload/include.php?filephp://filter/readconvert.base64-decode/resource/uploads/malicious.txt当include执行时它尝试“包含”php://filter/...这个流。php://filter包装器开始工作它读取/uploads/malicious.txt文件的内容即PD9waHAgcGhwaW5mbygpOyA/Pg。读取的数据立即通过convert.base64-decode过滤器。过滤器将Base64字符串解码回原始的二进制数据?php phpinfo(); ?。解码后的数据流被返回给include函数。include函数接收到这段数据流将其作为PHP代码执行于是phpinfo()页面被成功加载。实操心得这里有一个至关重要的细节。convert.base64-decode过滤器在解码时会自动忽略非Base64字符即不在A-Z, a-z, 0-9, , /, 范围内的字符。这意味着你可以在payload前后添加一些注释或垃圾字符有时能绕过一些简单的过滤。例如malicious.txt的内容可以是This is a normal text. PD9waHAgcGhwaW5mbygpOyA/Pg End of file.解码器会忽略“This is a normal text. ”和“ End of file.”只解码中间有效的Base64部分。3.3 进阶多重编码与嵌套过滤器链如果目标系统对base64-decode或php://filter关键字进行了过滤我们可以使用更复杂的过滤器链。方法一使用ROT13PHP默认还支持string.rot13过滤器。?php phpinfo(); ?经过ROT13编码后变为?cuc cucvasb(); ?。我们可以这样利用将ROT13编码后的代码写入malicious.txt。构造Payload/include.php?filephp://filter/readstring.rot13/resource/uploads/malicious.txtinclude包含时数据流被自动ROT13解码回原始PHP代码并执行。方法二双重编码绕过关键字检测假设系统黑名单过滤了base64-decode这个词。我们可以利用过滤器链进行两次编码第一次编码不使用敏感词。 例如我们可以先对原始PHP代码进行Base64编码再对得到的Base64字符串进行ROT13编码。 原始代码 - Base64 - ROT13。 写入文件的是经过ROT13处理的Base64字符串。 Payload则需要先进行ROT13解码再进行Base64解码/include.php?filephp://filter/readstring.rot13|convert.base64-decode/resource/uploads/malicious.txt这样Payload里包含的是string.rot13|convert.base64-decode可能绕过了对单一base64-decode的检测。重要注意事项string.rot13过滤器只影响字母字符数字和符号不变。而Base64编码串中包含、/、等符号ROT13不会改变它们。因此“Base64串的ROT13编码”仍然是有效的Base64串因为字母部分被替换了但解码器需要的是替换后的字母。这需要精确计算在实际攻击中通常使用脚本生成payload而非手动操作。3.4 利用convert.iconv构造特殊Payloadconvert.iconv过滤器功能强大也更为复杂。一个著名的技巧是利用它进行“死亡绕过”的变种无需在外部文件中预先写入编码后的PHP代码而是直接通过过滤器链动态生成。考虑这个Payload/include.php?filephp://filter/convert.iconv.UTF-8.UTF-16BE/resourcedata://text/plain,?php phpinfo();这里使用了data://协议直接内嵌代码。但重点在于convert.iconv.UTF-8.UTF-16BE。UTF-16BE编码会在每个原始字符前添加一个\x00字节。对于字符串?php phpinfo();转换后会在、?、p等每个字符前都加上\x00。为什么这样可能成功这依赖于一个非常特定且旧的环境当PHP设置中zend.multibyte启用并且设置了特定的编码检测顺序时PHP引擎可能会将这种带有BOM字节顺序标记或特定字节序列的流识别为UTF-16编码并尝试将其转换回UTF-8执行。然而在现代PHP版本5.4以后和默认配置下这种利用方式已经极其罕见成功率很低。它更常出现在CTF题目中作为考察对编码深层理解的知识点。实操心得convert.iconv的利用通常非常环境依赖且Payload构造复杂。在真实渗透测试中优先使用base64-decode和rot13这类可靠的方法。iconv更多用于处理特殊情况例如当目标文件内容不可控但我们可以通过多次编码转换最终巧合地生成一个?标签时。4. 防御视角如何识别与防范死亡绕过作为防御者了解攻击手法是为了更好地筑起防线。针对php://filter死亡绕过防御需要多层次进行。4.1 安全开发杜绝漏洞根源绝对避免动态文件包含这是治本之策。尽量不要使用include($_GET[‘file’])这种模式。如果业务必须动态加载则采用白名单机制。// 正确的做法白名单 $allowed_files [‘page1.php’, ‘page2.php’, ‘news.php’]; $file $_GET[‘file’]; if (in_array($file, $allowed_files)) { include(‘./templates/’ . $file); } else { die(‘Invalid file requested.’); }严格限制包含路径使用include时尽量使用绝对路径或基于固定目录的相对路径避免用户输入包含目录遍历字符../。// 设置包含路径为基础目录并禁止目录穿越 $baseDir ‘/var/www/html/includes/’; $file str_replace(‘..’, ‘’, $_GET[‘file’]); // 移除目录遍历 $filepath realpath($baseDir . $file); // 确保最终路径仍在基础目录内 if (strpos($filepath, $baseDir) 0 file_exists($filepath)) { include($filepath); }4.2 输入验证与过滤过滤协议包装器在接收文件路径参数时检查是否包含php://、data://、phar://等危险协议。$file $_GET[‘file’]; if (preg_match(/^(php|data|phar|zip|expect):\/\//i, $file)) { die(‘Dangerous protocol detected.’); }注意不要只过滤php://filter攻击者可能会使用php://input用于读取POST原始数据或其他包装器。后缀名强校验对于文件包含如果预期包含的是特定类型的文件如模板应强制校验后缀名并结合MIME类型检查。4.3 服务器与配置加固配置open_basedir在php.ini中设置open_basedir将PHP可访问的文件限制在网站根目录等必要目录下即使包含漏洞被利用也无法读取系统关键文件如/etc/passwd。禁用危险函数在生产环境中考虑在php.ini中禁用include、require等函数disable_functions。但这通常不现实因为会影响框架运行。更可行的是通过Web应用防火墙WAF或代码审计工具来监控危险函数的调用。更新PHP版本保持PHP版本最新许多利用特定编码转换的奇技淫巧在较新的PHP版本中已被修复或默认不可用。4.4 安全监控与审计日志分析监控Web服务器日志和PHP错误日志寻找包含php://filter、base64-decode等关键字的访问请求这通常是攻击尝试的明显特征。WAF规则在WAF上部署规则拦截包含常见过滤器链如convert.base64-decode、string.rot13的请求参数。代码审计定期进行源代码安全审计重点检查所有用户输入直接传入文件操作函数include,require,file_get_contents,fopen等的地方。5. 实战中的疑难杂症与排查技巧即便理解了原理在真实环境或CTF比赛中运用php://filter死亡绕过时仍会遇到各种问题。以下是我总结的一些常见坑点及排查思路。5.1 Payload构造失败常见原因问题现象可能原因排查思路与解决方案包含后页面空白或报错“No such file”1. 文件路径错误。2.php://filter被禁用。3. 包含的文件内容解码后不是有效PHP代码如缺少?php标签。1. 先尝试包含一个普通的文本文件如?file./test.txt确认路径和包含功能正常。2. 使用phpinfo()页面查看allow_url_include和allow_url_fopen设置php://包装器需要它们通常为On但allow_url_include自PHP高版本起默认Off且强烈建议关闭。3. 检查编码过程。确保原始PHP代码以?php开头而不是?短标签因为服务器可能关闭了短标签支持。将解码后的内容用echo base64_decode(‘…’)输出确认是否正确还原。包含后代码被直接显示在页面上文件后缀或内容未被服务器识别为PHP导致代码被当作纯文本输出。这正是死亡绕过要解决的问题。确认你的过滤器链确实将内容解码成了有效的PHP代码。尝试在Payload末尾添加一个some-param1因为有时包含非PHP文件时服务器会将其内容作为HTTP响应体输出添加参数可能改变响应头。但核心还是确保解码成功。Base64解码失败1. Base64字符串含有非法字符或格式错误。2. 字符串长度不是4的倍数对于填充的要求PHP的base64_decode函数比较宽松但过滤器可能严格。1. 确保编码内容只包含A-Z, a-z, 0-9, , /, 。使用在线工具或PHP的base64_encode函数确保编码正确。2. 如果手动构造注意填充。一个技巧可以在原始代码末尾添加注释如//a来调整长度使编码后字符串长度合规。使用iconv过滤器无效果1. 指定的编码名称错误或PHP未支持。2. 转换后的字节序列无法被PHP引擎识别为代码。1. 使用print_r(stream_get_filters());查看当前PHP环境支持的过滤器列表。2.iconv利用极其依赖环境。在实战中除非有明确提示如CTF题目描述否则不建议作为首选。将其视为一种“奇门”技巧。5.2 高阶技巧利用临时文件与竞争条件在严格限制下可能无法直接上传.txt文件。这时可以结合其他漏洞。例如如果存在一个允许上传任意文件但会检查文件头如图片的功能你可以上传一个内容为GIF89a?php ...的文件GIF文件头后面拼接PHP代码上传后服务器可能将其保存为.gif。然后利用文件包含漏洞配合php://filter并指定从某个偏移量开始读取php://filter本身不支持偏移但可以结合其他方式或利用zip://包装器跳过文件头只读取后面的PHP代码部分并进行解码执行。这需要更精细的构造。另一种思路是竞争条件。如果服务器会将上传的临时文件在极短时间内进行包含例如某些框架或缓存机制你可以尝试在上传的文件内容中嵌入php://filter的Payload指向自身或其他资源进行链式利用。但这要求时间窗口非常精确。5.3 工具辅助与自动化手动构造复杂的过滤器链尤其是涉及多重编码和iconv时很容易出错。推荐使用工具辅助PHP交互式命令行在本地PHP环境中使用php -a进入交互模式快速测试过滤器链的效果。php echo file_get_contents(‘php://filter/readconvert.base64-encode/resourcedata://text/plain,hello’); aGVsbG8Python/Node.js脚本编写小脚本自动生成经过特定过滤器链编码后的Payload并验证解码结果。Burp Suite 插件或扩展有些安全测试插件集成了Payload生成功能可以方便地生成和测试php://filter的各种变形。最后再分享一个我个人的检查清单在尝试利用文件包含漏洞时我会按顺序尝试以下Payload逐步提升复杂度基础路径遍历../../../../etc/passwd基础包装器php://filter/resource/etc/passwd编码读取php://filter/readconvert.base64-encode/resource/etc/passwd用于读取源码死亡绕过尝试上传一个Base64编码的PHP文件如shell.txt然后包含php://filter/readconvert.base64-decode/resourceshell.txt多重编码绕过如果上一步被WAF拦截尝试加入string.rot13或其他过滤器。尝试data://协议data://text/plain,?php phpinfo();?需要allow_url_includeOn现代环境极少开启。尝试phar://协议进行反序列化利用这是另一个话题。理解php://filter和死亡绕过不仅仅是学习一种攻击技巧更是深入理解PHP流处理机制和编码安全的一次绝佳机会。从防御角度看它再次强调了“永远不要信任用户输入”这一铁律以及实施深度防御、在多个层面进行校验的必要性。在渗透测试中它则是一件需要耐心和精准度的“手术刀”在看似严密的防御中寻找那条细微的裂缝。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻