PHP文件包含与日志利用:从CTF题目到实战攻防链

发布时间:2026/10/17 22:06:32

PHP文件包含与日志利用:从CTF题目到实战攻防链 1. 从一道经典题目看PHP文件包含与日志利用的完整攻防链[XNUCA2019Qualifier]EasyPHP这个标题在CTF圈子里算是一个老面孔了很多刚接触Web安全方向的朋友第一次遇到它时往往会被它看似简单的代码逻辑迷惑觉得不过就是一个文件包含嘛有什么难的。但真正上手之后才会发现这道题在文件包含的基础上叠加了多层限制涉及伪协议利用、日志投毒、临时文件竞争、正则绕过等多个知识点是一道非常适合用来打通Web安全基础链路的中等难度题目。我自己第一次做这道题的时候前前后后折腾了将近三个小时踩了无数坑从最开始的盲目尝试到后来逐步理清思路整个过程其实比最终拿到flag更有价值。所以这篇文章我打算把这道题从头到尾拆一遍不仅讲怎么做更要讲清楚每一步为什么要这么做以及在实际操作中容易遇到哪些问题。适合有一定PHP基础、了解文件包含基本概念、但还不太熟悉综合绕过的朋友阅读。如果你已经能熟练运用各种伪协议和日志包含技巧这篇文章里关于临时文件竞争和正则绕过的细节部分可能仍然对你有参考价值。先说一下这道题的大致面貌。题目给了一个PHP文件核心逻辑大概是接收一个参数然后做了一些过滤最后进行文件包含。表面上看过滤规则不算特别严格但实际测试下来会发现很多常规payload都被拦了。这就迫使你不得不去思考还有哪些路径可以走哪些被忽略的角落可以利用这种“在限制中找突破口”的思维方式恰恰是Web安全中最核心的能力之一。2. 题目源码拆解与核心限制分析2.1 源码逻辑逐行解读拿到题目后第一件事永远是看源码。这道题的核心代码结构大致是这样的基于常见版本还原?php if(isset($_GET[file])) { $file $_GET[file]; if(preg_match(/php|data|filter|input|log|http|ftp|zip|phar/i, $file)) { die(Hacker!); } include($file); } else { highlight_file(__FILE__); } ?这段代码的逻辑非常直白接收GET参数file用正则检查是否包含一系列敏感关键词如果包含就直接退出否则执行include。看起来很简单对吧但问题就出在这个正则上。首先它过滤了php、data、filter、input、log、http、ftp、zip、phar这些关键词而且用了/i修饰符意味着大小写不敏感。这就把大部分常见的伪协议和日志包含路径都堵死了。比如你想用php://filter读源码php这个词直接被拦想用data://执行代码data被拦想包含日志文件log被拦想用http://远程包含http被拦。但这里有一个关键点正则匹配的是整个$file字符串只要字符串中出现了这些关键词就会被拦截。那么有没有办法在不出现这些关键词的情况下实现文件包含呢这就是解题的核心思路所在。2.2 过滤规则背后的逻辑漏洞仔细分析这个正则你会发现几个可以利用的点。第一它只检查了$file参数的值但没有限制文件路径的构造方式。也就是说如果你能找到一个不包含敏感词的文件路径并且这个文件的内容可控那么就能实现代码执行。第二正则中虽然过滤了很多协议名但并没有过滤所有可能的路径。比如/proc/self/environ、/var/log/下的某些日志文件、/tmp/下的临时文件等这些路径本身不包含敏感词但可能包含可控内容。第三也是最关键的一点include函数在包含文件时如果文件内容中有PHP代码会直接执行。所以我们的目标就变成了找到一个路径中不含敏感词、内容可控、且能被include执行的文件。这里有一个容易被忽略的细节正则匹配的是$file的完整值但PHP在解析路径时可能会对某些字符做处理。比如/proc/self/root/这样的路径虽然最终指向根目录但字符串本身不包含敏感词。不过这道题里更直接的思路是找临时文件或者日志文件。2.3 为什么常规payload全部失效很多新手拿到这道题的第一反应是尝试php://filter/convert.base64-encode/resourceindex.php结果发现php被拦了。然后尝试大小写混合PHP://filter因为/i修饰符的存在也被拦。接着尝试php://inputphp和input都被拦。再尝试data://text/plain,?php system(ls);?data被拦。这时候很多人会陷入一个误区不停地尝试各种编码和变形试图绕过正则。但实际上正则匹配的是原始字符串URL编码在PHP接收到参数时已经被解码了所以%70%68%70这样的编码在$_GET中会变成php照样被拦。双重编码也不行因为PHP只解码一次。所以正确的思路不是硬绕正则而是换一条路找一个路径中天然不含这些敏感词的文件。这就引出了两个经典方向日志包含和临时文件包含。3. 日志包含与临时文件竞争的核心原理3.1 日志包含的可行性分析日志包含的思路是Web服务器会把用户的访问请求记录到日志文件中如果我们能控制请求头中的某些字段比如User-Agent就能把PHP代码写入日志然后通过文件包含执行日志文件中的代码。但这里有一个问题题目过滤了log这个词。这意味着我们不能直接在$file参数中写/var/log/nginx/access.log这样的路径。那怎么办呢其实日志文件的路径不一定非要包含log这个词。比如某些服务器配置下日志文件可能位于/var/log/目录下但文件名可能是access.log或者error.log都包含log。不过我们可以考虑其他路径比如/proc/self/fd/下的文件描述符或者某些应用自己写的日志文件路径中可能不含log。但更常见的情况是这道题的设计意图并不是让你直接包含日志文件而是利用PHP的临时文件机制。因为日志包含需要知道日志的确切路径而且很多CTF环境中的Web服务器日志配置并不一定可预测。3.2 PHP临时文件机制详解PHP在处理文件上传时会将上传的文件先保存为临时文件默认位于/tmp/目录下文件名通常是php加上一串随机字符比如/tmp/phpXXXXXX。这个临时文件在请求结束后会被自动删除但如果我们在请求结束前通过文件包含读取它就能执行其中的内容。这里的关键在于临时文件的路径中不包含任何被过滤的关键词。/tmp/phpXXXXXX中虽然有php但注意正则匹配的是$file参数的值而不是实际的文件路径。如果我们能通过某种方式让$file参数的值不包含php但最终包含的文件是/tmp/phpXXXXXX那就成功了。等等这里需要澄清一下include($file)中的$file就是我们传入的值如果我们要包含/tmp/phpXXXXXX那$file的值就必须包含php这会被正则拦截。所以直接包含临时文件是行不通的。那怎么办这就引出了另一个技巧利用/proc/self/fd/目录。在Linux系统中每个进程的文件描述符都可以通过/proc/self/fd/访问。当我们上传文件时PHP会打开这个临时文件其文件描述符可能是3、4、5等。如果我们能包含/proc/self/fd/3就能读取到临时文件的内容而/proc/self/fd/3这个路径中不包含任何被过滤的关键词。但这里有一个问题文件描述符的编号是不确定的取决于PHP进程当前打开了哪些文件。而且临时文件在请求结束后会被删除所以我们需要在同一个请求中完成上传和包含或者利用竞争条件。3.3 竞争条件的本质与利用方式竞争条件的核心思想是在临时文件被删除之前通过多次请求尝试包含它。具体来说我们可以发送一个上传文件的请求同时发送大量包含请求尝试包含/proc/self/fd/下的各个文件描述符。由于服务器处理请求是并发的总有一个瞬间临时文件还存在且文件描述符编号能被猜到。这种方法的成功率取决于服务器的并发处理能力和临时文件的存活时间。在实际操作中通常需要写一个脚本来自动化这个过程不断发送上传请求和包含请求直到成功执行代码。但这里还有一个问题题目过滤了php而/proc/self/fd/路径中不包含php所以这个路径本身是可以通过正则的。但我们需要包含的文件内容中要有PHP代码这可以通过上传一个内容为?php system(cat /flag);?的文件来实现。不过上传文件的功能并不是题目直接提供的。这道题只提供了一个文件包含点没有上传点。那怎么让临时文件出现呢答案是利用PHP的session.upload_progress机制或者利用multipart/form-data请求中PHP自动创建的临时文件。4. 完整利用链的构建与实操步骤4.1 利用session.upload_progress创建临时文件PHP有一个特性当session.upload_progress.enabled开启时如果你在multipart/form-data请求中上传一个文件并且POST字段中包含与session.upload_progress.name同名的字段PHP会自动创建一个上传进度文件这个文件的内容包含了你上传的文件名等信息而这些信息是可控的。具体来说我们可以构造一个请求上传一个文件文件名为?php system(cat /flag);?同时设置PHP_SESSION_UPLOAD_PROGRESS字段。PHP会将这个文件名写入session文件session文件通常位于/tmp/sess_XXXX或/var/lib/php/sessions/sess_XXXX。但这里又有一个问题session文件的路径中可能包含php比如/var/lib/php/sessions/这会被正则拦截。不过我们可以尝试其他路径或者利用/proc/self/fd/来读取session文件。实际上更常见的做法是直接包含session文件。但题目过滤了php而session文件路径中如果包含php就会被拦。所以我们需要找一个不包含php的session存储路径。在某些配置下session文件可能存储在/tmp/目录下路径为/tmp/sess_XXXX这个路径中不包含php可以通过正则。所以完整的利用链是构造一个multipart/form-data请求上传一个文件文件名为?php system(cat /flag);?。在POST字段中设置PHP_SESSION_UPLOAD_PROGRESS触发session文件的创建。同时发送包含请求尝试包含/tmp/sess_XXXX。由于session文件在请求处理过程中一直存在只要我们能猜到session ID就能成功包含。但session ID是随机的怎么猜答案是我们可以自己指定session ID。在Cookie中设置PHPSESSID为我们想要的值PHP就会使用这个ID创建session文件。这样我们就知道session文件的路径了。4.2 构造payload与绕过正则的细节现在我们来构造具体的payload。首先我们需要发送一个上传请求POST /?file/tmp/sess_abc123 HTTP/1.1 Host: target Cookie: PHPSESSIDabc123 Content-Type: multipart/form-data; boundary----WebKitFormBoundary ------WebKitFormBoundary Content-Disposition: form-data; namePHP_SESSION_UPLOAD_PROGRESS ?php system(cat /flag);? ------WebKitFormBoundary Content-Disposition: form-data; namefile; filenametest.txt test ------WebKitFormBoundary--注意这里的file参数值是/tmp/sess_abc123其中不包含任何被过滤的关键词。tmp、sess、abc123都不在正则的黑名单中。所以这个请求可以通过正则检查。当PHP处理这个请求时会创建session文件/tmp/sess_abc123并将上传进度信息写入其中。上传进度信息中包含了我们设置的文件名?php system(cat /flag);?所以session文件的内容中就包含了PHP代码。然后在同一个请求中include($file)会包含/tmp/sess_abc123执行其中的PHP代码输出flag。但这里有一个时序问题include是在PHP脚本执行时发生的而session文件的写入是在请求处理过程中完成的。如果include执行时session文件还没有写入完成就会包含失败。所以我们需要确保session文件已经写入。实际操作中通常需要发送多次请求或者利用并发来增加成功率。也可以先发送一个请求创建session文件然后再发送包含请求。但session文件在请求结束后可能被删除所以需要在同一个会话中快速完成。4.3 实操中的参数调整与注意事项在实际操作中有几个细节需要特别注意。第一session.upload_progress.enabled必须开启否则这个技巧无效。在CTF环境中通常是开启的但如果是真实环境需要先确认。第二session.save_path决定了session文件的存储路径。如果路径中包含php比如/var/lib/php/sessions那么$file参数中就不能直接写这个路径。这时候可以考虑用/proc/self/fd/来绕过或者寻找其他不包含敏感词的路径。第三session.upload_progress.cleanup如果开启session文件在上传完成后会被立即删除。这时候就需要利用竞争条件在上传完成前包含session文件。通常的做法是发送大量并发请求一边上传一边包含。第四正则匹配的是$file的完整值但PHP在解析路径时可能会对某些字符做处理。比如/tmp/sess_abc123中的下划线、数字都是安全的。但如果session ID中包含特殊字符可能会被正则拦截。所以建议使用纯字母数字的session ID。第五如果/tmp/目录不可写或者session文件不在/tmp/下可以尝试其他路径。比如/var/tmp/、/dev/shm/等。这些路径中都不包含被过滤的关键词。5. 常见问题排查与实战避坑指南5.1 包含失败的原因排查在实际操作中最常见的问题就是包含失败页面没有任何输出。这时候需要逐步排查。首先确认$file参数是否真的通过了正则。可以在本地搭建环境测试或者用简单的payload如/etc/passwd测试是否能包含成功。如果/etc/passwd能包含说明文件包含功能正常问题出在session文件上。然后确认session文件是否真的被创建了。可以通过包含/tmp/目录下的文件来测试但目录包含通常不会列出文件。更好的方法是直接尝试包含一个已知的session文件路径看看是否有内容输出。如果session文件没有被创建检查session.upload_progress.enabled是否开启。可以通过phpinfo()查看或者尝试其他创建临时文件的方法。如果session文件被创建了但包含失败可能是路径不对。不同的PHP配置下session文件的存储路径可能不同。常见的有/tmp/、/var/lib/php/sessions/、/var/lib/php5/等。可以尝试多个路径。5.2 竞争条件的成功率优化如果session.upload_progress.cleanup开启session文件会被立即删除这时候就需要竞争。提高成功率的方法有增加并发请求数量。用多线程同时发送上传和包含请求。延长上传时间。上传一个大文件让session文件存在的时间更长。使用更快的网络连接。减少请求之间的延迟。尝试不同的文件描述符。如果使用/proc/self/fd/需要尝试多个编号。在实际操作中我通常会写一个Python脚本使用多线程同时发送上传和包含请求循环几百次基本都能成功。5.3 常见问题速查表问题现象可能原因解决方法页面无输出包含失败或代码未执行先用/etc/passwd测试包含功能提示Hacker!payload中包含敏感词检查$file参数值确保不含黑名单词session文件不存在upload_progress未开启检查phpinfo或换用其他临时文件方法包含成功但无flag代码未正确执行检查文件内容确保PHP代码被写入竞争失败cleanup开启增加并发延长上传时间路径错误session存储路径不同尝试/tmp/、/var/tmp/、/dev/shm/等5.4 独家避坑经验分享我在做这道题的时候踩过几个坑这里分享出来希望大家能少走弯路。第一个坑是session ID的格式。我一开始用了包含特殊字符的session ID结果session文件路径中出现了被过滤的词导致包含失败。后来改用纯字母数字的ID就成功了。所以建议session ID只用小写字母和数字。第二个坑是请求的构造。multipart/form-data请求的格式非常严格boundary、Content-Disposition、空行都不能出错。我一开始手写请求结果因为少了一个空行导致PHP没有正确解析session文件没有被创建。后来用Burp Suite或者Python的requests库来构造请求就稳定多了。第三个坑是时序问题。我一开始把上传和包含放在两个不同的请求中结果session文件在第一个请求结束后就被删除了第二个请求包含时文件已经不存在。后来改成在同一个请求中完成上传和包含或者用并发请求才成功。第四个坑是路径猜测。不同环境下的session存储路径可能不同我试了好几个路径才找到正确的。建议先用phpinfo()确认或者用/proc/self/fd/来绕过路径限制。6. 从这道题延伸出的Web安全思维这道题虽然只是一道CTF题目但它背后涉及的思维方式在真实的Web安全测试中非常有用。首先是“限制条件下的路径寻找”。当常规路径被堵死时不要死磕而是去寻找那些被忽略的角落。比如/proc/目录下的文件、临时目录、应用自己生成的日志文件等。这些路径往往不在开发者的考虑范围内但恰恰可能成为突破口。其次是“时序竞争”的思维。很多安全漏洞的本质是时序问题在某个资源被删除或失效之前抓住那个短暂的时间窗口。这种思维在文件上传、临时文件、缓存等场景中都非常常见。最后是“信息泄露的利用”。这道题中session文件的内容包含了我们可控的信息这本身就是一种信息泄露。在真实环境中很多看似无害的信息泄露比如错误信息、调试信息、日志文件等都可能被组合利用最终导致代码执行。我个人在做Web安全测试时遇到文件包含点第一反应不是直接尝试伪协议而是先看过滤规则然后思考有没有不经过伪协议就能包含的文件这个文件的内容是否可控路径中是否包含敏感词这种“先分析再动手”的习惯能帮你节省大量时间。另外这道题也提醒我们PHP的配置项对安全性影响巨大。session.upload_progress.enabled、session.upload_progress.cleanup、allow_url_include等配置直接决定了某些攻击手法是否可行。在真实环境中了解目标服务器的PHP配置往往比掌握更多payload更重要。最后再分享一个小技巧在做这类题目时建议在本地搭建一个相同的环境用Docker或者PHP内置服务器都可以。本地环境可以随意修改配置、查看日志、调试代码比在远程盲猜效率高得多。我通常会用php -S localhost:8080快速起一个服务器然后根据题目源码调整配置这样能快速验证思路是否可行。
延伸阅读

