📜 ⬆️ ⬇️

Explore Chinese routers on RT5350

Once, lolipop bought a router on aliexpress. But not a simple router, but a very compact and cheap, with 2 Ethernet ports, USB, and even from a company that sold its mp3 players in the Russian Federation in the early 2000s: Nexx WT1520H.
image
Standard firmware, as almost always, was scarce, and, of course, I wanted to replace it with something more sane. But bad luck - there are no alternative firmware for the router, and it is not clear how to flash it, because no other firmware was received via the web interface, I had never seen a firmware header like this before, and didn’t find binwalk in it, so it was encrypted:
00000000 32 33 35 30 6b d9 39 00 00 00 0e 02 00 00 00 00 |2350k.9.........| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 3e 19 53 c5 |............>.S.| 00000020 63 f5 51 9f 82 74 2d 03 2e 2f 1f 32 9c 4a 93 96 |cQ.t-../.2.J..| 00000030 15 82 23 d0 b2 7e d7 1b 13 c3 1b 1f 06 fa f8 e0 |..#..~..........| 00000040 bb 43 9b c6 ee fc 4b 7a e6 50 71 2b f4 f3 95 c3 |.C....Kz.Pq+....| 00000050 63 d0 a3 9c 92 2e 16 c6 19 1c 4a 93 cb 95 c3 63 |c.........J....c| 00000060 d2 9b 1a f5 2e 16 c6 19 1c 4a 93 f9 68 3c 9c 73 |.........J..h<.s| 00000070 14 63 d5 10 5e d3 6b 25 2b c2 2e 07 eb 85 73 25 |.c..^.k%+.....s%| 00000080 9b 6b c0 f2 d8 9b cf 65 56 ac a9 c2 28 61 dd 55 |.k.....eV...(aU| 00000090 18 a4 5b e9 ba 11 93 ec 30 76 4f 40 c1 f0 7c cb |..[.....0vO@..|.| 000000a0 36 d3 b3 93 fe 3d 6b 10 66 fa 43 39 f2 f6 c0 91 |6....=kfC9....| 

lolipop merged data from a flash drive through a programmer, and we started poking around at them.

Firmware study


