Rooting, firmware analysis and persistent credentials of TP-Link TL-841N

Aug 02, 2026 11:19 PM - 4 hours ago 3

2 Aug 2026

Now, fto maine conscionable preface this pinch a disclaimer:

… and get utilized to this puppo, arsenic you whitethorn spot this a fewer much times successful this station :’).

In ray of continuing my bumbling foray into the hardware hacking landscape, I bought a bottom-of-the-line $10 TP-Link TL-841N(EU) router disconnected a random chap connected Facebook Marketplace, successful a thrilling, high-stakes woody conducted successful the mediate of… a adjacent mall.

My volition down specified a daring speech was to practice the end-to-end process of pulling isolated an IoT device to flick astatine it, access debug logs, perchance get a guidelines shell, believe various kinds of firmware extraction (via UART & via on-chip flash memory), and moreover person a mosey astir the filesystem for immoderate imaginable information vulnerabilities later down the road.

Here’s a short overview of the travel truthful far, documented below:

1. Cracking it open: Finding the UART & getting connected 2. Dumping the firmware (Method 1. - UART & tftp) 3. Dumping the firmware (Method 2. - on-chip flashrom spot extraction) 4. Un-squashing the guidelines filesystem 5. Having a Look-see - Preliminary Snooping/Analysis

Hang astir until the extremity for discovering what kinds of various plaintext, hardcoded creds from the erstwhile proprietor (censored) could beryllium recovered connected this device… 1 of which would past even a afloat “router reset”.

Yeah. Kinda… nary bueno?

Let’s excavation in.


I began by searching the router exemplary number up connected the FCCID to cheque for its datasheet, to get accusation connected the PCB, voltage levels & the disposable chips connected the board.

Opening it up and uncovering the very-well-labelled UART ports for serial/debug interface access was straightforward, pinch 1 quirk being that the flash was recovered connected the underside of my committee - exemplary number GD25Q64CSIG ( datasheet).

Finding the UART ports (four "traffic light" holes connected correct picture) connected the shelled router (left)

If your instrumentality isn’t arsenic friends pinch labelling, you tin besides usage a multimeter to probe each suspected UART larboard astatine footwear clip to find which pin is which - the device’s TX pin should person a fluctuating voltage during boot (due to it transmitting various footwear logs successful the shape of serial 1s and 0s, corresponding to fluctuations successful voltage), and the GND pin should beryllium 0V.

Probing the suspected UART pins astatine footwear until I recovered 1 pinch a fluctuating voltage level (i.e. not the modular dependable 3.3V aliases 0V for my router), indicating it's apt the `TX` port.

My trusty AliExpress USB-to-UART adapter (which I checked to corroborate supports some 3.3V and 5V output, truthful we don’t fry anything) had the pursuing pinout/wire colouring:

# Four-color DuPont terminal wiring RED: 5V BLACK: GND GREEN: TX WHITE: RX

So, I connected the USB GND to the router’s GND larboard (or tin conscionable touch grounded metallic connected the board), USB RX to the discovered router’s TX port, and USB TX to router’s suspected RX larboard (which tin beryllium recovered by trying the remaining 2 pins, until you get a successful input by spamming a cardinal connected your keyboard erstwhile connected).

I soldered excavation successful for a better/persistent connection, & plug-&-play functionality 😜.

Soldering connected the `GND`, `RX` and `TX` leads to entree the debug interface hands-free!

Once I identified what /mnt/dev instrumentality my USB-to-UART instrumentality was and sewage connected up pinch picocom -b 115200 --logfile bootlog-tplink.txt /dev/tty.usbserial-1210 (using the communal baud complaint of 115200, which worked), we tin spot we’re dropped into a guidelines ammunition erstwhile again! Too easy 😅.

(I besides attempted to link my telephone to the generated TP-Link Wifi network, which generated the other logs seen above)

Connecting my machine straight via 1 of the router’s LAN ports, we tin beryllium assigned an IP and entree the admin console arsenic expected:

Home saccharine home... but really do we get in?

So, now I could spot & navigate the filesystem, I wanted to dump & transportation it to my section instrumentality to execute immoderate preliminary analysis!