更多相关文章

2026/10/17 22:06:32

Hive电商消费行为分析实战:从数据清洗到RFM模型与性能优化

1. 电商消费行为分析到底在分析什么很多人第一次接触"电商消费行为分析"这个命题,脑子里浮现的可能是几张漂亮的折线图和饼图。但真正做过这类项目的人都知道,图表只是最后的呈现,真正花时间的是搞清楚"到底要回答哪些业务问题…

2026/10/17 22:06:32

蒙特卡洛搜索树在自动驾驶行为决策中的工程落地与调优实践

1. 为什么在自动驾驶里聊蒙特卡洛搜索树第一次在自动驾驶规划模块的评审会上听到有人提蒙特卡洛搜索树(MCTS),我的反应是:这不是AlphaGo下围棋用的东西吗,怎么跑到车道上来了。后来真正把一段城区无保护左转的决策逻辑…

2026/10/17 22:01:31

AI浏览器创业窗口:垂直场景与技术服务的切入点

最近“AI浏览器”这个词快被聊烂了。一批带着大模型能力的浏览器产品扎堆上线,融资消息一条接一条,圈内人都在说新一轮杀疯潮已经来了。但真正让我兴奋的,不是谁家浏览器又能自动总结网页,而是创业者在这个节点上的具体机会&#…

