The Fallback Password: Dissecting the Tenda Router Backdoor (CVE-2026-11405)

작성자

카테고리:

← 피드로
DEV Community · Amayo Clinton · 2026-07-20 개발(SW)

Amayo Clinton

If you’ve ever reviewed authentication code and felt a little itch when you saw an if (authFailed) { tryAnotherWay() } branch — congratulations, your instincts are correct. That exact pattern is at the center of a newly disclosed backdoor affecting multiple Tenda router models, and it’s a great case study in how not to design an auth flow.

Let’s break down what’s actually happening in the firmware, why it matters beyond “yet another router CVE,” and how you’d go looking for something like this yourself.

The TL;DR

CERT/CC (Carnegie Mellon’s Software Engineering Institute) published VU#213560 / CVE-2026-11405 on July 6, 2026, describing an undocumented authentication bypass in the /bin/httpd binary shipped on several Tenda router firmware builds:

  • US_FH1201V1.0BR_V1.2.0.14(408)_EN_TD
  • US_W15EV1.0br_V15.11.0.5(1068_1567_841)_EN_TDE
  • US_AC10V1.0re_V15.03.06.46_multi_TDE01
  • US_AC5V1.0RTL_V15.03.06.48_multi_TDE01
  • US_AC6V2.0RTL_V15.03.06.51_multi_T

No patch is available. CERT/CC says it notified Tenda privately on May 19, 2026, and got silence for seven weeks before disclosing publicly. Tenda has a documented history of not responding to vulnerability reports going back to 2013.

The Actual Vulnerable Logic

Reconstructing the flow described in the advisory, the login() function in /bin/httpd looks roughly like this:

// Pseudocode reconstruction based on CERT/CC's description
int login(char *username, char *password) {
    // Primary path: standard MD5-hashed credential check
    char *stored_hash = get_stored_password_hash(username);
    char *submitted_hash = md5(password); // via check_rand_key / PasswordToMd5

    if (submitted_hash == stored_hash) {
        return grant_session(username, ROLE_ADMIN);
    }

    // --- This is the backdoor ---
    // Fallback path: only reached when normal auth fails
    char *backdoor_password = GetValue("sys.rzadmin.password");

    if (strcmp(password, backdoor_password) == 0) {
        // Note: username is NEVER checked here
        return grant_session("rzadmin", ROLE_ADMIN); // role=2
    }

    return AUTH_FAILED;
}

Enter fullscreen mode Exit fullscreen mode

A few things jump out immediately if you’re reading this as a developer:

1. It’s not a hardcoded credential — it’s a fallback credential

Most classic router backdoors (Zyxel’s zyfwp, Fortinet’s SSH backdoor from 2019, various D-Link/ASUS incidents) work by embedding a static, hardcoded username/password pair that’s compiled into the binary or listed in /etc/passwd. You can find those with a simple strings pass or a known-credential wordlist.

This one’s architecturally sneakier: the backdoor password isn’t hardcoded in plaintext in the binary — it’s read at runtime from a config key (sys.rzadmin.password) via GetValue(). That means:

  • It could theoretically be different per device/batch if that config value is set during manufacturing/provisioning
  • It’s harder to spot via static binary analysis alone — you need to trace the GetValue() call and understand where that NVRAM/config key gets populated
  • It smells like a leftover engineering/support mechanism (remote diagnostics, QA testing) rather than a “let’s plant a backdoor” decision — though intent doesn’t really matter once it’s shipped in production firmware

2. Two authentication failures stacked on top of each other

It’s worth noting MD5 for password verification is already considered broken for anything security-sensitive — it’s fast to brute-force and has known collision weaknesses. So even before you get to the backdoor, the “legitimate” auth path is using deprecated crypto. The backdoor is really a second, more severe failure layered on top of a first one.

3. No username validation = every account is the admin account

This is the detail that pushes this from “bad” to “critical.” Because strcmp() only checks the password against the backdoor value and never validates username, you don’t need to know a valid admin username. admin, root, asdf, empty string — doesn’t matter. If you have the backdoor password, you’re in with role=2 (root-equivalent access to the web management daemon).

Where This Sits in the CVSS/Exploitability Landscape

If you triage CVEs for a living, this one checks every box for “fix now, ask questions later”:

  • Attack vector: Network (no local/physical access needed if remote management is exposed)
  • Privileges required: None
  • User interaction: None
  • Authentication bypass: Complete — this is the authentication mechanism failing open
  • Impact: Full administrative control — config changes, DNS/routing manipulation, disabling security features, pivoting into the LAN

