PolarCTF 2026春季赛复盘:命令执行、SQL注入与沙箱逃逸实战

PolarCTF 2026春季赛我完整打了下来,本来只想抽空做两道Web题热身,结果从上午一直坐到傍晚。这次比赛整体不刁钻,但题目覆盖面比我想象中广,从命令执行到SQL注入绕过,再到Node沙箱逃逸,甚至还有一道把日志分析和隐写串在一起的应急响应题。很多题目单看不难,真正做题时却会在各种小细节上卡住。这篇文章不是官方题解,而是我以参赛者身份做的一次完整复盘,重点挑几道让我印象深刻的题目,把现场一步步的尝试、绕过的思路和最后真正奏效的payload都还原出来。

如果你准备入CTF的坑,或者已经开始打比赛但经常在Web题上卡两三个小时,这篇WP应该比你去翻零散的教程更实用。我会尽量把"为什么这样做"讲透,而不只是贴结果。老规矩,所有操作都在PolarCTF的授权靶场环境里完成,思路用来学习没问题,别拿去做真实站点的测试。

1. 这次比赛的节奏和难度,先给没参加的人交代清楚

1.1 总的来说,是一场"新手友好、进阶有料"的比赛

PolarCTF的题目配置我比较熟悉,一直走的是常态化在线CTF路线,难度曲线不是突然拉高,而是从签到题一路阶梯式往上走。2026春季赛给我的感觉是:Web方向占了大头,Misc杂项紧跟其后,Pwn和Reverse也有,但数量没有Web那么多。

我自己的主攻方向是Web和Misc,所以这次复盘也集中在这两个方向。Web题里,命令执行和SQL注入的出场率很高,而且不是简单问你"会不会用system函数",而是加了各种过滤条件逼你绕。Misc里那道应急响应风格的题目也比较有意思,给了大量日志和一个所谓的"事故现场",需要自己从日志里逆向出攻击者的操作链条。

整场比赛做下来,我最大的体感是:大多数题目不需要你有多冷门的技巧,只需要把基本功打扎实,并且愿意在报错信息、过滤规则和页面回显里反复抠细节。

赛题整体区分度做得不错。面向新手的题目,只需要能理解基本的HTTP请求和PHP代码就能做;但想要高分,就必须掌握完整的绕过思路和工具使用。对于正在准备入门CTF的青少年选手来说,这样的难度梯度其实挺友好,不会被第一题直接劝退,又能通过后面的题目看到自己和别人的差距。

1.2 我在答题时间分配上踩的一个坑

我自己这次的时间分配是有问题的。开赛后一上来就冲Web题,连续解决了两道觉得难度不高的命令执行和SQL注入,瞬间有点飘。结果在一道Node VM沙箱题上耗了将近两个小时,中间反复试错,甚至一度怀疑是自己环境问题。

后来复盘发现,那道题其实在Web题里属于中高难度,最优策略应该是先放一放,把Misc题里能拿的分先拿到。CTF比的不是单题突破能力,而是单位时间内拿到的总分。如果一场比赛只能总结一条经验,我会说是合理分配时间,比死磕某一题重要得多。后面我会单独开一节讲我在时间安排上的调整思路,以及针对新手选手的答题建议。

1.3 这篇文章适合谁读

如果你是第一次听说CTF,担心自己基础不够,这篇文章同样可以看。每道题我会从题目到底想考什么开始分析,而不是上来就甩一条exp。比如命令执行题,我会先解释为什么网站会存在这种漏洞,服务端如果没有做过滤会发生什么;再讲到出题人加了黑名单之后,用哪些手法可以绕过。

这样读下来,就算你还没有完全掌握PHP和JavaScript语法,也能理解漏洞的成因和利用思路。CTF入门的核心不是背题,而是培养"看到一段代码,能推断出它哪里可能出问题"的思维能力。

2. 命令执行题:小写过滤、空格过滤,怎么一步步还原出完整命令

2.1 题目入口与最初的低级试探