For the entire process of initializing the hardware and running the programs, its own init is responsible - / sbin / rc. This is such a combo abruptly busybox, which contains almost all the logic of the router:
Hidden text
 .text:0040E758 .globl start_services .text:0040E758 start_services: # CODE XREF: main+14C8p .text:0040E758 # DATA XREF: main+14C0o ... .text:0040E758 .text:0040E758 var_10 = -0x10 .text:0040E758 var_8 = -8 .text:0040E758 .text:0040E758 li $gp, 0x7EBF8 .text:0040E760 addu $gp, $t9 .text:0040E764 addiu $sp, -0x20 .text:0040E768 sw $ra, 0x20+var_8($sp) .text:0040E76C sw $gp, 0x20+var_10($sp) .text:0040E770 la $t9, start_syslog .text:0040E774 nop .text:0040E778 jalr $t9 ; start_syslog .text:0040E77C nop .text:0040E780 lw $gp, 0x20+var_10($sp) .text:0040E784 nop .text:0040E788 la $t9, start_proftpd .text:0040E78C nop .text:0040E790 jalr $t9 ; start_proftpd .text:0040E794 nop .text:0040E798 lw $gp, 0x20+var_10($sp) .text:0040E79C nop .text:0040E7A0 la $t9, start_telnetd .text:0040E7A4 nop .text:0040E7A8 jalr $t9 ; start_telnetd .text:0040E7AC nop .text:0040E7B0 lw $gp, 0x20+var_10($sp) .text:0040E7B4 nop .text:0040E7B8 la $t9, load_smb_driver .text:0040E7BC nop .text:0040E7C0 jalr $t9 ; load_smb_driver .text:0040E7C4 nop .text:0040E7C8 lw $gp, 0x20+var_10($sp) .text:0040E7CC nop .text:0040E7D0 la $t9, sys_led_init .text:0040E7D4 nop .text:0040E7D8 jalr $t9 ; sys_led_init .text:0040E7DC li $a0, 2 .text:0040E7E0 lw $gp, 0x20+var_10($sp) .text:0040E7E4 nop .text:0040E7E8 la $t9, start_upnp .text:0040E7EC nop .text:0040E7F0 jalr $t9 ; start_upnp .text:0040E7F4 nop .text:0040E7F8 lw $gp, 0x20+var_10($sp) .text:0040E7FC nop .text:0040E800 la $t9, start_dhcpd .text:0040E804 nop .text:0040E808 jalr $t9 ; start_dhcpd .text:0040E80C nop .text:0040E810 lw $gp, 0x20+var_10($sp) .text:0040E814 nop .text:0040E818 la $t9, start_ntpc .text:0040E81C nop .text:0040E820 jalr $t9 ; start_ntpc .text:0040E824 nop .text:0040E828 lw $gp, 0x20+var_10($sp) .text:0040E82C nop .text:0040E830 la $t9, start_dns .text:0040E834 nop .text:0040E838 jalr $t9 ; start_dns .text:0040E83C nop .text:0040E840 lw $gp, 0x20+var_10($sp) .text:0040E844 nop .text:0040E848 la $t9, start_ddns .text:0040E84C nop .text:0040E850 jalr $t9 ; start_ddns .text:0040E854 nop .text:0040E858 lw $gp, 0x20+var_10($sp) .text:0040E85C nop .text:0040E860 la $t9, start_igmp_proxy .text:0040E864 nop .text:0040E868 jalr $t9 ; start_igmp_proxy .text:0040E86C nop .text:0040E870 lw $gp, 0x20+var_10($sp) .text:0040E874 nop .text:0040E878 la $t9, start_ipmac_bind .text:0040E87C nop .text:0040E880 jalr $t9 ; start_ipmac_bind .text:0040E884 nop .text:0040E888 lw $gp, 0x20+var_10($sp) .text:0040E88C nop .text:0040E890 la $t9, start_block_ipmac .text:0040E894 nop .text:0040E898 jalr $t9 ; start_block_ipmac .text:0040E89C nop .text:0040E8A0 lw $gp, 0x20+var_10($sp) .text:0040E8A4 nop .text:0040E8A8 la $t9, start_block_port .text:0040E8AC nop .text:0040E8B0 jalr $t9 ; start_block_port .text:0040E8B4 nop .text:0040E8B8 lw $gp, 0x20+var_10($sp) .text:0040E8BC nop .text:0040E8C0 la $t9, start_ddos .text:0040E8C4 nop .text:0040E8C8 jalr $t9 ; start_ddos .text:0040E8CC nop .text:0040E8D0 lw $gp, 0x20+var_10($sp) .text:0040E8D4 nop .text:0040E8D8 la $t9, start_monitor_rate .text:0040E8DC nop .text:0040E8E0 jalr $t9 ; start_monitor_rate .text:0040E8E4 nop .text:0040E8E8 lw $gp, 0x20+var_10($sp) .text:0040E8EC nop .text:0040E8F0 la $t9, start_upgraded .text:0040E8F4 nop .text:0040E8F8 jalr $t9 ; start_upgraded .text:0040E8FC nop .text:0040E900 lw $gp, 0x20+var_10($sp) .text:0040E904 nop .text:0040E908 la $t9, start_conntrack_limit .text:0040E90C nop .text:0040E910 jalr $t9 ; start_conntrack_limit .text:0040E914 nop .text:0040E918 lw $gp, 0x20+var_10($sp) .text:0040E91C nop .text:0040E920 la $t9, start_macfilter .text:0040E924 nop .text:0040E928 jalr $t9 ; start_macfilter .text:0040E92C nop .text:0040E930 lw $gp, 0x20+var_10($sp) .text:0040E934 nop .text:0040E938 la $t9, start_black_management .text:0040E93C nop .text:0040E940 jalr $t9 ; start_black_management .text:0040E944 nop .text:0040E948 lw $gp, 0x20+var_10($sp) .text:0040E94C nop .text:0040E950 la $t9, start_wlan_wps .text:0040E954 nop .text:0040E958 jalr $t9 ; start_wlan_wps .text:0040E95C nop .text:0040E960 lw $gp, 0x20+var_10($sp) .text:0040E964 nop .text:0040E968 la $t9, start_trakerurl .text:0040E96C nop .text:0040E970 jalr $t9 ; start_trakerurl .text:0040E974 nop .text:0040E978 lw $gp, 0x20+var_10($sp) .text:0040E97C lw $ra, 0x20+var_8($sp) .text:0040E980 move $v0, $zero .text:0040E984 jr $ra .text:0040E988 addiu $sp, 0x20 .text:0040E988 # End of function start_services 

By deduction and nmap, it was found out that telnetd was launched on the router, which is accessible via the WAN interface! Here it is! However, it was impossible to log in either as the root user or as the admin user.
As telnetd performs busybox. Let's look at it (the login_main function):

image
')
T-aa-ak, now allows to enter a password, but the standard password "admin" is not accepted. Interesting. Look further:

image