2026/10/17 23:26:46

AI Agent在航空管理中的落地实战:四大场景与架构剖析

1. 从“人海战术”到“决策闭环”:AI Agent到底改变了航空管理的什么我到现在还记得那个凌晨两点。某航司运行控制中心(AOC)大厅里,电话声此起彼伏,三条航线因为前站雷雨流控陆续返航,值班签派员一手拿着对…

2026/10/17 23:26:46

四款开源工具:架构图生成、Agent技能库、本地语音处理与模型训练

这周的Github周刊我翻得比平时慢,因为一下子撞见了四个看着就手痒的项目:Archify做架构图生成、一个面向科研场景的Agent技能库、VoiceStudio把语音处理拉回本地,还有MiniMind这个带6400万参数小模型训练框架的开源项目。四个项目方向完全不同…

2026/10/17 23:26:46

AI健康风险评估与Vue社区健康管理系统毕设全栈实战

如果你的毕业设计选题清单里同时出现了“AI”“Vue”和“社区健康管理”这几个词,你大概率和我当初一样纠结:AI到底要做到什么程度才算有工作量?前端做到什么效果才能让答辩老师一眼看出不是套模板?毕设做完之后,论文、…

2026/10/17 23:26:46

LinkedIn风格职业社交模板:语义化结构与容器查询实战