这道题的名字我记得叫polar_easy_rce,入口是一个PHP页面,URL里存在明显的cmd参数。我用浏览器直接访问:

/index.php?cmd=ls

页面没有任何输出,也没有报错。我一开始以为是参数名不对,试了command、exec等参数名,全部没反应。后来查看页面源码,发现HTML注释里写了一句:

// system($_GET['cmd']);

这就清楚了,服务端很可能是直接用system()执行了传入的命令,只是过滤规则把输出吞掉或者做了拦截。目标就是找到过滤规则,然后构造一条合法命令读取flag。

这类题目常见的flag位置是根目录或当前目录下的文件,文件名的命名有时候会故意避开关键词,比如fl4g.txt、1_flag等。为了先确认过滤规则,我尝试了:

/index.php?cmd=cat+/etc/passwd

仍然没有任何输出。到这里我意识到,过滤很可能不是针对输出来做的,而是直接在参数里检查敏感字符,一旦触发就die()掉,页面表现为空白。

2.2 真正卡住我的点:空格和关键词双过滤

为了确认拦截规则,我做了几组对照测试。先试一个纯字母命令:

/index.php?cmd=id

页面正常输出了uid=33(www-data),说明命令执行本身没问题。接着试:

/index.php?cmd=cat+/etc/passwd

空白;再试:

/index.php?cmd=cat+/etc/hosts

也是空白。我怀疑是cat被拦了,于是试:

/index.php?cmd=tac+/etc/passwd

居然也是空白。到这里才算意识到,问题很可能出在空格字符上,而不是命令名。PHP收到URL里的+在某些场景下会解码成空格,但题目可能连空格本身或者+都做了处理。

我把命令换成cat读取当前目录文件,尝试不加空格直接用重定向符:

/index.php?cmd=cat/passwd

依然没有回显。那说明cat这个关键词也被列入黑名单了。

到了这一步,解题方向就很明确了,需要同时绕过两个规则:空格和命令名关键词。此类命令执行题,出题人最常见的思路就是把最容易想到的几个关键词和符号拉进黑名单,但永远无法把Linux下所有等价写法都拦住。

2.3 绕过的完整过程与最终payload

先解决空格问题。Linux shell中,空格不只是唯一的单词分隔符,还能用${IFS}来替代。IFS是shell的内部字段分隔符,默认就包含空格、制表符和换行符。所以我尝试:

/index.php?cmd=cat${IFS}/etc/passwd

但还是空白。这里我踩了个坑,忘了在URL里$、{、}需要URL编码,不然服务端收到后可能变成别的内容。正确写法是:

/index.php?cmd=cat%24%7BIFS%7D/etc/passwd

其中%24是$的URL编码,%7B和%7D分别是{和}。

输出终于出来了,说明${IFS}能成功绕过空格过滤。接着解决cat被过滤的问题。Linux下有多个可以查看文件的命令,包括tac、more、less、head、tail、nl等。tac从文件末尾开始输出,对于读取flag这种单行文本来说效果完全相同:

/index.php?cmd=tac%24%7BIFS%7D/fl4g.txt

flag出来了。但我后来看其他选手的WP,发现这题其实还有更省事的解法,因为黑名单大概率只针对精确关键词,没有考虑shell通配符。直接用tac加通配符读取,连文件名都可以猜:

/index.php?cmd=tac%24%7BIFS%7D/f*

*会匹配当前目录下以f开头的所有文件名。这道题给我的教训很直接:先定位是哪一个字符触发了拦截,再针对性找替代品。很多人包括我一开始自己,一上来就堆payload,试了都不对,白白浪费好几分钟。

2.4 如果命令没有回显,该怎么处理

有的命令执行题会比这道题更狠,执行了命令但页面不输出任何结果,也就是无回显RCE。碰到这种场景,还有个思路是让命令结果落到一个我们可以再访问的文件里,比如:

/index.php?cmd=ls${IFS}/>${IFS}/tmp/z.txt

