The TP-Link TL-WR841N is a relatively inexpensive 300mbps WiFi router. This makes it a perfect device for reverse engineering and hardware hacking projects. My goal here was to gain shell access via the router's UART debugging port and see what can be done from there. Please note that this guide is specific to v14.6 of the router — some details may be slightly different for earlier or later hardware versions. Included in this repo is a partial file system dump of the router, as well as a firmware dump.
- CH340G
- CH341A
- Digital multimeter
- Header pins
- Jumper wires
- Soldering kit
- AsProgrammer
- PuTTy
- Tftpd64
The UART serial port is primarily used during the manufacturing process for debugging purposes. You can find UART ports on virtually any IoT device. With the proper tools, it is possible to access the shell using this port. From there, you can dump the firmware, flash hacked firmware, get device info, give yourself remote/backdoor access, and more. The UART pin layout is: GND
, TX
, RX
, VCC
.
To avoid shorting both the TL-WR841N router and the serial USB device, we must first confirm that we are using the correct voltages. This is an important step, as using the wrong voltage can damage the router, the serial USB device, or both. UART ports are typically either 3.3V, or 5V. Thankfully, this particular version of the router has pre-labeled pins — so there is no ambiguity over which pin is RX
, TX
, GND
, or VCC
.
Using a multimeter, I tested and confirmed a 3.3V VCC
, GND
, and TX
pins. The RX
pin reads at 0.0V, because it is essentially a listening port, just waiting for information. We can safely assume RX
receives at 3.3V, just like the rest of the pins. Once the voltage is confirmed to be 3.3V, the next step is to connect the 3.3V serial USB converter.
After confirming that the UART port works at 3.3V and soldering a set of header pins, we can safely connect our USB serial converter. Next we use a serial communication program to interface with the device and read the output. For this step, we connect GND-GND
, RX-TX
, and TX-RX
. It is not necessary to connect VCC
, so long as we set our serial USB device to use 3.3V.
Once we have secured a connection between the serial USB converter to the router's UART port, we can now modify the settings in PuTTy for serial communication. The main value we need to know is the UART port's baud rate. It is possible to use a logic analyzer to figure out what the baud rate is. However, there are several baud rates that are very common, so it is often easier to guess common values until one works. In this particular case, the router's UART runs at 115200 baud rate. Other baud rates will produce garbage on the screen. So we will use the following settings in PuTTy:
- Baud rate: 115200
- Data bits: 8
- Stop bits: 1
- Parity: None
- Flow control: None
After gaining access to the shell, I used the Linux command echo $USER
to confirm root access of the device. We know we have achieved this, as the device returns root
.
TL-WR841N-root-shell.mp4
At first I was only able to access the shell, but not issue commands — I was stuck in a read-only console. I learned that this is because TP-Link shorts the RX
pin of the UART port as a security measure. There is a single voltage-limiting resistor just next to the RX
pin on this particular model. Pulling that resistor out with a pair of tweezers did the trick, and I was then able to write to the console and issue commands. Take note of the missing resistor at R18
just above the RX
pin.
To extract the router's file system, we will archive each directory, then upload to our PC using TFTP. Using the busybox
command, we can see that the router uses a BusyBox with limited functionality.
While it has the tftp
function which will be useful for uploading files from the router, there are no commands available to us to archive directories/files.
To be able to archive directories and upload them, we will need to first upload a BusyBox binary with more functions defined. You can find a link to the one I used here.
~ # busybox
BusyBox v1.19.2 (2022-08-16 12:08:08 CST) multi-call binary.
Copyright (C) 1998-2011 Erik Andersen, Rob Landley, Denys Vlasenko
and others. Licensed under GPLv2.
See source distribution for full notice.
Usage: busybox [function] [arguments]...
or: busybox --list[-full]
or: function [arguments]...
BusyBox is a multi-call binary that combines many common Unix
utilities into a single executable. Most people will create a
link to busybox for each function they wish to use and BusyBox
will act like whatever it was invoked as.
Currently defined functions:
arping, ash, brctl, cat, chmod, cp, date, df, echo, free, getty, halt,
ifconfig, init, insmod, ipcrm, ipcs, kill, killall, linuxrc, login, ls,
lsmod, mkdir, mount, netstat, pidof, ping, ping6, poweroff, ps, reboot,
rm, rmmod, route, sh, sleep, taskset, tftp, top, umount, vconfig
Using the mount
command, we can see that most of the router's file system is read only. While rootfs
, /proc/
and /sys/
are technically read-write,
Linux typically restricts any write operations to these directories for security reasons. The only location we can reliably write to is the /var/
directory.
~ # mount
rootfs on / type rootfs (rw)
/dev/root on / type squashfs (ro,relatime)
proc on /proc type proc (rw,relatime)
ramfs on /var type ramfs (rw,relatime)
/sys on /sys type sysfs (rw,relatime)
To upload our new BusyBox, we connect our router to our computer, either with a Cat 5 cable or wirelessly. Using Tftp64, we prepare our new BusyBox binary for upload.
We then cd
over to the var/tmp
directory, and use tftp
to download the new binary, change its access permissions with chmod
, and then execute.
Uploading-BusyBox-TFTP.mp4
With our new BusyBox binary, we can now archive directories. Using ls
command from the root directory, we can see there are a total of 11 directories, as linuxrc
is a script.
So to extract the file system, we just need to archive each individual directory, and upload it via TFTP to our PC.
~ # ls
web usr sbin mnt lib dev
var sys proc linuxrc etc bin
Archiving-and-Uplading-Directories-via-TFTP.mp4
With /web/
archived and uploaded, we just have 10 directories left. It should be noted that some of these directories, particularly /dev/
, /proc/
, and /sys/
are
system-reserved directories that contain mostly symbolic links and other files which cannot be properly archived. In any case, we should be able to extract most of the file system using these methods.
With the file system successfully extracted, the next logical step is to extract the firmware. Upon closer inspection of the board, it appears to use the cFeon 25QH32 series IC chip for storing its firmware. With the CH341A programmer, there are two ways to go about extracting the firmware from the IC chip:
- Place the programmer's SOP clips directly on the chip.
- Desolder the chip, and resolder it onto the programmer's SPI board.
I had trouble getting a read on the IC chip using the first method. My hypothesis is that with this particular version of the board, reading the firmware with the SOP clips also powers the microprocessor, which interferes with the programmer's ability to get a proper read. Instead, I used the second method and desoldered the chip from the router, and resoldered it directly onto my programmer's SPI board.
The SPI programmer was then able to extract the firmware with no issues. AsProgrammer does a good job at auto-detecting the firmware chip ID, so all that needed to be done was click 'Read IC', and voila — it worked. Now here is what that firmware dump looks like from a hex editor. Notice the highlighted strings from the bootloader.
It should also be noted that the firmware chip on this board operates at 3.3V, and there is a known design flaw with the CH341A programmer where the data lines operate at 5V, regardless of how the programmer is configured. There are plenty of tutorials online on how to mod the CH341A to work at 3.3V, which I recommend doing to avoid shorting your board's firmware chip. You can find one such tutorial here.
Router.Tetris.mp4
While this router has very light resources and a mostly read-only file system, it is possible to get it to run simple terminal games. I was able to get the router to play Tetris and Snake. If you wish to get the router to play games, you will want to find games that are either written in pure C or C++, without any dependencies. You will then need to use Buildroot to compile the game as a statically-linked MIPS-32 little endian binary. Once you have Buildroot setup, here are the commands to compile:
# Compile C binary with uClibc toolchain
mipsel-buildroot-linux-uclibc-gcc -mabi=32 -mfp32 -EL -static -o program program.c
# Compile C++ binary with uClibc toolchain
mipsel-buildroot-linux-uclibc-g++ -mabi=32 -mfp32 -EL -static -o program program.cpp
# Strip binary
mipsel-buildroot-linux-uclibc-strip program