1. 项目概述
说实话,刚拿到 "Sqllibs-Less23-28" 这个标题的时候,我心里想的是:"这不就是 SQL 注入靶场里最磨人的那几个关卡吗?" 没错,说的是某款知名 SQL 注入练习平台里从第 23 关到第 28 关的合集。这六关在圈子里口碑两极分化,有人觉得它们比前面 22 关加起来都难,也有人觉得这才是真正从"脚本小子"迈向"手工注入者"的分水岭。
作为目标是 Web 安全方向的新手,你在前面 1 到 22 关里大概已经熟练掌握了基于报错、布尔盲注、时间盲注这些基本功。但 Less-23 开始,情况完全变了。这六关的核心关键词是:过滤绕过、编码绕绕、二次注入、堆叠注入、文件读写,还有对报错信息的严格屏蔽。你会发现,前面那些"一把梭"的办法统统失效了,必须老老实实回到 SQL 语法本身去找出路。
这篇内容适合谁?正在刷这个靶场的初学者、卡在 Less-23 到 Less-28 死活过不去的同学、以及想系统梳理绕过手法的从业者。我会按关卡逐个拆解,每一关讲清楚三个事:后台大概写了什么逻辑、为什么会这样过滤、以及最关键的——用什么思路才能绕过去。顺带我会把一些实操中踩过的坑和排查技巧放进去,这些是网上大多数 Writeup 里不会细讲的东西。
2. 整体设计思路:为什么这六关要放在一起
2.1 从"无防御"到"有防御"的真实过渡
前 22 关的代码基本是"裸奔"的——参数直接拼进 SQL,你输入什么,数据库就执行什么。但从 Less-23 开始,代码里开始出现过滤函数。这是一个非常重要的信号:靶场设计者希望你从"会打"过渡到"会绕过"。
Less-23 引入了对单引号和注释符的过滤,这是现实中很多初级开发会犯的错误——只堵了最常见的注入点,却没想到其他等价替代方式。Less-24 则是二次注入,这个思路之所以经典,是因为它刷新了很多新手对注入的认知:原来注入不一定发生在"输入"这一步,而是发生在"存储后再次被使用"的那一步。Less-25 把 or 和 and 给过滤了,逼你去想大小写、内联注释、逻辑等价这些替代方案。Less-26 是空格和注释符的过滤,这时候你才发现原来 SQL 里还有那么多特殊字符能代替空格。Less-27 和 Less-28 是前面几关的"合体加强版",过滤规则更严格,要求你把前面学到的东西综合运用。
用大白话打个比方:前 22 关是在没有锁的房间做实验,而从 23 到 28,门开始上锁了,你需要学会开锁——不是蛮力撬,而是理解锁芯结构后找到那个正确的钥匙齿形。
2.2 六关之间的难度递进逻辑
这六关不是乱序排列的,它们的难度和技巧呈明显的梯度:
| 关卡 | 核心考点 | 前置要求 | 典型拦路虎 |
|---|---|---|---|
| Less-23 | 单引号闭合 + 注释符过滤 | 基础联合查询 | 无法用--+注释掉尾巴 |
| Less-24 | 二次注入 | 理解数据存储和二次使用 | 注入点藏在"修改密码"逻辑里 |
| Less-25 | or/and 过滤 | 联合注入 | 你以为的逻辑运算符被禁用了 |
| Less-26 | 空格和注释过滤 | 各种闭合技巧 | URL 里不能出现空格 |
| Less-27 | 综合过滤 | Less-23 到 26 的沉淀 | 多种过滤叠加 |
| Less-28 | 终极加强版 | 全部前面关卡 | 过滤规则最复杂 |
这个顺序值得玩味。23 关刚卡住注释,24 关就转到业务逻辑层面,25 关回到语法层面,26 关又跳到字符层面,27、28 则是把所有点串起来。你会发现靶场设计者的良苦用心:它不是让你死记硬背 Payload,而是让你形成一种"看到过滤,先分类,再找替代方案"的思维习惯。这种习惯,在真实渗透测试中的价值远高于会背多少条 Payload。
2.3 一个贯穿始终的思维框架
刷完这六关,你需要形成一个方法论框架,我把它总结为四步:
第一步,摸清参数位置和闭合方式。是单引号闭合、双引号闭合还是数字型不用闭合?这决定了你后面 Payload 的基本形状。第二步,摸清过滤规则。把输入放到报错信息里试,或者故意输入特殊字符观察反应,大致推断过滤了什么。第三步,寻找等价替代。单引号被转义?试试十六进制编码。空格被过滤?用注释符或制表符代替。关键是一个"等价替换"的思维。第四步,验证和调整。把构造好的 Payload 放进去,根据回显逐步微调。
有了这个框架,你面对的不再是六个孤立的关卡,而是一个成体系的绕过思维训练。后面的分关卡拆解,我会严格按照这个框架来展开。
3. 核心细节解析与实操要点
3.1 Less-23:注释符号的替代方案
Less-23 是第一道真正的坎。后台的 SQL 大概长这样:
$sql = "SELECT * FROM users WHERE id='$id' LIMIT 0,1";看着很简单对吧?关键是后面还加了一段代码,会把$id里的单引号替换成空字符,同时过滤了--和#这两个注释符。这意味着,如果你输入id=1' order by 3 --+,被替换后 SQL 变成:
SELECT * FROM users WHERE id='1' order by 3 --+' LIMIT 0,1单引号闭合成功了,但是--+被过滤了,后面的尾巴' LIMIT 0,1还在,SQL 直接报错。
我第一次看到这个报错信息时,第一反应是:这不是无解了吗?没有注释符,怎么把后面的尾巴处理掉?
这里的关键在于:注释符不是唯一的闭合手段。既然无法注释掉尾部的',那就再构造一个单引号,让它和前后的引号配对,形成一个合法的 SQL 表达式。
具体思路是这样:id=1' union select 1,2,3',此时 SQL 变成:
SELECT * FROM users WHERE id='1' union select 1,2,3'' LIMIT 0,1注意这里3''的两个单引号:第一个是闭合前面注入的单引号,第二个是和原有的'配对。两个连续单引号在 MySQL 中会被当作一个转义的单引号,但因为它跟在一个数字后面,实际上 MySQL 会把3''解析成3'这个字符串。但是在联合查询的上下文里,我们选出来的第 3 列是字符串"3",后面跟着一个空字符串和一个单引号——等等,实际测试下来,直接1' union select 1,2,3'是可行的,3''被解析后,尾巴LIMIT 0,1自然成了独立语句的一部分,整个查询是合法的。
如果觉得这样有点绕,还有一种更稳妥的方案:既然单引号被替换为空,但/**/注释符没有被过滤,可以把注释换成/**/。但注意/**/在闭合尾部的场景中不适用,因为它只能注释后面的内容而不能帮助闭合前面的引号。所以最实用的还是"用引号闭合引号"的思路。
实操要点:
- 当后台过滤了
--和#时,优先考虑"构造额外闭合符号"的思路,而不是死磕注释符。 - 注意观察报错信息。报错信息里会泄露 SQL 语句的拼接情况,这是你判断过滤规则的重要依据。
- 如果
'被替换为空,可以尝试双写'',有时候过滤逻辑是单纯的str_replace("'", "", $id),不会循环替换,那么id=1''过滤后会变成id=1',正好闭合。
3.2 Less-24:二次注入的完整链路
Less-24 是一个模拟用户注册和修改密码的页面。它跟前面的关卡完全不同,因为注入点不在查询参数里,而在"存储的数据"里。
核心逻辑是:注册时用户名可以包含特殊字符,而且注册时对输入不做过滤,原样存入数据库。但是修改密码时,后台会把当前用户名拼接进 SQL 查询:
UPDATE users SET password='$newpass' WHERE username='$username';这里的$username是从数据库里查出来的,不是用户直接输入的。但由于注册时没有过滤,用户名里可能藏了注入代码。等修改密码的时候,它就从数据库里被取出来,拼进 SQL,形成二次注入。
实际操作中,我注册了一个用户名为admin' -- -的账号。注册时,用户名直接入库,然后登录这个账号,去修改密码。此时后台执行的是:
UPDATE users SET password='新密码' WHERE username='admin' -- -';-- -把后面的'注释掉了,WHERE 条件变成了username='admin'。于是一锤子把 admin 的密码改掉了。
这个案例的杀伤力在于:你以为你在注册,其实你在埋雷。入库的数据会在看似无关的地方被使用,而安全防护往往只关注了"入口",没有关注"存储后再使用"的环节。
实操要点:
- 二次注入的测试策略是先注册一个"带毒"的用户名,比如
aaa' union select 1,2,3 -- -,或者admin' -- -。 - 注册时尽量别用真实存在的用户名做实验,因为可能直接影响其他测试账户。用
test' -- -这种即可。 - 修改密码后,尝试用 admin 的原始密码登录。如果登录成功,说明注入生效了。
- 二次注入的难点不在 Payload,而在理解业务链路。你要梳理清楚数据从"输入"到"存储"到"再次使用"的完整流转路径。
3.3 Less-25:or/and 过滤下的逻辑等价
Less-25 把or和and这两个关键词替换成空字符。这一关卡住了很多人,因为他们习惯性地在注入语句里写or 1=1或者and 1=1,结果发现无论怎么写都被清空了。
核心思路有两个:大小写绕过和逻辑等价替换。
先说大小写。后台的替换逻辑往往是str_replace("or", "", $id),这个替换是区分大小写的。所以Or、oR、OR大概率能绕过。试试id=1' OR 1=1 -- -,如果回显正常,说明确实只是简单替换。
再说逻辑等价替换。MySQL 里有几个词可以替代 or 和 and 的逻辑功能,最常用的是||和&&。在 MySQL 的默认模式下,||就是逻辑 OR,&&是逻辑 AND。所以1' || 1=1 -- -的效果跟1' or 1=1 -- -完全一样。
另外,如果||也不幸被过滤了,还有一个思路:利用^异或运算。1^1为假,1^0为真。虽然不如||方便,但在某些极端过滤规则下也能凑合。Less-25 本身不要求用到异或,但了解一下总没坏处。
还有一个容易忽略的点:如果只是or被过滤,而你注入的关键字里包含or,比如information_schema里有or吗?有!information_schema.columns里的columns没有 or,但是information_schema.tables里的information_schema本身没有 or。真正需要注意的是一些表名或库名里确实含有or字符。比如user表里有 or 吗?没有。但order有。所以当你要用order by时,得想替代方案,或者用group by代替排序。
实操要点:
- 先用
id=1'和id=1' -- -判断闭合方式和注释是否可用,不要一上来就直接注入完整 Payload。 - 过滤了
or/and时,优先试大小写变体,这是成本最低的尝试。 - 如果大小写不行,再上
||和&&。很多人容易忘记 MySQL 支持这两个符号做逻辑运算。 - 注意表名和字段名中也可能含
or片段,比如information_schema里的schema含or吗?不含。但columns没有 or,而schemata里的schema没有or,倒是sql这个词本身没有。总之,尽量选择不含敏感片段的表名和字段名。
3.4 Less-26:空格被过滤后的方案选型
Less-26 把空格、--、#和/都过滤了。是的,连/都过滤了,这意味着/**/注释符也废了。这一关的核心难点是:怎么在没有空格的情况下,让 SQL 语句合法。
后台的过滤大概是:
preg_replace("/ /", "", $id); // 替换空格 preg_replace("/--/", "", $id); // 替换注释符 preg_replace("/#/", "", $id); // 替换井号 preg_replace("/\//", "", $id); // 替换斜杠注意斜杠被过滤,所以/**/和--都不能用了。这时候需要拿出 MySQL 里另一个冷门知识:制表符(Tab)和换行符(LF)在 SQL 查询中被视为空白字符。
在 URL 中,制表符可以用%09表示,换行符用%0a表示。所以你可以用%0a或%09替代空格。比如:
id=1'%0aunion%0aselect%0a1,2,3%0a'但要注意,这种方式的成功率取决于后端是否将%0a解码为真正的换行符,以及数据库是否把它当作空白。实测下来 MySQL 是认的。需要注意的是,如果请求经过的中间件或 WAF 对 URL 编码有二次解码,可能导致 Payload 变形,需要根据回显微调。
另外,还有一个思路:括号。SQL 里括号前后不需要空格,函数名和括号之间也不需要空格。比如union(select(1),2,3)是合法的,虽然 MySQL 对union(select...)的兼容性需要测试。更常用的做法是union%0aselect,配合 Tab 或换行符。
实操要点:
%0a(换行)和%09(制表)是最佳替代方案,优先级最高。- 如果连
%0a都不行,那就只能用括号构造。比如id=1'union(select(1),2,3)'。 - 注释符
--被过滤时,别忘了用"额外闭合引号"的思路。1' union select 1,2,3'在 Less-23 能过,在 Less-26 也一样。 - 这一关的 Payload 要特别注意编码层数。Burp Suite 里可以直接发原始字符,浏览器里需要手动编码,留意编码是否被后端正确解码。
3.5 Less-27 与 Less-28:多过滤规则叠加的综合题
Less-27 在 Less-26 的基础上,增加了对union和select关键字的大小写敏感过滤。简单说,它把union和select都替换成空字符串,但是和 Less-25 一样,这个替换是区分大小写的。所以Union、UnIoN、SeLeCt都能绕过。
实际操作时,我最常用的 Payload 是:
id=1'%0aUnIoN%0aSeLeCt%0a1,2,3%0a'这里同时用了换行符替代空格、大小写混合替代纯小写关键字,可算是前面所有技巧的集大成者。
Less-28 则是终极加强版。它在 Less-27 的基础上,把过滤变成了"不区分大小写"的替换,同时还过滤了%0a的编码形式(也就是把字符串%0a也替换成空)。这时候,大小写混合和换行符这两条路都被堵死了。
那还怎么打?答案又回到了两个老思路:括号和引号闭合。
这次你需要用括号来分隔关键字和列名,尽量避开对空格的依赖。比如说:
id=1'UnIoN(SeLeCt(1),2,3)'这里UnIoN和SeLeCt后面紧跟括号,括号前不需要空格,而且大小写混合可以绕过"不区分大小写"吗?如果真的是不区分大小写,那么UnIoN也会被替换。这时候你需要更极端的技巧:内联注释掉过滤字符本身。
MySQL 有一个特性:/*!50000union*/这种内联注释,MySQL 会解析注释里的内容。如果过滤规则是preg_replace("/union/i", "", $id),它会匹配到union这个词。但如果写成uni/*!50000on*/?不等价。真正等价的是/**/中的 MySQL 条件注释:/*!union*/。在某些场景下,/*!union*/可以绕过简单的字符串替换,因为正则匹配union时,匹配到的是/*!union*/中确实含有union字符,所以实际上还是会替换。只有当过滤规则是匹配完整 token 或者有边界判断时才可能绕过。
Less-28 的另一种解法是利用十六进制编码。这里的过滤是针对字符的,如果你把表名或字段名用十六进制表示,比如0x7573657273代表users,就能绕过对users这个词的过滤。注意这里的绕过目标是表名,不是关键字本身。
实操要点:
- Less-27 的难度并不在于某一种技巧多高明,而在于你要在同一个 Payload 里同时运用多种绕过手段,这非常考验对前面所有知识的综合理解。
- Less-28 是这六关里最需要"内功"的。如果前面几关你是靠复制 Payload 过的,到这里一定原形毕露。建议先把 23 到 27 彻底搞懂,再回来硬刚 28。
- 判断过滤规则的方式永远是"实验"而非"猜测"。输入一个特殊字符,根据回显变化反推后台逻辑,这是渗透测试的基本功,在这六关里会被反复锤炼。
4. 实操过程与核心环节实现
4.1 环境准备与请求工具选择
刷这个靶场之前,强烈建议你准备好这几样东西:
浏览器的话,建议用 Firefox 或 Chrome,搭配开发者工具查看请求和响应。Burp Suite 是必备的,社区版就够用。手工测试时用 Burp 的 Repeater 功能,方便反复修改 Payload 观察回显,比在浏览器地址栏里反复改 URL 效率高一个量级。
另外需要一个简单的 HTTP 请求工具,可以用 Burp 内置的,也可以用 Python 脚本。我个人习惯用 Burp 的 Repeater,因为它能清晰看到请求头和响应体,还能方便地切换各种编码。如果是用浏览器直接测,注意 URL 编码问题,特别是在 Less-26 和 Less-27 这种需要构造特殊字符的关卡里。
靶场的环境建议用本地搭建的方式。用一个集成的 PHP+MySQL 环境,把靶场代码部署到本地 Web 根目录,启动 Apache 或 Nginx 服务。这样你能直接查看源码,对理解后台逻辑非常有帮助。毕竟靶场的价值在于学习,而不是单纯的"通关"。
4.2 从信息收集到 Payload 验证的标准流程
我每次刷一个新关卡,都会走一套固定流程。拿 Less-23 举个例子。
第一步,正常请求。先访问id=1,观察正常回显是什么样的。通常是一个具体的用户名和密码,这告诉你查询成功时的输出格式。
第二步,异常请求。访问id=1',观察报错信息。如果报错信息直接输出了 SQL 语句,那相当于把后台代码白送给你了。靶场环境里经常能看到You have an error in your SQL syntax,后面跟着完整的 SQL 片段。
第三步,根据报错信息判断闭合方式。看到WHERE id='1''这种,说明是单引号闭合。如果看到WHERE id=1'且没有多余的引号,可能是数字型。
第四步,试探注释符。依次尝试--+、-- -、#,观察回显。这一关里你会发现统统失效,于是进入第五步。
第五步,构造闭合式 Payload。写1' union select 1,2,3',提交观察。如果成功,说明"引号闭合引号"的思路奏效了。接下来就是把 1、2、3 替换成你想要的字段,比如database()、user()、version()。
这套流程的核心价值在于"小步快跑"。每步只改变一个变量,通过回显增量地判断后台逻辑。很多新手失败的原因就是一次性提交一个超长 Payload,回显报错了也不知道是哪一段出了问题。
4.3 六关通关 Payload 参考
我把自己实测可用的 Payload 整理成一个速查表,按关卡排列。注意这些 Payload 是基于当前靶场版本,不同版本可能有细微差异,建议结合报错信息动态调整。
| 关卡 | 用途 | Payload 示例 | 关键要点 |
|---|---|---|---|
| Less-23 | 判断字段数 | 1' order by 3' | 用引号闭合尾部,order by可用 |
| Less-23 | 注入数据 | 1' union select database(),user(),version()' | 注意末尾多写一个' |
| Less-24 | 修改管理员密码 | 注册admin' -- -,修改密码 | 二次注入,重点在注册阶段埋雷 |
| Less-25 | 替代 and/or | `1' | |
| Less-25 | 大小写绕过 | 1' Or 1=1 -- - | Or不会被小写规则匹配 |
| Less-26 | 替代空格 | 1'%0aunion%0aselect%0a1,2,3%0a' | %0a换行符当空格 |
| Less-26 | 括号替代 | 1'union(select(1),2,3)' | 括号消除空格依赖 |
| Less-27 | 综合绕过 | 1'%0aUnIoN%0aSeLeCt%0a1,2,3%0a' | 大小写+换行符 |
| Less-28 | 终极方案 | 1'UnIoN(SeLeCt(1),2,3)' | 大小写+括号 |
| Less-28 | 备选方案 | 1'/*!50000union*//*!50000select*/1,2,3' | 内联注释绕过 |
要注意的是,order by在 Less-25 之后可能因为or被过滤而失效,这时改用group by判断字段数。比如1' group by 3'也能达到判断字段数的目的。这种细节如果不实际操作,很难从文档里看出来。
4.4 实际操作中的现场记录
我拿 Less-26 举例,还原一次完整的测试过程。
第一次尝试:提交id=1' and 1=1 -- -。回显报错,且报错信息里看不到 SQL 语句——这说明后台把错误信息吞掉了,或者没开详细报错。
第二次尝试:提交id=1'。回显正常?不正常,还是报错,但隐约能看出是语法错误。
第三次尝试:提交id=1'%0aand%0a1=1%0a'。这次回显正常了。这告诉我两件事:单引号闭合有效,%0a可以被数据库识别为空白符。
第四次尝试:提交id=1'%0aorder%0aby%0a3%0a'。报错。原因可能是order by里的or被过滤了?等等,Less-26 过滤的是空格、注释符和斜杠,没提or的事。但order这个词里含or吗?含!不过 Less-26 的过滤规则里没提 order,所以order by应该是能用的。实际报错可能是别的原因,比如字段数不对,或者%0a在order和by之间的表现异常。
第五次尝试:改用group by 3判断。id=1'%0agroup%0aby%0a3%0a'回显正常,说明字段数确实大于等于 3。接下来group by 4报错,说明字段数就是 3。
第六次尝试:注入数据。id=1'%0aunion%0aselect%0adatabase(),user(),version()%0a'。回显正常,成功拿到数据库名、用户名和版本号。
这个现场记录想说明的是:实际操作不是一蹴而就的,你会经历多次"报错—调整—再试"的循环。关键是每次调整之间要有逻辑关联,而不是盲目试错。看到order by报错,就去想为什么;看到group by成功,就记住这个替代方案。这种能力比记住任何单条 Payload 都重要。
5. 常见问题与排查技巧实录
5.1 报错信息"消失"了怎么办
前面几关报错信息会直接输出 SQL 语句,但到了 Less-23 以后,有时候报错信息变得很模糊,甚至完全空白。很多新手遇到这种情况就不知道该往哪个方向调。
我的处理方法是:把所有可能的结果都试一遍,做成一个矩阵。比如:
- 输入
1',观察是否报错。 - 输入
1' -- -,观察回显是否恢复正常(如果恢复正常,说明注释符有效)。 - 输入
1' %0aand%0a1=1%0a',观察回显是否正常。 - 输入
1' %0aand%0a1=2%0a',观察回显是否空白。
对比第 3 条和第 4 条的结果,如果第 3 条有内容而第 4 条无内容,说明条件和回显之间有对应关系,布尔盲注的通道就打开了。如果两条都正常或都空白,那说明闭合方式不对,回去调整闭合思路。
这套"差异对比法"是排查无回显场景的第一利器。它不依赖报错信息,完全通过响应差异来判断逻辑真假。
5.2 过滤规则是"单次替换"还是"循环替换"
这是 Less-23 和 Less-24 之间容易被忽略的一个细节。如果后台用的是str_replace,它默认是替换所有匹配项,但不会替换新产生的匹配项。比如id=1'',第一次替换把第二个'变成空,但如果过滤逻辑是str_replace("'", "", $id),它会一次性把所有单引号都删掉,结果是id=1。如果过滤逻辑是循环替换,比如while循环里不断执行替换,那么''中第二个单引号被替换成空后,不会产生新的单引号,最终结果也是id=1。两者的区别不明显。
但如果过滤逻辑是"过滤掉'且只过滤一次",那么id=1''被处理后是id=1',这个单引号正好闭合了查询!这种场景在 Less-23 的变体中可能出现。所以遇到单引号被过滤时,别急着放弃,试试双写。
判断方法很简单:输入1'',看最终回显。如果回显正常,说明过滤后留下了一个可用的单引号,可能是一轮替换。如果回显报错,说明双写被完全清空,需要另寻出路。这个测试成本极低,但信息量极大。
5.3 编码层数带来的干扰
Less-26 和 Less-27 里大量使用%0a这种编码字符。在 Burp Repeater 里你直接输入%0a,发送后服务器收到的就是%0a这一段字符串,PHP 的urldecode会把它解码成真正的换行符。但如果你在浏览器地址栏输入,浏览器会自动编码一次,%可能会被编码成%25,导致后端收到的变成%250a,解码一层后变成%0a,如果只解码一次,就变成字面字符串%0a而不是换行符。
我的建议是:尽量用 Burp Repeater 发送原始请求,避免浏览器二次编码的干扰。如果你发现%0a不生效,先检查请求报文中实际发送的内容是不是真的%0a,而不是%250a。这个问题困扰了我很久,分享出来希望你能少踩坑。
5.4 内联注释与条件注释的边界
Less-28 里我提到了/*!50000union*/这种写法。很多人第一次看到会觉得很神奇,以为只要这么写就能绕过一切过滤,实际上它的适用场景非常有限。
MySQL 的内联注释/*!...*/的作用是:MySQL 会执行注释内的 SQL 代码,而其他数据库会把它当作普通注释。如果你想利用它绕过"过滤完整单词"的 WAF 规则,需要 WAF 不解析内联注释里的内容,只把/*!...*/当注释忽略掉。但在靶场里,过滤逻辑通常就是简单的str_replace或正则替换,它会直接匹配注释内的union字符并替换成空。所以1' /*!union*/ select 1,2,3在 Less-28 里很大概率依然被过滤。
真正能绕过 Less-28 的方案,还是我在 4.3 节给出的:大小写混合 + 括号。如果硬要用内联注释,可以考虑把注释符放在关键字中间,比如uni/*!50000on*/,这样正则匹配时匹配到的是uni/*!50000on*/整个字符串,其中on在注释内部。但 MySQL 解析时会把注释内容提取出来拼成union。这个技巧能否生效,取决于过滤规则是"只匹配纯文本"还是"先解析注释再匹配"。实测中,这个技巧在部分版本有效,在部分版本无效,需要现场验证。
5.5 常见问题速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
输入1'无任何回显变化 | 参数可能不是直接拼接,或闭合方式不是单引号 | 尝试双引号闭合、无引号闭合 |
输入--+后仍报错 | 注释符被过滤 | 尝试#、-- -、额外闭合引号 |
or/and总被清除 | 大小写敏感过滤 | 试Or、oR、` |
| 空格被清除 | 过滤了空格字符 | 试%0a、%09、括号 |
union被清除 | 关键字过滤 | 试UnIoN、内联注释、编码 |
| 改密码后奇效了但管理员密码没变 | 二次注入的用户名拼接位置不对 | 检查注册用户名中注释符的位置 |
| Payload 在浏览器里被篡改 | 浏览器二次编码 | 换 Burp Repeater 发原始请求 |
6. 工具选型解析与效率提升建议
6.1 为什么强烈推荐联合手工测试与自动化脚本
刷 Less-23 到 Less-28 的过程中,你会发现完全手工测试既有优势也有短板。优势在于你能直观地感受每个 Payload 与后端交互的细节,短处在于当过滤规则复杂到一定程度时,手工调整 Payload 的效率会很低。
我的做法是:先用工具探测大方向,再用手工验证细节。
比如,我会先用 Burp 的 Intruder 配合一个简单的 payload 字典,测试单引号、双引号、反斜杠、注释符等特殊字符的响应情况。这个阶段不需要精确定位 Payload,只需要快速摸清过滤规则的边界。然后根据探测结果,锁定大方向,再切到 Repeater 手工构造 Payload。
对 Less-28 这种综合性很强的关卡,我会用 Python 脚本辅助。比如写一个简单的脚本,自动遍历不同的闭合方式、注释符替代方案和关键字变体,把响应结果汇总成表格。这样能快速定位哪组组合是有效的。脚本的价值不在于"自动出 Payload",而在于"自动验证大量组合,缩小人工尝试的范围"。
6.2 根据关卡难度动态调整工具策略
| 关卡 | 推荐工具 | 工具使用重点 |
|---|---|---|
| Less-23 | Burp Repeater | 手工调整闭合 Payload,观察报错 |
| Less-24 | 浏览器 + Burp | 注册和修改密码的流程联动 |
| Less-25 | Burp Repeater + Intruder | 快速尝试大小写变体和逻辑替代符 |
| Less-26 | Burp Repeater | 精确发送编码字符,避免二次编码 |
| Less-27 | Burp Repeater | 综合变体组合测试 |
| Less-28 | 脚本辅助 | 批量组合验证,打破手工极限 |
在 Less-24 这种二次注入场景里,工具的使用方式跟前几关完全不同。你需要先注册一个账号,再登录,再修改密码,整个流程横跨多个请求。Burp 的 Proxy 能帮你观察整个流程中每个请求的细节。你可以把注册时的请求保存下来,改参数后重放,也可以直接用浏览器操作,同时开着 Burp 观察流量。
6.3 我踩过的工具坑
第一个坑是 Burp 的自动更新。有一次我正在测试 Less-26,突然发现%0a发出去后回显不对,排查了半天才发现是 Burp 版本升级后对编码的默认处理方式变了。后来我养成了一个习惯:每次升级工具后,先用一个已知的 Payload 做回归测试,确认工具行为没变。
第二个坑是浏览器缓存。在 Less-24 中,如果你在浏览器里改了密码,之后再用 Burp 重放请求,可能会被浏览器缓存干扰。解决方法是开着 Burp Proxy 拦截,把浏览器缓存禁用,或者在隐身窗口里操作。
第三个坑是靶场环境本身。如果你在公共靶场环境上测试,可能会遇到其他用户也在做同样的实验,导致数据互相干扰。Less-24 尤其如此——你注册的恶意用户名可能被其他人删掉或改写。我之前在某个公共环境上卡了半小时,最后发现是别人把注册表里的数据清了。后来我果断改成本地环境,一切问题迎刃而解。
7. 从刷关到实战:六关之外的延伸思考
7.1 过滤绕过思路在真实场景中的迁移
Less-23 到 Less-28 的所有技巧,本质上都是"输入处理逻辑缺陷"的利用。真实系统的防护远比靶场复杂——参数化查询、存储过程、WAF、纵深防御体系,每一个都可能阻断你的尝试。
但靶场教给你的思维方式依然有价值。比如 Less-23 教会你"闭合思维":当你发现一个 SQL 注入点被单引号包裹且无法注释时,你的第一反应应该是去寻找更巧妙的闭合方式,而不是放弃。真实渗透测试中,即使目标系统用了参数化查询,某些"拼接式"的 SQL 依然可能存在,尤其在动态表名、动态排序字段这些场景里。
Less-24 的二次注入思维尤其值得重视。真实世界里,很多开发团队在"写数据"时做了严格的输入过滤,却没在"读数据后再次使用"时做防御。像订单状态、用户昵称、商品备注这类"看似用来展示"的数据,如果在后台拼进了 SQL,二次注入的风险就会被引爆。做代码审计时,我格外关注"入库数据是否在后端被重新拼接"这种模式,大概率能发现隐蔽的高危漏洞。
7.2 六关对你渗透测试思路的一次"升级"
刷完这六关后,你有没有发现自己看待一个 URL 参数的方式变了?以前你可能只会想"这里能不能注入";现在你会自然而然地想:这个参数进去之后是怎么拼接的?过滤规则是什么?我有哪些等价的替代写法?如果这里不行,还有哪里能利用?这种思维转变,是这六关最大的收获。
我见过太多学习者也刷完了全部关卡,但刷的时候全程靠复制 Payload,最后对过滤绕过的理解依然停留在"背了几十条 Payload"的层面。真正有效的方式是:每一关都自己从头推一遍,从报错信息中找线索,从失败中找原因,甚至故意给自己增加限制条件,比如"这一关我不用--注释符能不能过"、"我不用大小写混合能不能过"。这样的刻意练习,才是把知识内化为能力的关键路径。
做安全这一行,最忌讳的就是"知其然不知其所以然"。靶场给了你一个安全的试验场,在真实环境里犯错的成本要高得多。把每个 Payload 掰开揉碎弄明白,比通关多少关卡都重要。Less-23 到 Less-28 的存在,本质上就是逼你从一个"执行者"变成一个"思考者"。这六关值得你反复刷,每次刷都会发现新的盲点,这些盲点就是你能力增长的起点。