Firmware Patching: Fixing the TEW-632BRP

Customizing firmware images can be a very useful skill, allowing you to add or unlock features, fix bugs, and patch vulnerabilities when vendors can’t (or won’t) do so in a timely manner.

A while ago I found that my Trendnet TEW-632BRP and TEW-652BRP routers had a TFTP service running on both the LAN and WAN interfaces that allowed anyone to download the device’s configuration file without authentication:

embedded@ubuntu:~/TEW632$ tftp
tftp> get /tmp/etc/nvram.conf
Received 19897 bytes in 0.0 seconds
tftp> quit
embedded@ubuntu:~/TEW632$ head nvram.conf

After contacting the vendor they verified the vulnerability and issued a firmware update that disables TFTP access from the WAN. However, they insisted on leaving TFTP accessible from the LAN “for repair purposes”. I’d much rather have TFTP disabled completely, so in this tutorial we’ll be patching the Trendnet firmware in order to completely disable TFTP. The patching process for the TEW-632BRP is also pretty simple, so it makes for a good introduction to firmware patching too.

Grab the latest TEW-632BRP firmware from Trendnet’s Web site here. If you don’t have it already, also be sure to grab the latest versions of binwalk and the firmware mod kit.

[NOTE: Although the firmware mod kit allows you to automatically extract and re-build the TEW-632BRP’s firmware, you don’t learn much from that and it would make for a pretty boring tutorial. Instead, we’ll be using some of the SquashFS utilities included in the firmware mod kit in order to patch the firmware manually.]

In order to patch the TFTP vulnerability, we’ll need to edit the system’s start up scripts in order to stop the TFTP server from running on boot. Let’s start out by running binwalk against the firmware image to see if we can identify the file system:

embedded@ubuntu:~/TEW632$ binwalk TEW632BRPA1_FW110B31.bin 

0         	0x0       	uImage header, created: Wed Jun  8 03:35:19 2011, image size: 813719 bytes, Data Address: 0x80060000, Entry Point: 0x80290000, CRC: 0x15ED049D, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: Linux Kernel Image
64        	0x40      	LZMA compressed data, dictionary size: 8388608 bytes, uncompressed size: 2429062 bytes
1048576   	0x100000  	Squashfs filesystem, big endian, version 3.0, 2447513 bytes, 571 inodes, blocksize: 65536 bytes, created: Wed Jun  8 03:35:38 2011

Binwalk identified a uImage header at the beginning of the firmware image, followed by an LZMA compressed kernel and a SquashFS filesystem. Interestingly, the image size reported in the uImage header is 813,719 bytes while the SquashFS file system is located 1,048,576 bytes into the firmware image. This suggests that the checksum in the firmware header is only calculated over the LZMA kernel and not the file system.

Let’s look at a hexdump of the firmware image at offset 1,048,576 (hex 0x100000) to see if there’s a separate header for the file system:

embedded@ubuntu:~/TEW632$ hexdump -C TEW632BRPA1_FW110B31.bin | less
000c6aa0  bf c4 bf 07 c0 64 a9 8b  95 6b 60 69 f4 be 0d ba  |.....d...k`i....|
000c6ab0  2a ea 6d 4c 34 e4 8a 03  93 20 d7 34 bf 2b 77 61  |*.mL4.... .4.+wa|
000c6ac0  45 75 40 2b 46 cc c3 0d  a2 55 9f 7f d2 2e c7 dc  |Eu@+F....U......|
000c6ad0  5b c2 af e1 d0 00 00 00  00 00 00 00 00 00 00 00  |[...............|
000c6ae0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00100000  73 71 73 68 00 00 02 3b  9c bf ac 40 42 bf ac 40  |sqsh...;...@B..@|
00100010  7b 00 81 aa 00 f0 2d 94  00 00 00 00 00 03 00 00  |{.....-.........|
00100020  82 cf 00 10 40 02 02 4d  ef 50 7a 00 00 00 00 10  |....@..M.Pz.....|
00100030  52 05 e0 00 01 00 00 00  00 00 32 f6 3d 4e 2e 00  |R.........2.=N..|
00100040  00 00 00 00 25 58 99 00  00 00 00 00 25 58 89 00  |....%X......%X..|
00100050  00 00 00 00 25 58 91 00  00 00 00 00 25 32 28 00  |....%X......%2(.|
00100060  00 00 00 00 25 44 16 00  00 00 00 00 25 58 81 ff  |....%D......%X..|

There is a big section of null padding immediately before the ‘sqsh’ SquashFS magic bytes, but no header. This indicates that we should be able to replace the existing file system in the firmware image without needing to fix the checksum value in the firmware header. Although uImage headers are well documented and re-generating a valid header for this firmware image would not be difficult, this makes our job that much easier.

The last thing we need to look for is a firmware footer. Looking at the last few lines of the firmware image, we see that there is a footer starting at offset 3,866,624 (hex 0x3b0000):

embedded@ubuntu:~/TEW632$ hexdump -C TEW632BRPA1_FW110B31.bin | tail
00355850  9f 2c 56 03 f0 68 08 a1  67 8d 21 86 9e 72 da bc  |.,V..h..g.!..r..|
00355860  4b 73 58 f5 28 2b 68 e8  96 e0 a2 1d 4e 43 f2 22  |KsX.(+h.....NC."|
00355870  a2 61 3e 3a 9a b0 c8 e1  30 57 30 ff ff f0 4a 40  |.a>:....0W0...J@|
00355880  00 00 00 00 00 00 25 57  4f 00 00 00 00 00 00 01  |......%WO.......|
00355890  f4 00 00 00 05 00 00 00  06 00 00 00 00 00 00 00  |................|
003558a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
003b0000  41 50 38 31 2d 41 52 39  31 33 30 2d 52 54 2d 30  |AP81-AR9130-RT-0|
003b0010  37 30 36 31 34 2d 30 30                           |70614-00|

The footer contains references to the chip sets supported by the firmware; this is usually used to ensure that end users don’t upload incompatible firmware images to the device. We shouldn’t need to change any of this data but we will need to include the footer in our modified firmware image.

OK, now that we’ve identified the major pieces of the firmware image, let’s dd the SquashFS data from the firmware and extract the system files using the unsquashfs utility from the firmware mod kit:

embedded@ubuntu:~/TEW632$ dd if=TEW632BRPA1_FW110B31.bin bs=1 skip=1048576 count=2447513 of=tew632.squashfs
2447513+0 records in
2447513+0 records out
2447513 bytes (2.4 MB) copied, 4.27572 s, 572 kB/s
embedded@ubuntu:~/TEW632$ sudo /opt/filesystems/squashfs/unsquashfs3.0-lzma tew632.squashfs 
Reading a different endian SQUASHFS filesystem on tew632.squashfs

created 414 files
created 44 directories
created 73 symlinks
created 40 devices
created 0 fifos

Let’s try to find where the tftpd binary is started by grepping through all of the extracted files for references to ‘tftpd’ (be sure to exclude the dev directory!):

embedded@ubuntu:~/TEW632/squashfs-root$ grep --exclude-dir=dev tftpd * -R 2>/dev/null
Binary file sbin/tftpd matches
Binary file sbin/rc matches

Taking a look inside sbin/rc we quickly find that it is the culprit:

embedded@ubuntu:~/TEW632/squashfs-root$ strings sbin/rc | grep tftp
killall tftpd &
option tftp %s

If the rc file was a start up script it would be easy to edit out the tftpd service, but since it’s an executable ELF file, it will be easier to just replace the tftpd binary:

root@ubuntu:~/TEW632/squashfs-root# echo -e '#!/bin/sh\necho "Refusing to start TFTPD!"' > sbin/tftpd
root@ubuntu:~/TEW632/squashfs-root# cat sbin/tftpd 
echo "Refusing to start TFTPD!"

OK, now we need to re-package our modified file system into a SquashFS image. Again, we’ll use the firmware mod kit’s SquashFS utilities. Note that since the original SquashFS image was big endian, we need to supply the -be flag to mksquashfs in order to build our image in big endian byte order as well:

embedded@ubuntu:~/TEW632$ sudo /opt/filesystems/squashfs/mksquashfs3.0-lzma squashfs-root/ tew632_modified.squashfs -be
Creating big endian 3.0 filesystem on tew632_modified.squashfs, block size 65536.

Big endian filesystem, data block size 65536, compressed data, compressed metadata, compressed fragments
Filesystem size 2381.04 Kbytes (2.33 Mbytes)
	27.90% of uncompressed filesystem size (8533.83 Kbytes)
Inode table size 4577 bytes (4.47 Kbytes)
	25.54% of uncompressed inode table size (17922 bytes)
Directory table size 4935 bytes (4.82 Kbytes)
	51.80% of uncompressed directory table size (9527 bytes)
Number of duplicate files found 24
Number of inodes 571
Number of files 414
Number of fragments 49
Number of symbolic links  73
Number of device nodes 40
Number of fifo nodes 0
Number of socket nodes 0
Number of directories 44
Number of uids 2
	root (0)
	unknown (-201261056)
Number of gids 2
	unknown (83886080)
	unknown (100663296)

embedded@ubuntu:~/TEW632$ ls -l *.squashfs
-rwx------ 1 root     root     2441216 2011-06-19 05:59 tew632_modified.squashfs
-rw-r--r-- 1 embedded embedded 2447513 2011-06-19 05:41 tew632.squashfs

Now we need to extract the first part of the original firmware image, which includes the firmware header, kernel, and the null byte padding between the kernel and the file system. As we saw earlier, the file system starts at offset 1,048,576, so we we’ll take everything up to that offset:

embedded@ubuntu:~/TEW632$ dd if=TEW632BRPA1_FW110B31.bin bs=1048576 count=1 of=fw.part1
1+0 records in
1+0 records out
1048576 bytes (1.0 MB) copied, 0.00797675 s, 131 MB/s

Now we need to extract the firmware footer, which starts at offset 3,866,624:

embedded@ubuntu:~/TEW632$ dd if=TEW632BRPA1_FW110B31.bin bs=3866624 skip=1 of=fw.part2
0+1 records in
0+1 records out
24 bytes (24 B) copied, 5.463e-05 s, 439 kB/s

Let’s concatenate our modified file system image with the first part of the original firmware:

embedded@ubuntu:~/TEW632$ cp fw.part1 tew632_new.bin
embedded@ubuntu:~/TEW632$ sudo cat tew632_modified.squashfs >> tew632_new.bin 
embedded@ubuntu:~/TEW632$ ls -l tew632_new.bin 
-rw-r--r-- 1 embedded embedded 3489792 2011-06-19 06:15 tew632_new.bin

Our firmware image is now 3,489,792 bytes long; the footer should be located at offset 3,866,624, so we’ll add 376,832 bytes of padding and then concatenate the footer to our image:

embedded@ubuntu:~/TEW632$ perl -e 'print "\x00"x376832' >> tew632_new.bin
embedded@ubuntu:~/TEW632$ cat fw.part2 >> tew632_new.bin 
embedded@ubuntu:~/TEW632$ ls -l *.bin
-rw-r--r-- 1 embedded embedded 3866648 2011-06-08 18:35 TEW632BRPA1_FW110B31.bin
-rw-r--r-- 1 embedded embedded 3866648 2011-06-19 06:19 tew632_new.bin

Now for the moment of truth! Let’s upload our modified image:

And test the TFTP service:

embedded@ubuntu:~/TEW632$ tftp
tftp> get /tmp/etc/nvram.conf
Transfer timed out.


Success! The patched firmware can be downloaded here. Now on to fix the other bugs in the TEW-632…

Bookmark the permalink.

8 Responses to Firmware Patching: Fixing the TEW-632BRP

  1. PodeCoet says:

    Awesome, simply awesome! I’m tempted to buy a couple of routers to toy with after reading your firmware hacking tutorials

    • Craig says:

      Thanks! Glad you’re enjoying them. If you want to mess around with routers for cheap, I suggest eBay (of course) or Amazon. Amazon has a great return policy; you can play around with the router for 30 days then return it for little or no charge. Also they don’t check for warranty-void-if-removed stickers… 🙂

  2. Gi0 says:

    Once again:Craig, congrats and thank you for another fine article and for updating binwalk!

  3. Snack says:

    Fantastic article! Really enjoyed it. Can’t wait to see what new stuff you guys will post :D.

  4. leon says:

    great follow up to your first firmware hacking article! always a good read 🙂

  5. Pingback: /dev/ttyS0 » Blog Archive » Firmware-Mod-Kit Updated, v0.69 Released

  6. Sarim Khan says:

    Excellent guide as previous one. I was trying to do something similar but failed. I have a D-Link 2750U H/W Ver: C1 router. It came with a Indian Firmware last updated on 2011. I googled and downloaded another 2750U firmware which is from D-Link Russia. Also i got one which is from D-Link Africa. I straightforwardly uploaded the african one and bricked the device. After sending to warranty i got a new router of same model.

    Now the african and russian image contains CFE bootloader and i dont want to replace CFE.

    My Goal is to replace the filesystem of indian firmware with the filesystem of russian one.

    binwalk of indian:

    256 0x100 Squashfs filesystem, little endian, non-standard signature, version 4.0, compression:gzip, size: 6834613 bytes, 911 inodes, blocksize: 65536 bytes, created: Wed Dec 14 07:11:38 2011
    6836492 0x68510C LZMA compressed data, properties: 0x6D, dictionary size: 4194304 bytes, uncompressed size: 3734240 bytes

    binwalk of russian:

    0 0x0 Broadcom 96345 firmware header, header size: 256, firmware version: "8", board id: "6328AVNG_N31", ~CRC32 header checksum: 0xAD84A1BB, ~CRC32 data checksum: 0xD42DA5BD
    15044 0x3AC4 LZMA compressed data, properties: 0x6D, dictionary size: 4194304 bytes, uncompressed size: 142812 bytes
    60064 0xEAA0 Squashfs filesystem, little endian, non-standard signature, version 4.0, compression:gzip, size: 5381177 bytes, 986 inodes, blocksize: 65536 bytes, created: Mon Apr 1 17:16:18 2013
    5442220 0x530AAC LZMA compressed data, properties: 0x6D, dictionary size: 4194304 bytes, uncompressed size: 3345728 bytes

    Though binwalk can’t detect that in indian one, first 256 bytes are header and looks pretty same in both files.

    The indian one contains filesystem and kernel. the russian one contains cfe, filesystem, kernel.

    So i extracted the filesystem from russian one, the header and kernel from indian one using DD.

    cat header(256 byte, indian) + (filesystem, rus) + (empty bytes) + (kernel, indian) > newimage

    I added the empty bytes so that the kernel appears at the exact same byte as shown in binwalk indian image.

    But the webui rejects the image says its invalid. I bet is there is some checksum/hash/md5 that i need to match. Any idea how to proceed ?

  7. Benedikt M. says:

    Hey, Craig.
    I am really enjoying your firmware hacking posts, so I went to Amazon and bought this router for around 20€:

    I want to develop some skills in finding exploits and making my own firmware for it.
    I picked this device, because it is already supported by OpenWrt which tells me, that I will be able to break into it somehow (but I haven’t read how ;)).
    Is there a free alternative to your decompiler?

    Greetings, Benedikt

Leave a Reply

Your email address will not be published. Required fields are marked *