Greenwave C4000XG Root Exploit

Around the time that GPT4 I tried using it to help me gain root access via UART on a ISP-locked device. Painstakingly I disassembled it only to find a tease in that the logs were exposed but no input was allowed.

From a hardware perspective, the device was locked down pretty tight. No hardware shell, only a modest (and crappy) web UI. I found that there was ssh access but the userspace was extremely limited. So limited, I don't know why they bothered even granting access.

I tried a lot of different tricks with the AI but nothing stuck. Frustrated, I e-wasted the device and moved on with my life.

Until the other day, when I bought another one 'for parts' on eBay for $20. With Opus 4.6 out, lets give it another shot.

Dismantled C4000XG on a bench
Dismantled C4000XG on a very not ESD safe bench

I wrote a custom serial MCP server to allow Claude access to my USB/TTL adapter. We tried some payloads and got the same results as last time. No luck from the hardware side.

This time Claude and I focused on trying to elevate permissions via config backup/restore. This also failed, the device merged config on reboot and had a strict whitelist. We found some diagnostic endpoints and tried to inject into them for RCE but to no avail. Some accepted arbitrary data but it still ran in the same userspace as the admin user.

Port mapping this time found something interesting: a port that used TR-069 protocol to report diagnostics and such back to the ISP. What is this??

TR-069 and ACS

TR stands for 'transfer report' and the 69 (nice) represents the protocol. It's SOAP, in practice, exposed over a port.

On the surface, nothing too gross. Obviously these ISPs need tools to figure out when their equipment is connected, manage updates, etc. Unfortunately the service is pretty far from mundane.

Defaults

Testing with multiple firmware versions, I found that the default TR-069 ACS server was running:

  • with an 'inform' period of 30 seconds, quite aggressive
  • with an HTTP endpoint, with no SSL
  • with hardcoded QA credentials

Oh shit. What can this do?

As it turns out: diagnostics. Nothing that crazy. There were some things like triggering a remote reboot, reset, or firmware upgrade but these aren't exploitable (the firmware is signed + encrypted).

But there was one more thing: Device.IP.Diagnostics.IPPing. A ping diagnostic you can trigger remotely via TR-069. It's loaded in one command with an IP to ping and then executed from another command.

The Key Insight

Earlier, we had tried command injection on every diagnostic endpoint through the web API. Backticks, $(cmd), semicolons, pipes, newlines -- everything was stored literally and never executed. The web API's diagnosticsd daemon uses libcurl and raw sockets to perform pings. No shell involved, no injection possible.

But TR-069 triggers diagnostics through a completely different code path. When IPPing is triggered via CWMP SetParameterValues (through csd, the Connection Services Daemon), the Host parameter is passed to a shell command without sanitization. It shells out to /bin/ping using system() and the Host value is concatenated directly into the command string.

And csd runs as uid 0.

WAN MITM to Root in 20 Seconds

I wrote a fake ACS server in Python, connected my Mac to both the LAN and WAN ports of the router, and ran dnsmasq to simulate an ISP. When the router booted, it got a DHCP lease from my Mac on the WAN side and started sending TR-069 Informs to my ACS server every 30 seconds.

The entire exploit is 4 TR-069 SetParameterValues commands:

spv Device.Users.User.3.Enable=true:boolean
spv Device.X_CTL_CLI.RemoteAccessEnable=true:boolean
spv Device.IP.Diagnostics.IPPing.Host=;echo root:toor|chpasswd;:string
spv Device.IP.Diagnostics.IPPing.DiagnosticsState=Requested:string

Line by line: enable the root user account in the data model, remove dropbear's -g -w flags that block root login, inject chpasswd into the ping command to set root's password, and trigger execution. The diagnostic process inherits root privileges from csd. All 4 commands are sent in a single TR-069 session.

Then:

ssh root@192.168.0.1
# password: toor
# uid=0(root) gid=0(root)

Full root shell, outside the chroot, with all capabilities. Full filesystem access on the overlay, full iptables control, kernel module loading, the works.

Production Implications

In the lab, I pointed the ACS URL to my server via the web UI. But in production, you don't need LAN access at all. The default ACS URL is:

http://pqwesthdm.qwest.motive.com/cwmpWeb/CPEMgt