然后用另一个接口或路径去读取/tmp/z.txt。如果是需要访问外网的环境,也可以考虑通过DNS或HTTP请求把结果带出来,但比赛环境往往不做外连,所以把结果写入当前目录下的静态文件是更稳妥的选择。

这是命令执行题里一个非常重要的通用技巧,无论题目过滤了什么,核心逻辑都是:能不能把命令的执行结果以我们能接收的方式拿回来。如果页面直接回显,那最简单;如果没回显,就主动"造"一个回显。

3. 登录框SQL注入:从万能密码到正则过滤绕过

3.1 这道题比想象中更考验细节

比赛里有一道登录题,只给了一个用户名和密码输入框,没有任何提示。我习惯性地在用户名框输入了:

admin' or 1=1 -- -

结果页面提示"用户名或密码错误",没有SQL报错,也没有被拦截的提示。一开始我判断可能是用户名被MD5处理了或者后端用了参数化查询,差点就把这道题放弃了。

后来我试着单独在用户名处输入一个单引号:

admin'

页面回显变成了"SQL语句执行异常"。这个回显差别很关键,说明单引号能够影响到SQL语句的结构,注入点是存在的,只是我之前构造的语句没有成功匹配登录逻辑。

这种"报错不分字段"的场景,其实是SQL注入题最容易误导人的地方。页面不会告诉你拼接后的SQL长什么样,只能靠自己去推断后端代码的大致结构。

3.2 猜测后端结构,构造可用的永真式

把单引号引发的异常当成线索,我推断出后端代码大概是:

$username = $_POST['username']; $password = $_POST['password']; $sql = "select * from users where username='$username' and password='$password'"; $result = mysqli_query($conn, $sql); if (mysqli_num_rows($result) > 0) { // 登录成功 }

这样的代码在现代真实业务里基本绝迹,但CTF登录题十道里有八道是这样。如果构造admin' or 1=1 -- -,SQL变成:

select * from users where username='admin' or 1=1 -- -' and password='xxx'

从逻辑上讲应该能登录,但页面没有反应,说明空格或or、--这类字符在后端被过滤了。我没有办法看到黑名单的长度,只能逐个尝试。

一试发现,or被过滤了,and也被过滤了,空格和--同样没放过。但有过滤不代表没办法,如果页面在后端做的是字符黑名单匹配,而不是正则严格校验,||这个运算符往往会被漏掉。在MySQL默认行为下,||就是逻辑或的等价写法。

于是我尝试在密码框输入:

a'||'1'='1

用户名保持不变为admin。拼接后的SQL是:

select * from users where username='admin' and password='a'||'1'='1'

等价于admin AND ('1'='1'),这句话永远为真,因为密码框任意字符串与'1'拼接后,||左边是false(除非恰好是"1"),右边是true,整体为true。于是成功以admin身份登录。

这个手法的巧妙之处在于不需要注释符,也不依赖空格,适合在关键词黑名单严格到极致的时候绕。当然,如果在更极端的场景下连||也被过滤,还可以用字符拼接的方式构造or,比如在MySQL里用in、like等替代。

3.3 为什么这类题至今仍是CTF主流题

可能有新手会问,都2026年了,为什么比赛还在考SQL注入?难道是出题人跟不上时代?

恰恰相反,SQL注入之所以长期存在,是因为它考察的是开发者对"数据和代码是否混淆"的理解。这个问题今天依然存在于大量拼接SQL的遗留系统、报表系统、搜索功能里,只是攻击面从网站登录框转移到了更底层的数据处理模块。登录框里的注入只是入口,真正危险的是注入之后能否通过union select拖出整个库、能否通过into outfile写入webshell。CTF保留这类题,本质上是训练选手从一个入口思考整条链路的能力。

从防御角度来讲,解法也很清晰。只要把用户输入交给参数化查询处理,而不是直接拼进SQL字符串,整类问题基本就堵死了。比如用PHP的PDO预处理:

$stmt = $pdo->prepare("select * from users where username = ? and password = ?"); $stmt->execute([$username, $password]);

哪怕用户输入再刁钻的内容,也只会被当成字符串处理。强调一下,这也是我每次复盘SQL题都会提醒自己的点:比赛里学绕过,是为了在真实开发时知道为什么必须用预处理。

3.4 登录注入的一个小分支:报错信息利用

回到比赛,我后来顺手把这道题的数据库信息也拖了出来,因为登录成功之后,页面URL里多了一个id参数,同样存在注入。这次是有报错回显的,我优先用了updatexml报错注入。在MySQL中,UPDATEXML函数第二个参数如果不是合法XPath路径,就会把内容带进报错信息里。利用方式:

?id=1 and updatexml(1, concat(0x7e, database()), 1)

前提是站点的sql_mode允许这种写法,并且页面把MySQL错误原样输出。比赛中经常能看到这种"登录注入过了、但真正的数据读取在另一个参数点"的设计,所以不要以为拿到登录权限就结束了,题目想考的往往是后面的二次注入。

4. Node VM沙箱题:比赛里最费时间的一道题

4.1 看到"沙箱"两个字,我就知道事情不简单

这道Web题入口很简单,提交一段JavaScript代码,服务端会执行并返回结果。页面提示说代码运行在安全的Node.js VM沙箱中,只能使用白名单里的少量功能。我一开始试了:

process.mainModule.require('fs').readFileSync('/flag', 'utf-8')

结果直接报错process is not defined。这里其实暴露了它的实现方式:代码并没有运行在Node主进程的全局环境里,而是被丢进了一个独立的上下文,常见的process、require、Buffer等全局对象都拿不到。

这类题核心考察点就是:你以为沙箱隔离了代码,但JavaScript的语言特性可能早就留了后门。在看题目源码之前,先说一下我当时的基本判断。如果平台用的是Node自带的vm模块,而不是进程级隔离方案,那么沙箱内的代码在执行时,仍然可以访问到一些通过"原型链"或"Function构造器"穿透出来的对象。CTF历史上大量Node沙箱题都是栽在this身上。

4.2 从this到Function构造器,绕回主上下文的完整链路

既然直接的process访问不了,下一步就是在沙箱里找对象。我提交了:

this

执行结果返回了[object Object],说明当前作用域的this指向一个对象,而且沙箱代码默认不是严格模式。在非严格模式下,顶层this指向的是当前上下文里的global对象。global对象本身也是对象,它有构造函数Object,而Object对象的构造函数是Function。

关键在于,JavaScript里几乎所有构造函数都有一个constructor属性,一路向上最终会到达Function构造器。Function构造器创建的代码执行环境不再是沙箱,而是Node主上下文。我沿着这条链试了:

this.constructor.constructor('return process')()

页面果然返回了process对象的相关信息。拿到process之后,接下来就相对常规了,读取文件的途径有很多种,我当时用的是:

this.constructor.constructor('return process')() .mainModule.require('fs') .readFileSync('flag.txt', 'utf-8')

由于考场里的Node版本不算新,process.mainModule依然可用,所以一次就通了。后来我在自己本机的Node 20环境复测时发现mainModule已经变成undefined,这也是很多老经验在新版本Node上失效的原因,做题时不能死记payload,得理解每一步为什么能拿到那个对象。

这道题总结下来,考点可以压缩成一句话:vm模块创建的不是安全边界,只是一个新的上下文。只要代码还能拿到Function构造器,它就能回到主环境执行任意代码。CTF里所谓沙箱逃逸,本质是在问:沙箱给你提供的那几个对象里,有没有通往主环境的"隐藏引用"。

4.3 如果出题人加了Proxy或缓存保护,该怎么办

比赛结束后我特意去看了出题人公布的源码,发现我在场上提交的this方式正好是在预期解内,但出题人做了一些过滤:如果提交的代码里出现require、process、Buffer这些字符串,会先被拦截。可拦截字符串拦不住this.constructor.constructor,因为构造出来的代码是运行时生成的,静态过滤能看到"return process"里的process吗?其实也能看到,所以出题人没有把黑名单做得很绝对,否则这题难度还要上去一截。

假如真的遇到更严格的沙箱,比如用Object.create(null)做sandbox、或者用Proxy拦截了constructor属性,那就要考虑别的路子,比如从Error对象的stack里提取信息,或者利用async函数、generator等对象的内部结构寻找process。更极端的情况下还可以考虑WebAssembly对象逃逸。

但现实场景下,如果产品想在服务端执行不信任代码,最稳妥的方案永远不是去加固vm沙箱,而是把代码丢进真正的容器或子进程里做资源隔离。Node自带的vm模块在设计上就不是为了应对恶意代码,比赛里反复考它,其实是提醒开发者别把安全边界寄托在一个标准库上。

5. 文件读取与源码泄露:一次备份文件引发的连锁解

5.1 目录扫描发现的backup.zip

这道题入口是一个文件查看页面,URL长这样:

/index.php?page=welcome

页面会根据page参数加载不同的模板内容,点其他菜单时参数会变成about、contact。很典型的文件包含类题目,目标应该是通过控制page参数读取服务端文件。

我没有急着在参数上做文章,先用目录扫描工具跑了一下常见备份文件和目录,很快就发现根目录下有一个backup.zip。这里也提一句,CTF里发现备份文件会让题目难度直线下降,所以养成"开题先扫目录"的习惯很关键。工具方面,常见的御剑这类目录扫描工具用来做初步探测完全够用,但也别只依赖字典,有时候.git目录、.svn目录泄露同样值得手动检查。

下载并解压backup.zip后,里面是完整的PHP源码,这不只是给了你一把钥匙,而是把整个保险柜都打开了。我开始逐个看源码,寻找page参数在后端是怎么被处理的。

5.2 利用php://filter读取源码文件

源码中index.php的关键代码大概是:

$page = $_GET['page']; include($page . '.php');

看到include并且参数直接可控,这就是经典的本地文件包含。但因为拼接了.php后缀,直接传page=../../etc/passwd没有意义,除非目标环境支持截断,而在现代PHP版本中,空字节截断和超长路径截断早就失效了。

我先尝试读取同目录下的welcome.php源码:

/index.php?page=php://filter/convert.base64-encode/resource=welcome

因为拼接后是welcome.php,所以resource=welcome最终会被解析为welcome.php,并经过convert.base64-encode过滤器,把PHP源码以base64形式输出。这样就算文件包含执行了PHP代码,也不会直接显示源码,而是先被base64编码,绕过了PHP解析。

页面返回了一串base64字符串,解码后就是完整的welcome.php源码。这一步成功之后,我开始逐个读取源码中其他文件,最终在admin.php里发现了另一处可以二次利用的代码。

文件包含题的关键就在于:一是找可控参数,二是确认是否有过滤,三是选对过滤器。很多新手一上来就直接读/etc/passwd,但没有意识到include是会把目标当PHP执行的,只有配合过滤器或伪协议才能安全读取内容。

5.3 源码审计中发现的第二个入口

在admin.php里,我发现了一个后台登录逻辑和一个日志写入函数。这个日志功能会把用户输入直接拼进文件路径里,于是文件读取题瞬间变成了一个写文件入口。当时时间有限,我并没有把它延伸成完整的getshell链,只是通过日志写入构造了一个包含PHP代码的日志文件,再配合前面的include把日志包含进来,执行了想执行的PHP代码。

这个操作可以从文件读取升级成命令执行的思路,依赖的是把用户数据写进一个"可以被包含"的文件。日志文件、SESSION文件、临时文件都可能是利用点。在比赛里,这种由多个小漏洞串联起来形成的利用链,分值通常比单个漏洞高很多,也是源码审计题的乐趣所在。

我做完这题后的体会是,文件读取最怕的不是过滤严格,而是你根本不知道有哪些文件存在。所以前面扫目录发现backup.zip,是整道题最关键的一步。如果没有备份文件,你要靠猜文件名去读源码,效率会低得多。多扫目录、多注意响应头里的线索、多注释里的提示,这些细心活往往决定一道题能不能顺利解出来。

6. 应急响应风格的杂项题:把日志当故事读

6.1 一道需要"破案"的Misc题

PolarCTF这次有一道让我眼前一亮的杂项题,题面是一个压缩包,里面放了一份Nginx访问日志和一份网站目录拷贝,要求找出攻击者留下的后门并还原attack链。这类题目这几年在青少年CTF和应急响应演练里越来越多,本质上是用游戏化的方式模拟真实安全事件的分析过程。

压缩包解压后,access.log有二十多MB,直接打开看显然不现实。我的第一步永远是先做统计,而不是盲目看每一行。在Linux终端里执行:

awk '{print $7}' access.log | sort | uniq -c | sort -nr | head -50

参数$7对应的是请求路径,统计结果里出现了很多次相同路径/upload/evil.php,还有一个路径频繁携带cmd参数。这基本说明攻击者通过WebShell执行了大量命令。一个正常的业务网站不会有那么多次/upload/evil.php请求,所以这个文件就是第一条线索。

6.2 还原攻击链的完整过程

我在网站目录的upload文件夹里找到了evil.php,用它残留的代码反向追踪日志。攻击者的请求内容很有意思,大量使用了base64编码的参数。我随手截取一条:

grep "cmd=" access.log | tail -20

其中有几条请求的参数非常长,显然不是简单的命令。我把其中一条参数内容做URL解码后,发现是一段base64字符串,再用命令行解码:

echo 'xxx' | base64 -d

解码出来后是一个bash脚本,脚本先将网站日志压缩打包,然后通过某种方式带出。说明攻击者已经在服务器上执行过数据窃取操作。继续往日志后面翻,可以看到攻击者上传了一个图片文件到/tmp目录,而访问日志里对这个图片没有任何GET记录,这不符合正常用户行为。

我在/tmp目录拷贝里找到了这张photo.png,一开始用strings查看,没有发现明显flag。后来用steghide检测,提示文件头部之外的隐藏数据存在。直接解隐写:

steghide extract -sf photo.png

输入为空密码后,解出了一个flag.txt,内容就是flag。到这里整道题的attack链算是闭环了:攻击者先通过文件上传入口放了一个WebShell,接着用命令执行下载了一张图片,把数据隐写到图片里再想办法外传。

复盘时我最大的收获是:杂项题里的应急响应方向,考的并不是单一工具,而是你能不能从海量数据里快速定位关键线索。日志分析最忌讳逐行读,先做聚合统计是效率最高的方法。常用的命令不外乎grep、awk、sort、uniq,配合管道组合在一起就已经能解决大部分日志排序问题。

6.3 杂项场景里的实用工具组合

提到杂项题,很多人第一反应是图片隐写、音频频谱、压缩包伪加密,确实这些是常见考点。我这次在解应急响应题时用到的工具并不复杂,整理一下供参考:

  • strings:快速提取文件中的可见字符串,主要用于找flag和提示。
  • binwalk:检测文件里是否嵌入了其他文件,常见于图片里藏压缩包。
  • steghide:处理JPEG、BMP、WAV的隐写,用空密码或猜密码破解。
  • zsteg:PNG和BMP的LSB隐写检测工具,比赛出镜率很高。
  • exiftool:查看文件元数据,有时候flag直接写在EXIF信息里。
  • CyberChef:浏览器里的编码解码工具箱,遇到base64、hex、URL编码套娃时特别管用。

杂项题的套路很多时候是"多编码嵌套",不管是flask session的伪造还是图片隐写,到最后往往都要解一堆base64和hex。这种纯机械操作不需要太多思考,有一把好用的解码工具能省下大量时间,所以我强烈建议新手选手本地装好CyberChef,并熟悉它的Magic模块。

7. 赛后整理:给准备打下一场的人几个实在建议

7.1 工具不在多,顺手和链路完整更重要

以前我也喜欢收集各种工具,硬盘里躺着一堆扫描器、爆破工具、漏洞利用框架,真正比赛的时候能从头到尾用顺手的其实就那几个。这次比赛我实际用到的核心工具链大概是:浏览器开发者工具加Hackbar类插件做接口调试,Burp Suite抓包改包,御剑做目录扫描,CyberChef做编码转换,再用系统的grep、awk处理日志文本。

很多Web题的解法在Burp里会变得特别清晰,因为你能看到完整的请求报文,也能快速修改参数重放。比如文件包含题里那种base64读源码的操作,我在Burp的Repeater里改参数比在浏览器地址栏里改要高效得多,还能保留历史记录对比每次响应的差别。

对新手来说,先把Burp的Proxy和Repeater两个功能练熟,就能覆盖大部分Web题的需求。没有必要一上来就追求各种商业级工具,反而容易让精力浪费在工具配置上。目录扫描工具也不需要贪多,选一个字典质量高、能稳定跑完的就够了。

7.2 结合这次比赛,我再梳理一下新手的答题策略

首先是签到题。多数平台会放一道基础题让选手拿flag热身,读取页面源码或者简单计算即可。哪怕你暂时没有思路,也要学会从源代码、响应头、Cookie、注释里找线索,很多flag就藏在你想不到的位置。

其次是Web题。看到参数和文件名,先试着判断可能存在什么漏洞类型,不要盲目上payload。比如命令执行题,先确认参数是不是被system、exec这类函数接收了;SQL注入题,先判断是字符型还是数字型注入,再考虑过滤规则。这个判断过程远比你背一百条payload重要。

然后是Misc题。老生常谈的是保持耐心。图片隐写解不出来的情况太常见了,但答案往往就是多试一种工具、多看一眼文件尾部。做题遇到瓶颈时可以开启"扫描式思维":这里有没有隐藏文件?文件头对不对?有没有拼接数据?能不能从元数据里找到线索?把这些问题列成清单,排除效率会高很多。

7.3 卡住超过四十分钟时的自我排查清单

一场比赛四五个小时,一定会碰到卡壳的题。我发现很多问题不是你不会,而是你陷入了某一种错误假设里。这里分享一个我自己整理的排查清单,每次卡住都会过一遍:

  1. 参数名和请求方式是不是搞错了?比如有些POST接口被当成GET请求发,自然没有结果。
  2. 有没有对URL做必要编码?$、#、%、{、}在URL里没有编码会导致服务端收到完全不同的内容。
  3. 黑名单过滤的是字符串本身还是正则匹配?这决定了你是该找等价命令,还是该做字符拼接。
  4. 报错信息到底有没有被吞掉?如果页面空白,不妨先尝试引发一个明显错误,看回显机制是否正常。
  5. 题目的flag是不是被拆成了多段?在一些综合题里,只拿其中一段可能无法通关。

上次我在一道命令执行题里卡了很久,最后发现问题只是URL编码没做完整,$IFS三个字符里有$没编码。这种低级错误在比赛现场很容易犯,列一个排查清单可以有效缩短踩坑时间。

7.4 一次比赛真正的收获不在flag数量

比赛结束后,平台会开放WP查看权限,我花了几天时间把每一道自己没做出来的题重新过了一遍。个人认为,独立复现一道题带来的成长比比赛时解出三道题还要大。因为你已经知道答案,再倒推时能更集中注意力去理解漏洞形成的本质,而不只是机械地复制某个payload。

最后留一个小技巧。每次比赛我都会把用到的payload和对应的原理记成一篇简短笔记,尤其记录那些把自己卡住的过滤规则和绕过手法。下一次再遇到类似题目,直接翻笔记比重新试错快得多。积累到一定数量,你会发现CTF题型的套路其实非常固定,命令执行绕过来绕过去就是空格、关键词、黑名单,SQL注入翻来覆去就是过滤、闭合、编码。把基本功打牢,下一次比赛你一定不会后悔这次认真做的复盘。