The only mitigating factor right now is that no public PoC or confirmed active exploitation has been reported as of this writing. That window won’t stay open long — once someone extracts the exact backdoor password (or the algorithm that derives it) from firmware, this becomes a mass-scannable vulnerability.

How You’d Actually Find Something Like This

If you want to understand the discovery process (or audit your own embedded/IoT firmware for similar issues), the general workflow researchers use looks like:

# 1. Extract the firmware filesystem
binwalk -e firmware_image.bin

# 2. Locate the web server binary
find . -name "httpd"

# 3. Pull printable strings, looking for suspicious config keys
strings ./bin/httpd | grep -iE "password|admin|rzadmin|backdoor"

# 4. Disassemble around the login() function
# (Ghidra or IDA Free work well for MIPS/ARM router binaries)
# Look for comparison logic that runs *after* a failed primary auth check —
# especially calls to GetValue()/NVRAM-style config getters followed by
# strcmp()/memcmp() rather than a hashed comparison

Enter fullscreen mode Exit fullscreen mode

The giveaway pattern to grep for in disassembly: a hash-based comparison (md5, sha) followed conditionally by a plaintext strcmp() against a runtime-fetched value. Legitimate fallback/recovery auth (e.g., factory reset flows) usually requires a physical trigger (button press, serial console) — not a remote HTTP request.

Detection If You’re Running Fleets of These Devices

If you manage a bunch of Tenda hardware (MSPs, this means you), here’s a rough network-level check while waiting on a patch:

# Confirm whether remote/WAN management is exposed at all —
# this is your actual blast-radius question
nmap -p 80,443,8080 <router-ip>

# If reachable, check for the login endpoint and response behavior
curl -s -o /dev/null -w "%{http_code}n" http://<router-ip>/login.cgi

Enter fullscreen mode Exit fullscreen mode

You can’t test for the specific backdoor password without it being disclosed (responsible security practice, and also nobody’s published a PoC), but you can and should verify whether the management interface is internet-facing at all. That single control point — WAN-facing admin panel, yes/no — determines whether this bug is “theoretical” or “actively scannable” for your deployment.

Mitigations (Until Tenda Ships a Fix, If Ever)

CERT/CC’s guidance, plus some practical additions:

  1. Disable remote/WAN web management — this is the single highest-leverage fix. It doesn’t close the backdoor, but it removes it from the internet-facing attack surface.
  2. Change the default LAN IP range — reduces opportunistic discovery by automated scanners hunting default 192.168.x.1-style ranges. Doesn’t stop targeted attackers.
  3. Segment IoT/consumer hardware onto its own VLAN — if this router is sitting in a business environment, don’t let it share a broadcast domain with anything sensitive.
  4. Treat “no vendor response since 2013” as a procurement signal — Tenda’s disclosure history (2013, 2020, 2021, 2022, now 2026) is itself a data point worth weighing against price when selecting network hardware for anything beyond disposable home use.

So What? (The Actionable Bit)

If you’re building or reviewing embedded auth code, the concrete lesson here isn’t “don’t use backdoors” — nobody’s shipping this intentionally as a “feature” in the marketing sense. It’s:

  • Any fallback authentication path is a second attack surface. If you have a “primary check fails, try secondary check” pattern anywhere in your auth flow — for support tooling, factory testing, recovery — that secondary path needs the same scrutiny (and ideally the same removal-before-shipping discipline) as the primary one.
  • Config-driven secrets aren’t automatically safer than hardcoded ones. They’re harder to spot via static strings analysis, sure — but that’s a detection problem, not a security improvement. If anything, it makes audits harder without making the underlying risk smaller.
  • Username validation isn’t optional, ever, even in a fallback/debug path. “Any username + secret password = admin” is a strictly worse design than “specific username + secret password,” and it costs nothing to enforce.

If you’ve got Tenda hardware anywhere in your stack — home lab, MSP client site, that one office router nobody’s touched since 2022 — this is worth 10 minutes today to check exposure, even without a patch to apply.

Have you found similar “fallback auth” patterns during firmware audits or code reviews — intentional debug backdoors that just never got stripped before shipping? Where do you draw the line between “reasonable engineering shortcut” and “should never have existed in production”? Curious how other embedded/security folks think about auditing this class of bug systematically rather than one CVE at a time.

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다