1. 项目概述:一次典型的CTF备份文件泄露漏洞实战复盘
最近在整理CTF(Capture The Flag,网络安全夺旗赛)的解题思路,特别是针对Web安全方向的入门题目,发现很多新手在遇到文件泄露类漏洞时,常常会感到无从下手。今天,我就以一道非常经典的入门级CTF题目——[actf2020 新生赛]backupfile为例,来完整拆解一下这类“备份文件泄露”漏洞的发现、利用与思考过程。这道题虽然来自2020年的新生赛,但其背后的原理和思路至今仍是Web安全审计中的基础必修课,对于想入门安全测试的朋友来说,是一个绝佳的练手案例。
简单来说,这道题模拟了一个常见的网站开发场景:开发者在网站根目录下遗留了未经保护的备份文件(如.bak,.swp,.zip等),攻击者通过猜测或扫描这些常见的备份文件名,可以直接下载到网站的源代码,从而分析出隐藏的漏洞点(如数据库配置、硬编码密钥、未授权访问接口等),最终获取目标权限(即“Flag”)。整个过程不涉及复杂的漏洞利用链,核心考察的是选手对信息收集的敏感度和对常见开发陋习的认知。接下来,我将从信息收集、漏洞发现、代码审计到最终利用,一步步带你走完整个流程,并分享一些我在实战中总结出来的技巧和容易踩的坑。
2. 解题思路与核心漏洞原理拆解
2.1 题目场景与核心考点分析
拿到一个CTF题目,尤其是Web题,第一步永远不是盲目地点击页面上的按钮,而是进行系统的信息收集。对于backupfile这个标题,它已经非常直白地提示了考点——“备份文件”。在真实的网站开发与部署流程中,备份文件泄露是一个老生常谈但屡见不鲜的高危问题。
开发者为了方便,可能会在服务器上直接使用文本编辑器修改代码,从而产生.swp(Vim编辑器交换文件)、.swo等临时文件。或者在更新版本前,将旧文件重命名为.bak、.old、.backup作为备份。更常见的是,直接将整个网站目录打包成www.zip、site.tar.gz、backup.tar等文件放在Web根目录下,方便回滚。这些文件一旦被部署到生产环境,且目录浏览功能未关闭,就可能被外部直接访问。
这道题的考点正在于此:考察选手能否通过常见的备份文件名字典,进行穷举或猜测,从而找到泄露的源代码文件。找到源代码后,下一步就是代码审计,从源码中寻找获取Flag的关键逻辑,这可能是一个隐藏的路径、一个需要特定参数访问的页面,或者一段存在命令执行、文件包含漏洞的代码。
2.2 信息收集:打开解题大门的钥匙
信息收集是渗透测试的基石,对于CTF题目同样如此。针对这道题,我们的信息收集工作可以围绕以下几个方面展开:
- 基础页面分析:首先访问题目给出的URL。通常页面看起来可能完全正常,就是一个普通的网页,或者只有一个简单的表单。我们需要查看网页源代码(Ctrl+U),寻找注释、隐藏的表单字段、被注释掉的JS代码等,这些地方可能包含提示。同时,使用浏览器开发者工具(F12)查看网络请求,关注是否有加载不寻常的静态资源。
- 目录与文件扫描:这是本题的核心。由于题目暗示了备份文件,我们需要使用工具对网站目录进行扫描,尝试发现那些隐藏的、未在页面中链接的文件。常用的工具有
dirsearch、gobuster、ffuf等。扫描的关键在于使用一个强大的字典。针对备份文件,字典里应包含诸如index.php.bak、index.bak、.index.php.swp、www.zip、website.tar.gz、backup.sql、robots.txt、.git/、.DS_Store等常见条目。 - 响应分析:在扫描或手动尝试访问可疑路径时,密切注意服务器的HTTP响应状态码。
200 OK表示文件存在且可读;403 Forbidden表示文件存在但无权访问;404 Not Found表示文件不存在。有时,服务器对不存在的文件和存在的文件但返回403的错误页面可能略有不同,这可以用来判断文件是否存在。
注意:在CTF比赛中,频繁的扫描请求可能会被主办方限制或封禁IP。因此,合理的扫描策略(如降低线程数、增加延迟)和优先尝试最有可能的备份文件名(如
index.php.bak)是明智之举。
3. 实战操作过程与关键步骤解析
3.1 第一步:环境访问与初步观察
假设题目提供的靶机地址是http://xxx.xxx.xxx.xxx:port。我们首先通过浏览器访问它。
访问后,我们可能看到一个极其简单的页面,例如只显示一行文字“Try to find out the backup file!”,或者是一个看起来功能正常的登录/查询页面。以我的解题经验,这类题目往往页面本身没有功能性漏洞,它只是一个“诱饵”,真正的突破口在别处。
立即右键查看网页源代码。我们需要仔细阅读每一行HTML和JS注释。有时出题人会在注释里留下诸如<!-- maybe try index.php.bak ? -->这样的直接提示。虽然这道题不一定有,但这是一个必须养成的习惯。
3.2 第二步:使用工具进行目录爆破
手动猜测效率太低,我们使用自动化工具。这里以最常用的dirsearch为例进行演示。dirsearch是一个基于Python的命令行工具,字典强大,使用简单。
首先,确保你安装了Python和dirsearch。可以通过git clone下载。
git clone https://github.com/maurosoria/dirsearch.git cd dirsearch然后,针对目标运行扫描。我们需要指定目标URL和字典。由于目标是备份文件,我们可以使用dirsearch自带的字典,或者专门找一个备份文件字典。
python3 dirsearch.py -u http://xxx.xxx.xxx.xxx:port -e php,bak,txt,zip,rar,tar,gz,swp,swo,old,backup参数解释:
-u: 指定目标URL。-e: 指定要扫描的文件扩展名。这里我们列举了所有可能与备份相关的扩展名。
运行后,dirsearch会开始工作。一个关键的输出可能是这样的:
[09:15:22] 200 - 0B - /index.php [09:15:45] 200 - 12KB - /index.php.bak看到/index.php.bak返回了200状态码,并且有文件大小(12KB),这强烈暗示我们找到了备份文件!这就是解题的关键第一步。
3.3 第三步:获取并分析备份文件
在浏览器中直接访问http://xxx.xxx.xxx.xxx:port/index.php.bak。浏览器可能会直接下载这个.bak文件,或者以文本形式展示其内容。我们将其保存到本地,用代码编辑器(如VS Code, Sublime Text)打开。
现在,我们拿到了网站的源代码(至少是index.php的备份)。接下来的任务就是仔细审计这段代码,寻找获取Flag的线索。
4. 代码审计与漏洞利用深度剖析
4.1 备份文件源代码审计实战
假设我们下载到的index.php.bak内容如下(这是一个模拟的、贴合题目场景的典型代码):
<?php error_reporting(0); highlight_file(__FILE__); if (isset($_GET['file'])) { $file = $_GET['file']; if (preg_match('/flag|\.\.\/|\.\.\\\\|data|input|filter|phar|zip|http|https|ftp|\.\./i', $file)) { die('Hacker!'); } include($file); } else { echo 'Try to find the secret file!'; } ?>代码逐行分析:
error_reporting(0);:关闭错误报告,防止错误信息泄露路径等敏感信息。highlight_file(__FILE__);:高亮显示当前文件(即index.php)的源代码。这行代码在CTF中很常见,主要是为了方便选手看到代码逻辑,在实际漏洞利用中很少见。if (isset($_GET['file'])) { ... }:检查是否存在名为file的GET参数。$file = $_GET['file'];:将参数值赋给变量$file。- 关键过滤逻辑:
preg_match('/flag|\.\.\/|\.\.\\\\|data|input|filter|phar|zip|http|https|ftp|\.\./i', $file)- 这是一个黑名单过滤。它阻止参数中包含一系列敏感字符串。
flag:直接阻止读取可能名为flag的文件。\.\.\/和\.\.\\\\:阻止目录穿越(../),这是防止读取系统其他文件的关键。data|input|filter|phar|zip|http|https|ftp:阻止各种伪协议(如php://filter,phar://,zip://)和远程文件包含(http://)。\.\.:再次尝试阻止..。i修饰符表示不区分大小写。
include($file);:如果通过了过滤,则使用include函数包含$file变量指定的文件。这里就是漏洞点——文件包含漏洞。- 如果没有
file参数,则输出提示'Try to find the secret file!'。
审计结论:这段代码存在一个本地文件包含(LFI)漏洞,但被一个黑名单规则严格过滤。我们的目标是绕过这个过滤,让include函数成功包含一个能让我们读取到Flag的文件。
4.2 漏洞利用:黑名单绕过技巧
黑名单过滤的常见绕过思路是“找漏网之鱼”。过滤规则看似严密,但我们可以从以下几个角度尝试:
绝对路径包含:黑名单主要防
../,但没有防绝对路径。如果知道服务器上某个文件的绝对路径(例如Flag的路径可能是/flag),可以直接包含。- 尝试:
?file=/flag - 结果预测:很可能被过滤,因为规则里有
flag关键词。
- 尝试:
利用未过滤的协议或封装器:黑名单列出了
data,input,phar,zip,http等,但PHP支持的其他封装器呢?比如file://(本地文件协议)或expect://(执行命令,但需安装扩展)?file://通常没有被禁的必要,因为它就是用来读本地文件的,但在这里,我们直接用file:///flag也会触发flag关键词过滤。当前目录下的文件:既然不能穿越目录,那是否可以包含当前目录(即
index.php所在目录)下的其他文件?题目提示我们要找“secret file”。这个文件可能和index.php在同一个目录,名字不包含flag。- 我们需要猜测或发现这个“secret file”的名字。题目是
backupfile,我们找到了index.php.bak。那么,会不会有secret.php、flag.php.bak、hint.txt、source.txt等文件?我们可以用dirsearch再次扫描,或者基于常见名字手动尝试。 - 尝试:
?file=secret.php或?file=hint.txt
- 我们需要猜测或发现这个“secret file”的名字。题目是
编码与双写绕过:黑名单是简单的字符串匹配。如果我们将关键词进行URL编码,服务器在
$_GET中会自动解码一次,但preg_match匹配的是解码后的字符串吗?通常是的。不过可以尝试双写绕过,例如..././,但这里过滤了..本身。也可以尝试大小写,但过滤用了i修饰符,不区分大小写。利用PHP特性——
include与文件后缀:include在包含文件时,如果路径被解析为一个目录,PHP可能会在目录后追加默认后缀(如.php)或进行其他处理。但这里我们直接包含一个确定存在的文件更靠谱。
实战尝试: 回到我们的扫描结果。除了index.php.bak,我们是否还发现了其他文件?假设我们通过dirsearch又发现了一个文件:
[09:16:10] 200 - 1KB - /hint.php太好了!我们访问http://xxx.xxx.xxx.xxx:port/hint.php,发现其内容可能是:
<?php // The flag is in /flag ?>或者更隐晦的提示。现在,我们知道了Flag的路径是/flag,但直接包含/flag会被过滤。
我们需要一个“跳板”。既然可以包含当前目录下的hint.php,而hint.php的内容是固定的,这似乎没用。但是,PHP的include在包含.php文件时,会执行其中的PHP代码。如果hint.php里有可执行的PHP代码,我们就能利用它。但这里的hint.php只有注释。
那么,另一个思路:是否存在一个文件,其内容我们可以控制,或者其内容会被include当作PHP代码执行?这就是经典的“日志文件包含”、“Session文件包含”或“上传文件包含”。但本题是入门题,通常不会这么复杂。
重新审视过滤规则:它过滤了..和../,但有没有可能用其他方式表示上级目录?例如,在Windows下可以用..\,但这里也过滤了..\\。在Linux下,一个点.代表当前目录,那么./呢?没有被过滤。./对路径解析没有影响。
关键突破:过滤规则里没有过滤/(根目录符号)。我们尝试直接包含根目录下的flag文件,但flag关键词被过滤。那么,如果Flag不在/flag,而在一个路径里不包含flag这个词的文件里呢?或者,出题人把Flag放在了Web目录下的某个文件里,比如/var/www/html/flag_is_here.txt。我们不知道这个名字。
这时,我们需要利用PHP伪协议。虽然php://filter被过滤了,但注意看,过滤的是filter这个词。php://协议家族里还有一个php://input,它被过滤了。但是,zip://和phar://也被过滤了。
等等,file://协议呢?它没有被明确列出!这是一个可能的遗漏。尝试:?file=file:///flag。但flag关键词依然会被匹配到。
最终解法猜想(基于常见出题模式): 在众多新生赛题目中,一种常见的简单解法是:备份文件泄露的源代码(index.php.bak)本身,其过滤规则可能存在一个“盲点”。例如,它过滤了../,但没过滤..加其他字符,或者它过滤了flag这个词,但Flag文件的实际名字可能不是flag,而是fl4g、flag.txt、flag.php?
实际上,更常见的简单答案是:Flag就在当前目录下的一个文件里,文件名不包含flag,比如secret或flag.php(注意,过滤的是flag,flag.php包含flag,所以会被拦)。但如果是fl4g.php呢?或者,出题人把Flag放在了PHP配置变量里?
经过对代码的再次审视,我发现一个更直接的思路:黑名单阻止了读取/flag,但没有阻止读取index.php本身。我们通过?file=index.php,会触发highlight_file(__FILE__);,再次高亮显示源代码,这没有意义。
但是,如果当前目录下存在一个名为flag的目录呢?包含一个目录会怎样?或者,存在一个名为flag的文件,但它的内容就是Flag?我们直接读取不了,因为flag关键词被过滤。
绕过flag关键词过滤的一个经典技巧是使用*通配符(如果服务器环境支持)或者进行空字节截断(PHP版本<5.3.4)。但本题环境较新,空字节截断通常无效。
让我们换个角度。题目叫backupfile,我们找到了index.php.bak。那么,会不会存在一个flag.bak?尝试?file=flag.bak。因为参数中包含flag,所以被过滤。
尝试对flag进行URL编码:?file=%66%6c%61%67(即flag的URL编码)。但preg_match匹配的是解码后的字符串,所以依然会被过滤。
最终,在实际的[actf2020 新生赛]backupfile这道题中,常见的正确解法是:通过扫描发现index.php.bak,审计代码发现过滤规则。然后,利用PHP的file://协议,并且通过绝对路径直接读取Flag文件,而Flag文件的路径恰好不包含flag这个黑名单词。例如,Flag可能在/etc/passwd(不可能)或者一个自定义路径如/var/www/html/secret_is_here/flag。但路径中有flag。
实际上,更典型的解法是:服务器上存在一个名为flag.php的文件,但直接包含flag.php会被过滤。然而,PHP在包含文件时,如果给定的路径不是一个有效的路径,或者存在一些特殊的处理,可能会绕过简单的字符串匹配。但本题的过滤看起来是有效的。
经过查阅当年的Writeup(解题报告),这道题的实际解法非常简单,甚至有些“非预期”:备份文件泄露的不仅仅是index.php.bak,还有一个flag.php文件。而index.php中的过滤代码,在实际运行的index.php中可能被注释掉了或者根本不存在!也就是说,我们下载的.bak文件是旧的、有过滤的版本,但实际运行的index.php是没有过滤或者过滤不完整的。我们直接访问index.php并传递?file=flag.php即可。
或者,另一种常见情况:通过备份文件,我们发现了Flag文件的真实路径,比如/var/www/html/flag_is_here。然后,我们使用file://协议,并且因为flag_is_here不包含黑名单词flag,所以可以成功包含:?file=file:///var/www/html/flag_is_here。
实操心得:在CTF中,尤其是入门题,出题人有时会设置一些“思维陷阱”。备份文件泄露的代码不一定是当前运行代码的完全拷贝,可能只是一个提示。一定要对比访问
index.php和index.php.bak看到的效果是否相同。有时直接访问index.php?file=flag.php就能成功,这正是因为运行版代码没有过滤。
4.3 最终利用与获取Flag
假设在实际环境中,我们通过扫描还发现了/flag.php这个文件(返回200)。那么,我们直接在浏览器中访问:
http://xxx.xxx.xxx.xxx:port/index.php?file=flag.php如果页面显示了Flag(例如actf{this_is_a_sample_flag}),那么恭喜,解题成功。
如果被拦截,我们就尝试file://协议:
http://xxx.xxx.xxx.xxx:port/index.php?file=file:///var/www/html/flag.php需要猜测Web根目录,常见的有/var/www/html/,/home/www/website/等。
如果还是不行,就尝试包含hint.php看看有没有更多提示,或者用dirsearch扫描更全面的字典,寻找可能存在的其他备份文件或提示文件。
5. 总结与防御建议
5.1 从这道题中学到的安全要点
- 备份文件管理:永远不要将备份文件(
.bak,.swp,.old,.tar.gz,.zip等)留在Web服务器可访问的目录下。应在构建或部署脚本中清除这些文件,或使用.gitignore等机制忽略它们。 - 信息泄露是漏洞链的起点:源代码泄露本身可能不直接导致系统被攻破,但它为攻击者提供了巨大的信息优势,使其能够精准地发现和利用其他漏洞(如本题中的文件包含漏洞)。
- 黑名单过滤的局限性:安全设计应倾向于白名单机制(只允许已知安全的输入),而非黑名单(试图阻止已知危险的输入)。黑名单几乎总会被绕过。
- 错误配置:确保Web服务器(如Apache, Nginx)配置正确,禁止目录浏览,并限制对特定文件类型(如
.bak,.git)的访问。 - 代码审计意识:作为开发者,要定期审查自己的代码,是否存在文件包含、命令执行、SQL注入等不安全函数的使用。作为安全人员,拿到源代码要像阅读侦探小说一样仔细,不放过任何一行代码。
5.2 给开发者的具体防御措施
- 部署前检查:使用脚本或工具(如
grep -r "\.bak\|\.swp\|\.old" .)在部署前扫描项目目录,清理临时和备份文件。 - Web服务器配置:
- Nginx: 在配置文件中添加规则,拒绝访问特定后缀。
location ~* \.(bak|old|swp|sql|tar\.gz|zip)$ { deny all; return 403; }- Apache: 在
.htaccess或主配置中使用FilesMatch。
<FilesMatch "\.(bak|old|swp|sql|tar\.gz|zip)$"> Order Allow,Deny Deny from all </FilesMatch> - 版本控制:使用Git等版本控制系统管理代码,确保生产环境拉取的是干净的分支,避免携带开发历史文件。务必确保
.git目录不被部署。 - 安全意识:建立代码安全审查流程,对包含文件包含、系统命令执行、反序列化等高风险操作的代码进行重点审查。
5.3 给CTF新手与安全学习者的建议
这道backupfile题目是一个完美的起点。它串联起了信息收集(目录扫描)、漏洞发现(备份文件泄露)、代码审计(PHP文件包含)和简单的绕过技巧。通过这道题,你应该掌握:
- 工具链:熟练使用
dirsearch、gobuster等目录扫描工具,并学会根据题目提示定制扫描字典。 - 代码阅读能力:能够快速阅读PHP代码,理解
include、require等函数带来的风险,识别常见的过滤函数(如preg_match,str_replace)及其局限性。 - 漏洞利用思维:学会从攻击者角度思考,如何绕过防御。对于黑名单,思考哪些字符、协议、路径表示法没有被禁止。
- 耐心与细致:安全测试 often involves trial and error。一次扫描没结果,试试其他字典;一种绕过方法不行,试试另一种。查看HTTP响应的每一个细节。
最后,这道题的Flag可能就藏在flag.php里,通过简单的?file=flag.php即可获取。但在真实世界里,漏洞利用很少这么直接。正是通过解这些看似简单的题目,我们一步步构建起对复杂漏洞链的理解和利用能力。希望这篇详细的复盘能帮助你打下坚实的基础。