That's how it is! Login nexxadmin, password y1n2inc.com0755, with access via WAN.
There is mtd_write in the firmware, so nothing prevents us from pouring OpenWRT right into the flash right now, which was done by lolipop , but it was still interesting to reverse the encryption algorithm. Unfortunately, my MIPS assembly skills in my head are clearly not enough, and I experienced great discomfort just by looking at all this code, so I ordered the same router for myself, and, oh, a miracle, after 2 months I had it.

We continue the study


image
Only RX is connected, the ground is shared with a laptop by USB-powered.

The router can be updated either via the web interface or via tftp (which, again, listens to the WAN!). The tftp daemon (upgraded from rc) seems to be broken, because updating through it did not lead to updating the firmware in the router, although there were no errors. It should be noted that updating firmware via tftp requires authentication with the same password as on the web interface, so it’s hard to call it backdoor, rather, it’s just an incorrectly configured service.

I decided to investigate the firmware update through tftp.
A simple upgrade via a standard tftp client resulted in “Upgrade not possible: Incorrect Password” from upgraded. Let's take a look at it:

image

Regular clients do not seem to be able to send tftp options, so I downloaded the tftpy python library, modified one line in the sample client file, and it all worked:
 --- tftpy_client.py 2014-09-30 21:48:57.375550027 +0400 +++ tftpy_client.py_ 2014-09-30 21:48:50.355520342 +0400 @@ -83,7 +83,7 @@ progresshook = Progress(tftpy.log.info).progresshook - tftp_options = {} + tftp_options = {'admin': ''} if options.blksize: tftp_options['blksize'] = int(options.blksize) if options.tsize: 

Fine! Now it remains to deal with encryption firmware. In rc, there is a decrypto function, it looks like this:

image

I could not find the key in the statics, so I started debugging. To debug something on the device, of course, you must first build a debugger. linux_server from IDA Pro is not built for MIPS (and the router is built on this architecture), so it was necessary to somehow build gdbserver as a router. The router uses the old-age kernel 2.6.21 with uClibc 0.9.28. First of all, I decided to use buildroot, so that he and the toolchain with uClibc assembled, and gdbserver statically. Ok, everything is set up, it seems to start, however, when debugging via IDA Pro, the server is constantly crashing, some strange errors ptrace gives, well, I think you need to rebuild it with headers from the 2.6.21 kernel and with uClibc 0.9.28, t .to. uClibc never promised binary compatibility. On the Internet, I found the Ralink SDK with the correct kernel version and uClibc. The GDBServer compiled by this toolchain behaved one-to-one like the old one. Unfortunately, IDA Pro has some incompatibility with gdbserver, which is running on MIPS. Fortunately, gdbserver works fine with regular gdb compiled under mips (./configure --target mipsel-linux).
I very rarely debug something in bare gdb, and convenient add-ins and scripts for it work only with x86 and ARM. Fortunately, I found a repository with .gdbinit for MIPS , and the convenience of debugging has increased noticeably. Everything happened like this:

image

Ultimately, the key was found only a few lines above, which was to be expected:

image

After I wrote a script to decrypt the firmware, lolipop sent me another one with a similar header, but from another router. The first 4 characters (magic) she had were R3G2. Looking for this line in Google, it turned out that everything was already done before us, even as early as the beginning of 2013: (
In any decrypted firmware there is a line of Linux Kernel Image, which is always at the same offset. This string is longer than the XOR key, which means that we do not need to get it from rc from different manufacturers, and we can simply “find” it from this string.

But it is not enough just to unpack the firmware; you also need to pack it in order to be able to update it via the web interface. As it turned out, the update function contains the checksum calculation of the firmware:

image

What looks like in C like this:
  for (i=0; i<f.len-1; i+=2) { checksum += (data[i+1] << 8 ) | data[i]; } if (i < f.len) { checksum += data[i]; printf("Got odd byte: 0x%02X\n", data[i]); i+=1; } checksum = checksum + (checksum >> 16) + 0xffff; checksum = ~(checksum + (checksum >> 16)) & 0xffff; printf("Checksum = 0x%04X\n", checksum); data[i] = checksum & 0xFF; data[i+1] = (checksum >> 8) & 0xFF; 

Now we have everything we need to upload any firmware via the web interface.

Links

Decrypt
Cipher

Conclusion

This method is suitable for many SoC RT5350 devices. This is probably some kind of regular way to update the firmware from the SDK.
Firmware with backdoor and such poor quality force the consumer to look for normally working software for the device. lolipop has added support for this device in OpenWRT, and it will soon be added to Trunk. Also, yesterday, the release of OpenWRT Barrier Breaker finally came out! (Announcement on the site yet).
That's how we live.

Source: https://habr.com/ru/post/238713/


All Articles