Blog: Internet Of Things

ZTE MF910 – An end of life router, running lots of vivacious hidden code

G Richter 10 Aug 2019

You might be here because you saw our talk at Defcon 27. You might want to watch that for the full rundown!

The ZTE MF910 is a really interesting router for reversing, mainly because it’s full of nice debug calls, and underused functionality. Also, it’s never going to get patched, and it’s really cheap. So it’s a great 4G router to start messing around with.

This post gives a bit of a rundown of the debug functionality and bugs we found in the ZTE MF910. The same (or similar) API calls might be found in other ZTE MF* series routers. We’re not entirely sure, because ZTE aren’t exactly proactive at fixing issues reported to them.

Have fun!

Embedded Web Servers

Unlike typical web server behaviour, where files stored on the filesystem are parsed and returned, many embedded web servers rely on functionality internally-defined in the binary itself.

Whenever looking at a device with an embedded web server, it’s worth decompiling the web server binary and seeing what kind of internal functionality it has. Often, you’ll find debug or other unused endpoints which might be useful.

Here’s a list of a few interesting and buggy endpoints we found in the MF910. There’s loads more in the zte_topsw_goahead binary to have a look at and find, which will be left as an exercise for the reader :)

Fun Stuff

An API for the ZTE “syslog” (post-authentication)

Remote syslog

Enables a remote syslog mode, where ZTE “syslog” information is sent over UDP to port 514 on the requesting host.

Local syslog

Same, but for local logging.

Kernel syslog

Same again, but for kernel logging

Download syslog  file

Just for downloading locally-stored logs. Seems to check for validity of the filename – can either be syslog or syslog.0.

Various ADB mode switch endpoints

All of these functions just put the device into ADB mode on the USB interface. There are two which can only be called post-authentication, and one that can be called pre-authentication.

Post-authentication 1

Post-authentication 2

Note: usb_mode takes an int value by default, but is not sanitized and is injectable on the MF910.


Extraneous endpoints


This is definitely just old code. Searching for the default value of address

As you can see from a rough decompile, this function is really simple:

So it’s a perfect candidate for XSS.

This is definitely just old code. Searching for the default value of the “address” parameter, you can see that it’s old test code from GoAhead web server.

There’s a lot more

There are a lot more endpoints which aren’t used during normal use, but are still in the code.

Not all are interesting for our purposes, but it demonstrates really how much debug or simply old code might end up in these kinds of devices.


We reported a couple of bugs in the MF910 to ZTE. They told us the device is end of life, so they won’t be fixing them. We also found similar issue sin the MF920 – which is still supported – and ZTE told us they fixed them.

Information Leak

It’s possible to read arbitrary values from the configuration table in the device NVRAM. A request like the following will read the value of the admin_Password field, which – you may be able to get – is the administrator password for the device.


Command Injection

The post-authentication debug function USB_MODE_SWITCH is injectable, so it’s possible to execute arbitrary commands on the MF910.;

Cross-Site Scripting

We didn’t report this to ZTE in the first place, because we weren’t trying to chain to write a CSRF exploit. But since they won’t fix it in any of their other devices based on a report on the MF910 anyway, it’s not worth our time to report. Anyway, this XSS can be used to bypass the CSRF protections on the device so the two above exploits can be chained to execute arbitrary code from another page context.<script>alert(“XSS_GOES_HERE”)</script>


The simplest way to exploit these issues remotely, is the following flow:

    • XSS to inject javascript into the /goform/formTest page
      • This allows our JavaScript to run in the broswer page context which means we bypass the ZTE Referer header filter.
      • It also means we don’t have to worry about the Same Origin Policy and read back responses.
    • Request and read back the admin_Password

  • Send a login request with that password.
  • Send a request to the USB_MODE_SWITCH goformId with arbitrary commands in.

Check out  for a quick PoC.


08/02/2019 – We try to get in touch with ZTE PSIRT

12/02/2019 – We follow up because no response

13/02/2019 – ZTE send PGP key

13/02/2019 – We send encrypted disclosure of MF910 & MF65+ issues.

14/02/2019 – ZTE confirm receipt.

21/02/2019 – ZTE reply to say that MF910 & MF65+ are end of life & superceded by MF920 and MF65M2

21/02/2019 – We reply, noting that the MF910 and MF65+ are still being sold by vendors, and asking if MF920 & MF65M2 are still supported. We also ask about CVE IDs (since ZTE are a CNA)

27/02/2019 – ZTE reply to the question as to why they are still selling products they consider end of life:

“the internal delisting announcement of each product will be released in time, and the external delisting announcement will be released only when the customer explicitly requests it”

27/02/2019 – We ask again for a list of supported products, and their end of life dates.

28/02/2019 – ZTE reply saying:

“Unless the carrier customer requests the product delisting announcement,there is no public product delisting announcement.”

01/03/2019 – We reply, asking why they’ve previously created CVE IDs for lower-risk issues, on end of life products. We also ask again for them to confirm that the issues we reported don’t affect any more of their products.

12/03/2019 – ZTE reply saying still no CVE for these because the product is end of life. Still don’t confirm which products are actually supported. Also don’t confirm whether the same issues would be found in other products in their MF* line.

12/03/2019 – We reply saying the same issues are obviously present in public firmware for the MF920. Ask them to confirm that these issues are not present in other devices in their MF* line. Ask again for a list of officially currently-supported devices.

13/03/2019 – They reply asking for details on the MF920 issues, including which firmware version they were tested on. These are essentially the exact same issues as those for the MF910.

13/03/2019 – We reply with firmware details.

15/03/2019 – ZTE reply saying they’ll check. They also ask:

“the Web interface “goformId=USB_MODE_SWITCH” and “syscmd=zte_syslog&syscall=set_locallog” is Internal interface,

I mean user cannot get this interface information by regular webui operation.

How did you find this interface?”

15/03/2019 – We reply, saying we reversed the zte_topsw_goahead binary.

18/03/2019 – ZTE reply saying that they removed nc from the MF920, so the exact PoC I sent them which used nc on the MF910 didn’t work on the MF920, so they assumed it wasn’t affected. It actually is. They also ask exactly what tools we used for RE.

18/03/2019 – We reply. I used IDA, so I told them about IDA. By then Ghidra had been released so I told them about Ghidra. I told them they would also have found these issues by manual code review. I ask about whether they also confirmed the information leak issue. Also asked AGAIN for a list of supported devices.

18/03/2019 – ZTE reply saying the information leak isn’t there. They also offer to tell me a list of devices if I tell them which country I live in.

18/03/2019 – We send a curl command for them to replicate the info leak. Also make the point that I just want a list of devices, so I can also import them from other countries if I want to.

21/03/2019 – ZTE confirm information leak.

22/05/2019 – We chase for an update on these being fixed.

05/06/2019 – We chase again.

06/06/2019 – ZTE reply with a link to an advisory, which is also de-indexed from their main vulnerability listings page.

ZTE assigned a couple of CVEs for issues we found in the MF920:

CVE-2019-3411: Info leak

CVE-2019-3412: Command execution