Listing the flash partitions astatine /proc/mtd, arsenic good arsenic a fewer different equine details, gives america a bully constituent to start:

Some preliminary specifications astir the router's configured representation partitions

Preparing our receiving TFTP server

So we tin transportation the firmware files over, I installed & started a tftp-now server pinch tftp-now service connected my Mac (with brew, arsenic americium connected pome silicon).

NOTE: Ensure to create a files successful your location directory corresponding to wherever you’re transferring the record from connected your router (for me, from ~/var), different you’ll get a “No specified record exists” correction erstwhile the tftp supplier attempts to put its distant record connected your machine (as it does truthful astatine the aforesaid way that it’s uploading it from, /var/).

To lick the (many) errors shown below, I created a impermanent var files wrong my ~ location directory to lucifer up pinch the record way the router’s tftp wanted to “put” to:

tftp-now server connected my Mac

Now, link your study machine to the shelled router's LAN port, & cheque that it's gets assigned an IP (like `192.168.0.200`) truthful they tin talk to 1 another, contempt not having net access.

Switching backmost to our UART relationship to the router, and checking we tin statesman to create a dump of the first flash partition pinch a instrumentality for illustration feline (if your router doesn’t person dd, for illustration mine).

NOTE: Keep an oculus connected your router’s storage - if yours is besides afloat (like excavation was - usage df to check), constitute the firmware dumps incrementally into the /var directory, which is RAM-backed and my largest partition (still only ~6.4 MiB).

To debar overloading the RAM, for each partition dump, I utilized the beneath process:

  1. **Dump flash partition pinch feline /dev/mtd0 > /var/boot.bin
  2. Transfer it complete to the IP reside of your receiving study laptop pinch tftp -p -l /var/boot.bin 192.168.1.100
  3. Delete transferred flash partition pinch rm /var/boot.bin, and repeat.
# Process: cat /dev/mtd0 > /var/boot.bin tftp -p -l /var/boot.bin 192.168.1.100 rm /var/boot.bin # Repeat supra process for each sector, rm'ing arsenic you go cat /dev/mtd1 > /var/kernel.bin cat /dev/mtd2 > /var/rootfs.bin cat /dev/mtd3 > /var/config.bin cat /dev/mtd4 > /var/romfile.bin cat /dev/mtd5 > /var/rom.bin cat /dev/mtd6 > /var/radio.bin

Tedious, but really it had to beryllium (unless performing an on/off-flash spot extraction, aliases you person a USB interface connected your router to constitute to).

However, for a CLI value of life boost arsenic stated successful this article, “we tin make our lives overmuch easier by uploading our ain fully-loaded BusyBox via the existing TFTP customer connected the device. You whitethorn find a precompiled little-endian MIPS BusyBox binary connected the official website.” This will springiness america entree to significantly much modular linux commands to usage connected the router.

Again, well, it should beryllium evident, but…

We’re learning. It worked successful the end. Firmware files extracted to my section machine complete tftp & UART!

An replacement measurement of analysing the filesystem is via an on-chip (or off-chip, if you want to desolder a mini IC and person a basking aerial gun) firmware extraction. For this, I utilized the " poor man’s flash extraction tool" - the CH341A.

I first recovered the flash spot - connected the backmost of the committee - and checked it was a flash spot pinch it’s exemplary number. This besides told maine that the flash rom voltage is astatine 3.3V ( datasheet) truthful the CH341A one person is safe to use. Some chips tally astatine 1.8V, not 3.3V, truthful earlier cautiously placing the clip connected the clip and plugging successful the programmer, check your chip’s voltage (via online datasheets aliases multimeter). If it’s 1.8V, usage a voltage adapter (bought pinch the adapter) to debar imaginable damage.

Also, guarantee the clamp-side’s reddish cord is successful statement pinch to PIN 1 of the router’s flash chip, which should beryllium (typically marked pinch a mini circle successful the corresponding corner).

The on-chip extraction, pinch the circle marking wherever to align the area of the clip pinch the reddish ligament starring to it

