back to blog

XL-B1310L Router: Firmware Teardown

Firmware image: flashdump.bin (8 MB, MIPS 32-bit little-endian, MediaTek MT7628)

I picked up an XL-B1310L dual-band router and wanted to see what was running inside. The device presents a LuCI web interface on 192.168.188.1, advertises itself with hostname pro, and identifies as a CST+8 timezone device, which puts manufacture somewhere in China. What follows is a full static teardown of the flash dump.


Getting the Firmware

The image came off the device as a single 8 MB flash dump. Binwalk identified the layout:

The interesting material is all in the JFFS2 overlay. That is where user configuration, credentials, and the web interface live. After extraction:

/jffs2-root/    -- writable overlay (JFFS2, offset 0x5D0000)

Two notable ELF binaries were also present at offsets 0x4FF9D6 and 0x510A96 in the flash, identified as:

The U-Boot environment block was also readable and contained a few surprises. More on that below.


Filesystem Recon

Before touching the binaries I went through every config file in the overlay by hand.

Plaintext Admin Credentials and Telnet

# /jffs2-root/etc/config/control

option username 'admin'
option password '1234'
option telnet '1'

The web interface admin password is stored in plaintext in control. It is 1234. Telnet is also enabled (option telnet '1'), meaning an unauthenticated shell is available to anyone on the LAN without even needing the web password. This is the single worst finding in the image.

LuCI RPC Daemon Password

# /jffs2-root/etc/config/rpcd

config login
    option username 'admin'
    option password '$p$admin'
    list read '*'
    list write '*'

The $p$ prefix is OpenWrt’s rpcd pseudo-hash scheme. It is not actually a hash, the value after the prefix is the plaintext password. The admin password for the LuCI JSON-RPC interface is admin. Full read and write access granted.

Shadow File Whiteout – Passwordless Root

$ ls -la jffs2-root/etc/shadow
c--------- 1 root root 0, 0 shadow

The shadow file is an OverlayFS whiteout (character device 0,0). In an OverlayFS mount this deletes the underlying shadow file, leaving /etc/shadow absent at runtime. The passwd file still contains root:x:0:0:..., where x means “look in shadow”, but there is no shadow to look in. The result is that the root account has no password on the running system.

Combined with the telnet service enabled above, this means telnet 192.168.188.1 from the LAN, then pressing enter at the password prompt, gives root.

SSH: Root Password Login Enabled

# /jffs2-root/etc/config/dropbear

config dropbear
    option PasswordAuth 'on'
    option RootPasswordAuth 'on'
    option Port '22'

Dropbear SSH is running with root password authentication enabled. Since root has no shadow entry, the account is unlocked with an empty password. Any SSH client can connect as root with no password.

WiFi Credentials – Plaintext PSK

# /jffs2-root/etc/wireless/mt7628/mt7628.dat

SSID1=tp58-PRO-2.4G7612
WPAPSK1=20102010
RADIUS_Key=ralink
# /jffs2-root/etc/wireless/mt7612/mt7612.dat

ApCliEnable=1
ApCliSsid=tp58
ApCliBssid=58:41:20:3f:09:aa
ApCliWPAPSK=20102010

The 2.4 GHz AP PSK is 20102010 in plaintext. The default RADIUS shared secret is ralink, a hardcoded MediaTek SDK default that ships with every Ralink/MediaTek reference design. More interesting is the MT7612 client bridge config: ApCliEnable=1 means this device is configured to connect as a wireless client to an upstream AP. The upstream SSID is tp58, BSSID 58:41:20:3f:09:aa, PSK 20102010. An attacker who reads this config can stand up an evil-twin AP with the same SSID, BSSID (MAC-spoofable), and PSK to intercept the router’s upstream traffic.

Firewall

# /jffs2-root/etc/config/firewall (LAN zone)

config zone
    option name lan
    option input ACCEPT
    option output ACCEPT
    option forward ACCEPT

The LAN zone accepts all inbound traffic. Every service on every port is reachable from any LAN host.

Boot Services

From ucitrack the following services start at boot:

network, firewall, dnsmasq, dropbear, httpd (lighttpd),
fstab, qos, luci_splash, miniupnpd, ntpclient, samba, tinyproxy, lan_wan_nat

Both samba and tinyproxy are running, expanding the attack surface further. Tinyproxy running on the LAN can be abused as a pivot point.

U-Boot Environment

bootcmd=tftp
bootdelay=2
serverip=10.10.10.4
ipaddr=10.10.10.123
ethaddr=00:AA:BB:CC:DD:10

The bootloader has a 2-second delay before executing bootcmd, which is set to tftp. If a TFTP server is running at 10.10.10.4 on the local network, U-Boot will attempt to fetch and boot a network image on every power cycle – no serial interruption required.


Binary Analysis

Binary Protections

Both ELF binaries score as poorly as possible on standard hardening checks:

Binary NX Stack Canary PIE RELRO FORTIFY
/4FF9D6 (lighttpd) Yes No No No No
/510A96 (iptables) Yes No No No No

NX is the only protection present. No stack canary means any stack buffer overflow goes straight to exploitation. No PIE means the binary loads at a fixed address every time. No RELRO means the GOT is writable, a write primitive redirects any function call.

lighttpd 1.4.45 – Known CVEs

CVE CVSS Description
CVE-2019-11072 9.8 Signed integer overflow in http_request_parse() via Content-Length. Heap corruption, potential RCE.
CVE-2018-19052 7.5 Path traversal in mod_alias. Alias prefix bypass via encoded path separators allows unauthenticated file read.

CVE-2019-11072 is directly relevant here. The function http_request_parse (decompiled at 0x004142f4) reads Content-Length with strtoll() and stores the result as a signed 64-bit value. There is no upper bound check before the value enters downstream allocation arithmetic:

// From http_request_parse, ~0x004153e0
long long content_length = strtoll(content_length_buf->ptr, &end, 10);
if ((*end != '\0') || (content_length < 0)) {
    // rejects obviously negative values
    goto error;
}
// no upper bound check -- INT64_MAX passes here
*(long long *)(con + 0x130) = content_length;

With a fixed binary base address and writable GOT, a heap corruption from this path has a clear path to code execution.

I also decompiled and annotated the MD5 block transform used for HTTP Digest authentication (FUN_004204f8 / md5_block_transform at 0x004204f8), identified by the RFC 1321 round constants (0xd76aa478, -0x173848aa, etc.), and the config file tokenizer (FUN_00410338 / configfile_lexer at 0x00410338). These are saved with cleaned decompilation in the project’s AI Cleaned view.

OpenSSL 1.0.x – End of Life

lighttpd links libssl.so.1.0.0 and libcrypto.so.1.0.0. OpenSSL 1.0.x reached end-of-life on December 31, 2019. Selected CVEs:

CVE CVSS Description
CVE-2021-3711 9.8 SM2 decryption buffer overflow – code execution
CVE-2022-0778 7.5 Infinite loop in BN_mod_sqrt() – DoS
CVE-2020-1967 7.5 NULL pointer in SSL_check_chain – DoS
CVE-2019-1551 5.3 rsaz_512_sqr overflow – RSA key recovery

66 CVEs total assigned after the last 1.0.2 release. None will ever receive a patch.

dnsmasq ~2.80 – DNSpooq and KeyTrap

dnsmasq carries the full DNSpooq vulnerability set (JSOF, January 2021) plus two later high-severity issues:

CVE CVSS Description
CVE-2020-25681 8.1 Heap overflow in sort_rrset() DNSSEC – RCE from WAN
CVE-2020-25682 8.1 Heap overflow in extract_name() DNSSEC – RCE from WAN
CVE-2023-50387 7.5 KeyTrap: CPU exhaustion via crafted DNSSEC response
CVE-2022-0934 7.5 Single-byte UAF via crafted DHCP packet – DoS
CVE-2020-25683/87 5.9 DNSSEC heap overflow variants
CVE-2020-25684/85/86 3.7 Transaction ID entropy reduction – cache poisoning amplifiers

