Whispers on the Wire: From Copper Pads to a Live UART Root Shell

We often treat embedded devices as impenetrable black boxes, assuming their security relies entirely on the strength of their software and the complexity of their cryptography. But software doesn’t exist in a vacuum; it requires physical silicon to breathe. And if you know where to listen, that silicon loves to talk.

This is the story of my journey into the physical reality of a router; mapping its circuit, translating its electrical pulses into readable text, and reaching an unauthenticated root shell through an exposed UART console.

The Arsenal

Before diving right in, here is the hardware setup I used:

  • The Target Router: A standard TP-Link TL-WR845N (v4.0) to serve as our physical subject.

  • CP2102 USB-to-TTL Adapter: The essential silicon translator that converts the router’s raw hardware-level voltages (3.3 V) into USB data my laptop can understand.

  • The Jtagulator: An automated hardware brute-forcing tool acting as a programmable switchboard to hunt down hidden debug pins.

  • Personal Computer (PC): Equipped with terminal emulators like PuTTY and Picocom to capture, display, and interact with the serial data streams.

The Target and the Theory

Cracking open the plastic chassis of the TP-Link revealed the holy trinity of embedded systems: a MediaTek main processor (SoC), a volatile Zentel SDRAM chip, and a non-volatile 4MB SPI Flash chip.

The immediate instinct for many is to desolder the SPI flash and dump it directly. But firmware at rest on a flash chip is often packed, compressed, or encrypted. However, to actually run the operating system, the SoC has to decrypt and unpack that firmware into the volatile RAM.

If we can tap into the system after it wakes up but before it locks the doors, we get a pristine, unencrypted view of the live operating system. That is the beauty of the Universal Asynchronous Receiver-Transmitter (UART) interface.

The Multimeter Dance

Next to the main SoC sat a teasing, unpopulated 4-pin header. No labels, no silk-screened hints, just four bare copper pads. Before attaching any logic tools, I had to understand the physical layout using a standard digital multimeter.

  • The Anchor (GND): Ground is the absolute reference point for all electrical communication. By setting the multimeter to continuity mode and touching the metal shielding of the Ethernet port, probing the pads quickly revealed the first pin with a continuous beep.

  • The Power Rail (VCC): Switching to DC voltage mode and powering the board, the second pin held a steadfast, unwavering 3.3V.

  • The Heartbeat (TX): The third pin is where the magic happened. Keeping the black probe on Ground, I touched the red probe to the third pad and power-cycled the router. For the first few seconds of the boot sequence, the voltage twitched wildly between 2.8V and 3.3V. That erratic electrical pulse was the processor frantically dumping its boot logs down the wire. That was our Transmit (TX) line.

  • The Listener (RX): By process of elimination, the final, silent pad floating at 0V was the Receive (RX) line, waiting for external input.

A crucial lesson to be learnt here : When bridging this to my laptop using the CP2102 adapter, I connected Ground, crossed TX to RX, and left VCC completely disconnected. Supplying external power to a self-powered board creates a back-feed loop that can instantly fry the main processor or your laptop’s USB controller.

The Cascade of Secrets

With the adapter wired up and PuTTY set to this router’s rate of 115200 baud, I flipped the power switch.

There is a very specific thrill when electrical noise snaps into crisp ASCII text. The U-Boot bootloader spilled the architecture’s deepest secrets across my screen: the CPU frequency, the RAM size, and most importantly, the exact MTD (Memory Technology Device) partition table. The kernel logs explicitly mapped out where the U-Boot, the kernel, and the SquashFS root filesystem lived in the memory blocks.

Then came the grand finale. As the initialization scripts (/etc/init.d/rcS) concluded, the system didn’t bother to ask for a username or password. Instead, it threw this line onto the terminal:

starting pid 78, tty '/dev/ttyS1': '/bin/sh'

A simple press of the Enter key yielded the # prompt. I had total, unauthenticated root access to the live filesystem. Any mathematical security on the router’s web portal was rendered entirely irrelevant by four copper pins.

Automating the Hunt

Of course, not every board has a neatly aligned 4-pin header waiting to be probed. To prepare for hardened targets, I brought in the Jtagulator to automate the brute-forcing of the pins.

Transitioning from manual probing to automation introduces its own set of technical traps. I quickly fell into the “Dual-Serial Trap”, forgetting that the Jtagulator maintains a strict 115200 baud connection to the host laptop, while dynamically shifting speeds on the target pins.

When I set up an active scan (U mode) and it came back empty, my first instinct was to blame timing. Active mode doesn’t test a single fixed rate; it sweeps through a list of standard baud rates per pin permutation, and boot logs only print for a few seconds after power-on. My working theory was that the scan simply hadn’t reached the correct pin/rate combination yet when that window closed, and the router had already moved past its boot sequence by the time the sweep caught up.

But then I looked at the error logs properly. The scan hadn’t failed to find the router in time, it had never gotten the chance to try! Configuring U mode means walking through a full chain of prompts first: channel range, known pins, output string, response delay, LOW-drive behavior. First, a capitalized Y at one of those prompts silently crashed the configuration routine. The CLI parser wanted a lowercase y and nothing else. On the retry, a different input tripped a Value out of range! error, killing the scan before it ever reached the “press spacebar to begin” step. The active scan didn’t send a single byte to the router because it never got that far.

The reliable solution was to stop fighting the configuration and simply listen. Switching to the Jtagulator’s passive continuous mode (T command) and powering on the router, the tool acted as a high-speed digital ear. It caught the noisy boot logs on startup, instantly isolated the TX pin, and calculated the correct baud rate simultaneously. No multi-step setup required, just point it at the channels and watch. From there, I dropped into UART Passthrough (P) mode, turning the Jtagulator into a transparent bridge right back into my root shell.

(One general lesson worth carrying forward, even though it might not be the cause of this particular hiccup: embedded Linux shells expect a true newline, \x0A, to execute a command and not a Windows-style carriage return, \x0D. Speak Windows to a Linux shell and it just stares blankly back at you.)

The Takeaway

There is a profound gap between the theoretical security of compiled code and the physical reality of hardware. Software assumes it is running in a secure, isolated bubble. But when you have physical access to the silicon, you dictate the rules of the environment.

You don’t always have to break the cryptography. Sometimes, you just have to listen to the wires.

2 Likes