Plain HTTP. No TLS. No certificate validation. Every C4000XG on CenturyLink's network reaches out to this URL periodically (and pretty frequently, at athat) over the WAN. Anyone positioned on the WAN path can intercept the Inform and respond with the 4-command payload. Root in one session.

It gets worse. Port 7547 is open on the WAN interface with hardcoded connection request credentials: qacafe:qacafe123. "QA Cafe" is a TR-069 testing company -- these are test credentials that shipped in production firmware, identical across all units and firmware versions I tested. Hitting this port with a GET triggers the router to immediately connect to its ACS. You can't send exploit commands directly to 7547 (it rejects POSTs), but combined with DNS poisoning of the ACS hostname, you can trigger and exploit any router whose WAN IP you know.

The mass exploitation scenario writes itself. Poison DNS for pqwesthdm.qwest.motive.com and every C4000XG on the network delivers itself to your ACS within 30 seconds. This isn't theoretical -- the 2016 Deutsche Telekom attack used TR-069 on port 7547 to compromise ~900,000 routers with a similar pattern.

Other Findings

Along the way we found a handful of other vulnerabilities: a NULL pointer dereference in the CGI parameter parser (strtok_s in libsafec crashes on malformed POST bodies), a CGI authentication bypass when POST body is empty but query string has parameters (mitigated by lighttpd's cookie auth, but still a defense-in-depth failure), missing SameSite attributes on session cookies, and CSRF token generation that's enabled but never actually enforced. The UPnP service accepts any port mapping from any LAN client. All services run as root with no privilege separation.

What We Tried That Failed

For the record, here's a partial list of things Claude and I threw at this device before finding the TR-069 injection:

  • Chroot escape from admin SSH: uid 10003, no capabilities, no suid binaries, /proc/1/root is permission denied
  • BPF kernel exploit (CVE-2021-38300): needs 4096 BPF_MSH instructions, kernel limits to 1276 per program
  • Web API diagnostic injection: diagnosticsd uses libcurl directly, no shell involvement
  • Config backup/restore manipulation: device merges config on reboot with a strict whitelist
  • Direct root SSH: dropbear runs with -w -g flags blocking root login; TR-069 data model password doesn't correspond to /etc/shadow
  • NTP/WiFi SSID/hostname injection: values stored literally, services don't shell out on config changes
  • /proc/sys writes: all blocked for uid 10003
  • debugfs/cgroup escape: all blocked, no write access
  • servd (port 12345) writes: confirmed read-only from the chroot after testing 9 different write protocol formats

The crucial thing Claude spotted was that the TR-069 diagnostic path goes through csd while the web API goes through diagnosticsd. Same parameters, different handlers, different security posture. I had already dismissed diagnostic injection as a dead end based on the web API testing.

After Root

With root access secured, I turned to the question of what to actually do with the thing. The stock firmware is a locked-down ISP image with a crappy web UI and no package management.

The C4000XG runs on an Intel/Lantiq GRX500 SoC -- a MIPS chip that Intel sold to MaxLinear in 2020 and was never upstreamed to mainline Linux. The only open-source build system for it is a feed-intel package feed written against kernel 4.9, maintained by the prpl Foundation. I got prplWrt 19.07 booting via TFTP with LAN ethernet working, LEDs, USB, temp sensors, the whole deal. WiFi is a dead end -- Intel's proprietary mtlk driver is a binary blob compiled against kernel 4.9 and nobody is open-sourcing it.

Modern OpenWrt (24.10, kernel 6.6) would require rewriting 66 kernel patches spanning 31K lines against a kernel 7 years newer. I assessed this and shelved it. The practical path is an OpenWrt 19.07 fork with the Intel feed -- same kernel, same hardware support, full OpenWrt userspace with LuCI and opkg.

Closing Thoughts

TR-069 is one of those protocols that exists in the background of millions of ISP-managed devices and nobody thinks about it. The combination of plain HTTP transport, hardcoded credentials, and unsanitized shell execution in the diagnostic path is about as bad as it gets. Every one of these vulnerabilities is individually mundane. Together they form a remotely exploitable, zero-interaction, mass-targetable root chain on hardware deployed to real customers.

I've disclosed these findings to the relevant parties prior to publishing this. Whether you own one of these and want to run your own firmware or you're just here for the exploit archaeology, I hope this was an interesting read.

The full research notes, ACS attack server, and automated exploit script are on GitHub.