CVE-2020-25681 and CVE-2020-25682 are reachable from the WAN: the router forwards DNS queries to an upstream resolver, and a malicious upstream response triggers the overflow in dnsmasq’s DNSSEC validation code. CVE-2023-50387 (KeyTrap) allows a single crafted DNS response to peg the router’s CPU at near-100% until the query times out.

The three cache poisoning amplifiers (CVE-2020-25684/85/86) combine to reduce effective DNS transaction ID entropy from 16 bits to roughly 14 bits, making a Kaminsky-style off-path cache poisoning attack practical.

miniupnpd ~2.1 – NULL Pointer Dereferences

CVE CVSS Description
CVE-2019-12108 7.5 NULL pointer dereference in GetOutboundPinholeTimeout (int_port)
CVE-2019-12109 7.5 NULL pointer dereference in GetOutboundPinholeTimeout (rem_port)
CVE-2019-12111 7.5 NULL pointer dereference in copyIPv6IfDifferent

All three are denial-of-service: a crafted UPnP request from the LAN crashes miniupnpd. Since miniupnpd is not supervised by a watchdog in this firmware, the UPnP service goes down permanently until the router is rebooted.


Vulnerability Summary

CRITICAL – Plaintext Admin Credentials + Telnet (CVSS 10.0)

/etc/config/control stores admin:1234 in plaintext and sets telnet '1'. Anyone on the LAN can telnet to the router and authenticate with these credentials. No exploitation tooling required.

CWE-256 (Unprotected Storage of Credentials), CWE-306 (Missing Authentication for Critical Function)


CRITICAL – Passwordless Root Account (CVSS 10.0)

The OverlayFS whiteout on /etc/shadow removes the shadow password database at runtime. The root account in /etc/passwd references a shadow entry that does not exist, making root passwordless. Combined with telnet and SSH root login enabled, this is an instant root shell.

CWE-521 (Weak Password Requirements), CWE-732 (Incorrect Permission Assignment)


CRITICAL – Plaintext WiFi PSK and Hardcoded RADIUS Key (CVSS 8.8)

WPAPSK1=20102010 is stored in plaintext. The RADIUS shared secret RADIUS_Key=ralink is the MediaTek SDK default present in every reference design.

CWE-256 (Unprotected Storage of Credentials), CWE-798 (Use of Hard-coded Credentials)


HIGH – LuCI rpcd Plaintext Password (CVSS 8.8)

$p$admin is not a hash. The plaintext password is admin. Full read/write access to all UCI namespaces from any LAN host.

CWE-256 (Unprotected Storage of Credentials)


HIGH – Dropbear SSH Root Password Authentication (CVSS 8.1)

SSH root login with password authentication is enabled. With root being passwordless, ssh root@192.168.188.1 with an empty password delivers a shell.

CWE-306 (Missing Authentication for Critical Function)


HIGH – lighttpd CVE-2019-11072: Signed Integer Overflow (CVSS 9.8)

Content-Length parsing accepts values up to INT64_MAX with no upper bound check. Downstream truncation and overflow in 32-bit arithmetic creates heap corruption. No stack canary or PIE on the binary.

CWE-190 (Integer Overflow)


HIGH – lighttpd CVE-2018-19052: mod_alias Path Traversal (CVSS 7.5)

Alias prefix bypass via URL-encoded path separators. lighttpd runs as root. Arbitrary file read from an unauthenticated request.

CWE-22 (Path Traversal)


HIGH – OpenSSL 1.0.x End-of-Life (CVSS up to 9.8)

66 unpatched CVEs. CVE-2021-3711 (buffer overflow, code execution) is directly applicable to the HTTPS endpoint.

CWE-1104 (Use of Unmaintained Third-Party Components)


HIGH – dnsmasq DNSpooq Heap Overflows (CVSS 8.1)

CVE-2020-25681 and CVE-2020-25682 allow RCE via crafted DNSSEC responses from a compromised upstream resolver. Reachable from WAN without any credentials.

CWE-122 (Heap-based Buffer Overflow)


HIGH – No Binary Exploit Mitigations (CVSS 7.0)

NX only. Every memory corruption finding in lighttpd or dnsmasq is significantly easier to weaponize without canaries, PIE, or RELRO.

CWE-693 (Protection Mechanism Failure)


