Full compromise of the TP-Link WR841N wireless router

For educational purposes only.


Still in the process of getting familiar with basics of hardware security and exploitation, I acquired a second-hand TP-Link router which is famous for being both cheap and vulnerable, which is perfect for experimentation, considering that I fried the last router I played with.

The goal is to do a full compromise of the router and gain physical root-level access.

Recon and hardware enumeration

Luckily for us, we can begin our reconnaissance work before the router is delivered. Every device emitting radio waves being sold in the US must submit an application to the Federal Communication Commission, and the documents related to the application are public and searchable. The file contains, among other things, internal and external pictures of the device, as well as some specs. Sadly, at the time of writing this, the FCC website is no longer operational, due to “a partial lapse in federal government funding” :(.

All we can get from external FCC data providers12 are external pictures and cover letters.

Fast-forward a few days later, the router arrived and I started the hands-on recon and enumeration. Here are some pics I took of the outside of the device:

And the inside:

With those images, we can clearly identify the main chips present on the board:

We also see 4 empty via pads spaced 2.54mm apart at the top right, which is very likely an UART interface (they are super easy to spot when looking at the board from behind, almost impossible to miss them). All the identified components are annotated here:

Accessing the UART console

Upon closer look, we can see that one of the UART pins may be airgapped with a missing resistor connector (R87):

I thought I may have to bridge the two empty resistors pins together. As the pads are labelled, I was pretty sure that Tx was left open so an attacker cannot easily send commands. I tried to plug in using my TTL-to-USB device and got the following:

$ picocom -b 115200 /dev/ttyUSB0 

// ... picocom headers removed for readability

[04080B0F][04080C0F][8B7F0000][29273E3A][0029273B]
DU Setting Cal Done


U-Boot 1.1.3 (Sep  3 2020 - 16:10:48)

Board: Ralink APSoC DRAM:  32 MB
relocate_code Pointer at: 81fc0000
flash manufacture id: 1c, device id 70 16
Warning: un-recognized chip ID, please update bootloader!
============================================ 
Ralink UBoot Version: 4.3.0.0
-------------------------------------------- 
ASIC 7628_MP (Port5<->None)
DRAM component: 256 Mbits DDR, width 16
DRAM bus: 16 bit
Total memory: 32 MBytes
Flash component: SPI Flash
Date:Sep  3 2020  Time:16:10:48
============================================ 
icache: sets:512, ways:4, linesz:32 ,total:65536
dcache: sets:256, ways:4, linesz:32 ,total:32768 

 ##### The CPU freq = 580 MHZ #### 
 estimate memory size =32 Mbytes
RESET MT7628 PHY!!!!!!
continue to starting system.
 0 
disable switch phyport...
   
3: System Boot system code via Flash.(0xbc010000)
do_bootm:argc=2, addr=0xbc010000
## Booting image at bc010000 ...
   Uncompressing Kernel Image ... OK
No initrd
## Transferring control to Linux (at address 8000c150) ...
## Giving linux memsize in MB, 32

Starting kernel ...


LINUX started...

// ... boot sequence snipped for readability

turn off flow control over.
starting pid 79, tty '/dev/ttyS1': '/bin/sh'
~ #

After a few seconds, what looks like a Shell prompt got displayed, but I cannot send anything, which does not confirm my initial theory of Tx being airgapped with the missing R87 resistor (we wouldn’t receive anything).

It appears that the real culprit is R18: it looks like a pull-down resistor bringing Rx to GND, hence blocking the input.

I tried unsoldering R18, but did not succeed. Unsoldering SMT is a big pain in the arse without flux and a hot air gun. I looked online and found a write-up from Zero Day Initiative having the same issue as me, and solving it by using a 5V Rx signal to provide enough voltage make the pull-down resistor ineffective. I did the same and was able to gain a properly working shell on the machine:

First thing to do, check what’s in /etc/passwd:

~ # cat /etc/passwd
admin:$1$$iC.dUsGpxNNJGeOm1dFio/:0:0:root:/:/bin/sh
dropbear:x:500:500:dropbear:/var/dropbear:/bin/sh
nobody:*:0:0:nobody:/:/bin/sh

I threw the admin (uid=0) user password hash in hashcat and got the clear-text password almost immediately:

1234, nice :]

Firmware extraction and analysis

Now that I’ve got a root shell using UART, I want to try to extract the firmware and poke around it. The EN25Q32 chip has a pretty standard pinout:

I struggled with flashrom setup rewired the chip to my FT232H device many times, and ended up using --force to make the firmware dump work.

$ sudo flashrom \
  -p ft2232_spi:type=232H,divisor=8 \
  -c "EN25Q32(A/B)" \
  -r firmware.bin --force

Despite the errors, firmware.bin seems to be a properly formatted file:

I then used binwalk again to extract the SquashFS section and explore the firmware:

$ binwalk -e firmware.bin

From here, it’s an usual Linux filesystem and we can start exploring it, looking for bugs, hardcoded credz and vulnerabilities.

Non-exhaustive list of identified vulns

After exploring the firmware (I uploaded it here), I identified multiple attack vectors that demonstrate TP-Link poor security posture:

  • Hardcoded default credentials in /web/frame/login.htm (L494, L537, L569). Basically, admin/admin. A fresh new router can be fully compromised with curl -u admin:admin http://<router ip>.
  • YOLO permissions on the filesystem, set in /etc/init.d/rcS.
  • Poor password hashing mechanism. As seen at the end of the UART exploitation part, the passwords are hashed using MD5, both on the system (/etc/passwd.bak:1) and in the web application (/web/js/tpEncrypt.js:1)
  • No CSRF protection whatsoever.
  • Command injection via diagnostic tools in /web/main/diagnostic.htm. Some JS functions (wanTest(), interTestDns() and interTest()) build a system command with user input without sanitizing it. This seems to be similar to CVE-2020-35576, but with a older router version (mine is v14 and not v13). Another recent CVE, CVE-2025-9377 is also covering a similar attack against older versions of the router, but with another parameter (the CVE is using url_0 parameter to inject code).

Ending word

This project has been a fun introduction to hardware and IoT security, and the TP-Link WR841N is a really good target to get started, and attacks are well-documented online.

There is still plenty of things I would like to do on this router: trying a chip-off firmware extraction if I can get my hands on a hot air gun, remove R18 to be able to have a read/write console on the UART interface without have to use a different voltage, try to backdoor the firmware and flash it back…