Beginning of the spot exemplary number often corresponds to type: truthful for our 25Q64CSIG, the 25 astatine the commencement intends we would position our 8 pins complete the slots marked arsenic 25 SPI BIOS, whereas if our spot said 26, it would spell connected the EPROM I2C side.

After seating the PCB & connected cord successful the USB programmer with the RED cord connected the apical right, arsenic pictured below, double cheque that the reddish cord connected the clamp-side is attached to PIN 1 of the router’s flash chip (typically marked pinch a mini circle, arsenic mentioned before).

The on-chip extraction, pinch the circle marking wherever to align the area of the clip pinch the reddish ligament starring to it

- [Video explaining the CH341A relationship process successful item (Matt Brown)](https://www.youtube.com/watch?v=6Z5aWu9tqmA&t=207s)

Then, simply instal & occurrence up flashrom, and tally the pursuing bid to extract the firmware!

flashrom --programmer ch341a_spi -r modem.bin

Now we person modem.bin, which (being a afloat flash dump) should incorporate wrong it, the aforesaid manually-extracted flash partitions arsenic extracted via ftpd. However, to analyse it much meaningfully, we tin divided it up into the various boot, config, rootfs sectors for illustration earlier pinch dd.

Manually carving retired the flash sectors

Using the memory sizes/addresses recovered earlier pinch /proc/mtd erstwhile connected the router, we tin manually carve our modem.bin flash dump into the identified sectors for analysis.

The commands tally earlier connected the router to representation retired the flash representation sectors

To get our first sector, mtd0, which presumably contains U-Boot based connected our observed footwear logs, we tin usage dd to carve the specified chunk retired of modem.bin:

dd if=modem.bin of=mtd0-boot.bin bs=1 count=$((0x20000)) # Search for strings identifying u-boot strings -a mtd0-boot.bin | grep -Ei 'u-boot|boot|version|console|baud|mtd|flash'

In this case, my U-boot assemblage wasn’t initially identified by binwalk automatically erstwhile I first analysed modem.bin, owed to apt being a customised, vendor-modified type of U-boot. Hence why I decided to carve retired the sectors manually according to the router’s /proc/mnt information, conscionable for immoderate further accuracy and organisation.

Using `binwalk` to analyse the dumped firmware's structure

The beingness of a customised U-boot was confirmed pinch strings uncovering Ralink UBoot Version successful the mtd1 partition (the presumed linux kernel) erstwhile this portion was carved retired and analysed later on, indicating this was a Ralink-modified U-Boot version.

Anyway, moving the strings bid again connected the extracted suspected U-boot mtd0 partition confirms it is the portion of the flash containing U-Boot, and helps america place much info astir it, including U-Boot’s type number and behaviour.

Using `strings` to extract accusation astir the U-Boot sector

I repeated the supra process to carve our each assemblage of the firmware dump, until I had each sectors, mtd0-mtd6, for analysis.

# Carving retired each assemblage from afloat modem.bin flash dump dd if=modem.bin of=mtd0-boot.bin bs=1 count=$((0x20000)) dd if=modem.bin of=mtd1-kernel.bin bs=1 skip=$((0x020000)) count=$((0x140000)) dd if=modem.bin of=mtd2-rootfs.bin bs=1 skip=$((0x160000)) count=$((0x660000)) dd if=modem.bin of=mtd3-config.bin bs=1 skip=$((0x7C0000)) count=$((0x010000)) dd if=modem.bin of=mtd4-romfile.bin bs=1 skip=$((0x7D0000)) count=$((0x010000)) dd if=modem.bin of=mtd5-rom.bin bs=1 skip=$((0x7E0000)) count=$((0x010000)) dd if=modem.bin of=mtd6-radio.bin bs=1 skip=$((0x7F0000)) count=$((0x010000))

I asked the friends (?) neighbourhood AI chatbot to thief representation a ocular summary of the spot truthful far:

0x000000 ┌───────────────────────────┐ │ │ │ mtd0 │ │ Custom U-Boot 1.1.3 │ │ Ralink │ 128 KiB │ 0x020000 ├───────────────────────────┤ │ │ │ mtd1 │ │ Linux kernel │ │ │ 0x160000 ├───────────────────────────┤ │ │ │ mtd2 │ │ SquashFS │ │ 6.6 MiB │ │ │ 0x7C0000 ├───────────────────────────┤ │ mtd3 config │ 64 KiB 0x7D0000 ├───────────────────────────┤ │ mtd4 romfile │ 64 KiB 0x7E0000 ├───────────────────────────┤ │ mtd5 rom │ 64 KiB 0x7F0000 ├───────────────────────────┤ │ mtd6 power │ 64 KiB 0x800000 └───────────────────────────┘

If we usage binwalk -e to extract what it tin automatically from the original modem.bin, it besides gives america the uncompressed kernel image (mtd1) successful the shape of decompressed.bin, which checks retired erstwhile looking astatine its identified magic bytes below:

Verifying that `binwalk` detects the linux kernel image connected our 2nd `mtd1` sector

Another logic that I had to carve retired and extract the mtd2 squashfs filesystem manually pinch dd was, erstwhile again, because I’m connected a mac. And the sasquatch bid initiated arsenic portion of binwalk’s extraction process (when extracting a squashfs sector) continued to neglect for maine (despite being installed…),

Oh well. At slightest we cognize what to do from supra to get and un-squash the rootfs for analysis:

# extract the squashfs filesystem directly dd if=modem.bin of=mtd2-rootfs.bin bs=1 skip=$((0x160000)) count=$((0x660000)) # cheque it's squashfs file mtd2-rootfs.bin # unsquash it (uses xz) to explore unsquashfs -no-xattrs mtd2-rootfs.bin

Note: If you get errors for illustration create_inode: could not create characteristic instrumentality squashfs-root/dev/zero, because you're not superuser!, don’t panic. As the filesystem contains instrumentality nodes nether /dev, your machine (MacOS for example) whitethorn not licence a normal personification to create those typical instrumentality files. However, they’re (allegedly) not usually needed for firmware analysis, but you tin usage axenic Linux to unsquash this while preserving the instrumentality nodes pinch sudo.

(yes, one cognize this would’ve conscionable “all worked” connected linux. one americium waiting for my mediocre mac babe to dice earlier the last leap.)

Comparing the 2 extraction methods (left done via Method 1, tftpd, and correct via on-chip extraction, Method 2), we tin spot nearly identical copies of the filesystem to analyse!

Comparing the results of our 2 extraction methods

Now, we tin proceed to analyse the dumped binaries/unsquashed filesystems utilizing strings, grep, and binwalk, to understand really the router useful and possibly moreover uncover immoderate juicy vulns later down the track…!

But, I’ll time off the groundbreaking vulnerability reversing to portion 2 (or much tin souls than I), arsenic this is getting long, but acknowledgment for sticking on for the ride! We’ve eztracted firmware from a router successful 2 ways; via tftp complete UART, arsenic good arsenic by utilizing flashrom for a nonstop on-flash-chip firmware extraction. \

But… we should person a peek earlier we go, shouldn’t we? Just a little?

NOTE: I americium conscionable poking astir present to study much astir what is/isn’t exposed & accessible connected a reasonably emblematic IoT instrumentality that I ain and person afloat authority over. The instrumentality was mill reset earlier being sold to me, and nary discovered credentials were utilized to entree immoderate unauthorised material.

Now, to present you to the inexpensive & nasty firmware analyst’s favourite tool: strings.

As stated connected it’s man page, strings "…looks for ASCII strings successful a binary file, object, aliases modular input.". So we tin quickly parse compiled .bin files and the for illustration for a soiled peek astatine immoderate readable strings that whitethorn beryllium within, which is astir apt to beryllium either matter the programme prints, aliases plaintext configuration values (depending connected compilation type, of course).

Examining the mtd3-config.bin first, wherever router-specific configs should live, pinch strings mtd3-config.bin | less, we tin really find the router’s hard-coded WPS password successful plaintext, i.e. the 1 printed connected the backmost of the router!

Finding the hardcoded plaintext WPS password, located connected the bottommost of the router

Well, uh, upon further research: we’ve stumbled upon what appears to beryllium a antecedently discovered “CVE” ( CVE-2026-4346), successful fact:

“The vulnerability affecting TL-WR850N v3 allows cleartext retention of administrative and Wi-Fi credentials successful a region of the devices flash representation while the serial interface remains enabled and protected by anemic authentication. An attacker pinch beingness entree and the expertise to link to the serial larboard tin retrieve delicate information, including the router’s guidance password and wireless web key. Successful exploitation tin lead to afloat administrative power of the instrumentality and unauthorized entree to the associated wireless network.”

(Although I’d say, if someone’s sewage hardware entree to your router’s serial interface/flash spot successful the first place, you’re already beautiful borked. And I wouldn’t telephone it protected by “weak authentication”… no authentication.)

Another gem uncovered by strings mtd3-config.bin | grep -ri "pass" are hardcoded credentials for what appears to beryllium erstwhile PPPoe ISP/router provisioning credentials (the @unitiair information powerfully suggesting an ISP/service-provider account).

The hardcoded plaintext PPPoe ISP customer credentials, presumably from the erstwhile router's proprietor (verified via OSINT)

Oopsies.

And for an moreover bigger oopsies/yikes, even aft performing a router mill reset, these credentials were still accessible via the freshly-dumped config partition of the firmware.

Given that I americium not the section mini business whose email is listed successful plaintext successful the un-redacted type of the image above, nor a customer of Unitiair, I don’t judge I should still beryllium seeing this aft the router was “reset”.

This intends that not moreover a FACTORY reset of the device (holding down RESET fastener for 10 seconds, and verifying reset was performed by checking footwear logs, arsenic elaborate successful the TP-Link documentation), which astir consumers would presume would swipe each personification OR ISP-modified settings from it, can genuinely erase the ISP PPPOe WAN credentials.

Boot logs during the router's mill reset

Comparing the first firmware dump to the factory-reset firmware dump

So, a mill reset leaves a erstwhile subscriber’s broadband authentication credential recoverable from persistent flash storage., successful Firmware Version: 0.9.1 4.16 v0001.0 Build 170622 Rel.64334n (the latest for my EU exemplary of the router)

The router's listed firmware version

Whilst you don’t look to beryllium capable to authenticate to this ISP itself (which, luckily for them, requires an relationship number to login, dissimilar my ISP which uses email), user/ISP modified credentials stored successful plaintext persisting aft a reset is… not a awesome privateness motion for what is considered a “factory reset” device.

Anyway…

Searching for much “sensitive” strings successful the extracted root-fs (mtd2), we get some the router login from ./web/help/RestoreDefaultCfgHelpRpm.htm, arsenic good arsenic the hashed password of the admin OS user.

grep -RniE 'password|passwd|credential|admin' . 2>/dev/null ... ./web/help/RestoreDefaultCfgHelpRpm.htm:20:<li class="admin">Default User Name<B> - admin</B>.</lI> ./web/help/RestoreDefaultCfgHelpRpm.htm:21:<li class="admin_0">Default Password<B> - admin</B>.</lI>... ./etc/passwd.bak:1:admin:$1$$iC.dUsGpxNNJGeOm1dFio/:0:0:root:/:/bin/sh

`grep`ping the admin account's password hash

So, we tin login to the router admin interface acknowledgment to the aureate keys of… admin:admin. Of course!

Behind the gates we sneezed connected to open...!

Additionally, arsenic we recovered an UNSALTED admin hash from /etc/shadow, we tin effort and ace the admin OS personification account’s password (whose permissions are mapped to root, GUID=0, truthful an relationship that’s kinda useful):

  • Wordlists
## Create password hash file printf '%s\n' '$1$$iC.dUsGpxNNJGeOm1dFio/' > admin.hash ## cheque it pinch cat cat admin.hash ## download a wordlist (see link) & usage pinch hashcat to ace MD5 hash (easy, arsenic has nary salt) hashcat -m 500 -a 0 admin.hash common.txt --status

Our maestro relationship password was, of course, cracked wrong a fewer seconds…

Cracked successful 1,2,3,4...

admin:1234 - fun times. i’m quaking successful my boots :’).

well, until adjacent clip - spot you for the squeakquel!

(Jeanette is my tone animal)
More