简介:这是一套专为企业招聘与职业社交场景设计的LinkedIn风格网页模板,面向前端初学者与中小企业开发者,用于快速搭建专业、响应式的在线招聘平台原型或轻量级落地页。资源包含56个文件,主体为9个HTML页面(如job-profi…

2026/10/17 23:21:45

长字符串索引怎么加?MySQL前缀/哈希/全文/字典表四方案全解析

给长字符串加索引,见过的人都知道,这不是一条ALTER TABLE ADD INDEX能轻松搞定的事。这种需求在线上出现过不止一次,反正是把我本人困住过好几回。最典型的是处理一张每天新增几十万行、里面全是带参数 URL 的记录表,字段类型是VA…

2026/10/16 5:33:43

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/17 23:05:43

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/17 14:20:58

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/17 0:04:32

PlanarSLAM:面向曼哈顿世界的点线面联合SLAM系统

简介:本资源是面向机器人视觉与SLAM研究者的C开源RGB-D SLAM系统实现,专为结构化室内场景(如办公室、走廊)设计,通过融合点、线、平面三类几何特征并引入“曼哈顿世界”假设,显著提升位姿跟踪鲁棒性与稠密地…

2026/10/17 0:04:32

TDH Workflow调度实战:从DAG依赖驱动到任务编排与故障排查

1. 从一个调度脚本说起:TDH Workflow到底在解决什么问题第一次接触TDH的Workflow,是在一个数据清洗项目里。当时团队每天要跑几十个ETL任务,任务之间有严格的先后依赖——A表清洗完才能跑B表的关联,B表跑完才能触发C表的聚合。最开…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