MEDIUM – Evil-Twin AP via Hardcoded Upstream BSSID + PSK (CVSS 6.5)

Client bridge config contains upstream AP BSSID and PSK. An attacker with these values can impersonate the upstream AP and intercept all WAN traffic.

CWE-798 (Use of Hard-coded Credentials)


MEDIUM – KeyTrap DNS DoS (CVSS 7.5)

CVE-2023-50387 pegs the CPU at near-100% from a single crafted DNS packet. Repeatable indefinitely. Denies internet connectivity to all LAN clients.

CWE-400 (Uncontrolled Resource Consumption)


MEDIUM – miniupnpd NULL Pointer Dereferences (CVSS 7.5)

Three CVEs (CVE-2019-12108/09/11) in the UPnP daemon allow any LAN host to crash the service with a crafted UPnP request.

CWE-476 (NULL Pointer Dereference)


MEDIUM – U-Boot TFTP Boot Attack (CVSS 5.7)

bootcmd=tftp, bootdelay=2, serverip=10.10.10.4. Physical access delivers a U-Boot shell in 2 seconds. Network-adjacent: a TFTP server at 10.10.10.4 automatically delivers firmware on every power cycle.

CWE-276 (Incorrect Default Permissions)


MEDIUM – Firewall Accepts All LAN Input (CVSS 5.3)

LAN zone input ACCEPT. Every service is reachable from every LAN host with no restriction.

CWE-1188 (Insecure Default Initialization)


Remediation

Finding Fix
Plaintext credentials in control Remove telnet; store passwords as salted SHA-512 hashes
Shadow whiteout / passwordless root Restore shadow file with a strong root password; disable root login over telnet/SSH
WiFi PSK plaintext Generate per-device credentials at manufacturing; do not store PSK in plaintext
RADIUS key “ralink” Replace with a randomly generated per-device secret
rpcd \$p\$ plaintext Use a proper hash scheme; force password change on first boot
lighttpd 1.4.45 Upgrade to >= 1.4.76
OpenSSL 1.0.x Replace with OpenSSL 3.x or LibreSSL
dnsmasq 2.80 Upgrade to >= 2.90; disable DNSSEC if not required
miniupnpd 2.1 Upgrade to >= 2.3.6; disable UPnP if not required
No binary hardening Recompile with -fstack-protector-strong -fpie -pie -Wl,-z,relro,-z,now
U-Boot TFTP Set bootdelay=0; change bootcmd to flash-only boot
Firewall LAN ACCEPT Implement per-service accept rules; default LAN policy to DROP

The Shortest Path to Root

From any device on the LAN:

$ telnet 192.168.188.1

pro login: admin
Password: 1234

root@pro:~# id
uid=0(root) gid=0(root) groups=0(root)

Or over SSH:

$ ssh root@192.168.188.1
root@192.168.188.1's password: [empty -- press Enter]

root@pro:~# id
uid=0(root) gid=0(root) groups=0(root)

Both paths require no tooling beyond a standard terminal and deliver a root shell in under five seconds.


Closing Thoughts

The most dangerous combination here is the plaintext admin:1234 in control, telnet enabled by default, and the shadow whiteout giving root a blank password. Three separate unforced errors that stack into an instant root shell from the LAN. Any device connected to this router – a laptop, a phone, a smart TV – can gain root without any exploitation skills or tooling.

The firmware is based on the MediaTek MT7628 SDK reference design, and several of the issues (RADIUS key ralink, the TFTP bootcmd, the default WiFi PSK) are SDK defaults that were never overridden for production. This is a common pattern with ODM/white-label router firmware: the vendor ships the SDK reference config, changes the branding, and calls it done.

On the library side, the combination of EOL OpenSSL 1.0.x, unpatched dnsmasq 2.80, and no binary exploit mitigations means that any of the reachable CVEs has a lower exploitation bar than it would on a hardened device. CVE-2020-25681 in dnsmasq is particularly concerning: it is reachable from the WAN without any credentials, requires no prior access to the device, and the heap overflow lands in a binary with no canary or ASLR.

All findings were identified through static analysis of the firmware image only.

--- // --- // --- // ---