Getting a restricted UART shell on the Thomson TG585v8

Getting jailed in a restricted UART shell on the Thomson TG585v8

  Β·  10 min read

For educational purposes only.


As I’ve recently wanted to get into hardware modification/hacking, and in memory of my background in electrical engineering, I bought a second-hand home network router online, the Thomson TG585, for 10 euros. I grabbed this device because for a few reasons. First, it’s cheap so it’s not a big deal if I fry anything on the PCB. Second, it is an old device (the v8 I got has been manfactured in 2010), meaning that it is probably very insecure by design.

My goal is to be able to backdoor the router’s firmware in a persistent way, to make it spin up a daemon that will regularly reach out to a remote server for instructions. Some sort of auto-update daemon, if you like πŸ˜‡.

First look at the PCB #

After removing the main cover, all the principal components are exposed without any protection. We can easily identify the typical 4 pins used for UART, as well as the flash memory chip:

View of the TG585 PCB.

I suspected there was a JTAG debug port above the horizontal quartz, which I confirmed thanks to the FCC’s Exhibits List for this device, on which we can see internal pictures with the connector still attached to the PCB.

The SoC used on this board is a Lantic PSB 50601.

Getting a shell using UART #

Before starting the probing process to identify the UART pins, I wanted to solder PCB connectors to ease the future operations. Unfortunately, the pads are not standard 2.54mm pitches, but probably 2mm ones, and I do not have the right connectors.

So I pulled up my soldering iron and my shaky hands to directly attach wires to those pads. And oh god, my soldering skills are long gone.

Badly soldered wires to the UART interface. Ground has been soldered in the other side for convenience.

Once wires have been soldered, we can easily probe each pin using a multimeter:

Testing continuity and voltage to identify UART pinout.

I made some attempts with an old serial-to-USB cable, but I never managed to get anything that’s not pure gibberish using various baudrates and picocom settings. I do think that my old adapter has a fixed baudrate:

$ stty -f /dev/tty.usbserial-ftE0RKTK raw speed 115200
9600

Again, I did not spend too much time investigating the issue a managed to find a proper FT232RL connector. With a baudrate set to 115200, I finally got proper text on my screen:

$ picocom /dev/tty.usbserial-A50285BI -b 115200
picocom v3.1

port is        : /dev/tty.usbserial-A50285BI
flowcontrol    : none
baudrate is    : 115200
parity is      : none
databits are   : 8
stopbits are   : 1
escape is      : C-a
local echo is  : no
noinit is      : no
noreset is     : no
hangup is      : no
nolock is      : no
send_cmd is    : sz -vv
receive_cmd is : rz -vv -E
imap is        :
omap is        :
emap is        : crcrlf,delbs,
logfile is     : none
initstring     : none
exit_after is  : not set
exit is        : no

Type [C-a] [C-h] to see available commands
Terminal ready

ROM VER: 1.2.0
CFG 04
EEPROM Data OK
.............................

Here’s the full boot log if you’re interested:

Full firmare boot log
ROM VER: 1.2.0
CFG 04
EEPROM Data OK
.............................

Version BL: 1.0.6
Multicore disable; Booting Linux kernel

BOOTING THE THOMSON LINUX KERNEL

Starting the kernel @ 0x801f5000
Extra parameters passed to Linux:
	[0]: Thomson bootloader
	[1]: memsize=0x1FDD000
serial initialized
MC_PRIO = 0xc -> 0xcc
Linux version 2.6.20.19 (buildmgm@edgmwbuild04.edegem.eu.thmulti.com) (gcc version 3.4.6) #1 Wed Jan 19 16:00:53 CET 2011
CPU revision is: 0001906c
Determined physical RAM map:
 memory: 01fdd000 @ 00002000 (usable)
On node 0 totalpages: 8159
  DMA zone: 63 pages used for memmap
  DMA zone: 0 pages reserved
  DMA zone: 8096 pages, LIFO batch:0
  Normal zone: 0 pages used for memmap
Built 1 zonelists.  Total pages: 8096
Kernel command line: root=/dev/mtdblock2 rootfstype=squashfs
1 MIPSR2 register sets available
Primary instruction cache 8kB, physically tagged, 4-way, linesize 16 bytes.
Primary data cache 8kB, 2-way, linesize 16 bytes.
Synthesized TLB refill handler (20 instructions).
Synthesized TLB load handler fastpath (32 instructions).
Synthesized TLB store handler fastpath (32 instructions).
Synthesized TLB modify handler fastpath (31 instructions).
Setting up vectored interrupts
PID hash table entries: 128 (order: 7, 512 bytes)
mips_hpt_frequency:133333333
r4k_offset: 000208d5(133333)
Using 133.333 MHz high precision timer.
[ifx_asc_init_hardware 1372]: ASC ID = 0x101044c4
[ifx_asc_init_hardware 1374]: TxFIFO size = 16, RxFIFO size = 16
[ifx_asc_init_hardware 1376]: TxFIFO CON = 0x1f01, RxFIFO CON = 0x1f01
Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
Memory: 26868k/32628k available (1705k kernel code, 5760k reserved, 288k data, 112k init, 0k highmem)
Calibrating delay loop... 262.14 BogoMIPS (lpj=131072)
Mount-cache hash table entries: 512
NET: Registered protocol family 16
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
NET: Registered protocol family 2
IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
TCP established hash table entries: 1024 (order: 0, 4096 bytes)
TCP bind hash table entries: 512 (order: -1, 2048 bytes)
TCP: Hash tables configured (established 1024 bind 512)
TCP reno registered
Amazon_Se Port Initialization
squashfs: version 3.4 (2008/08/26) Phillip Lougher
squashfs: LZMA suppport for slax.org by jro
JFFS2 version 2.2. (NAND) (C) 2001-2006 Red Hat, Inc.
io scheduler noop registered (default)
ttyS0 at MMIO 0xbe100c00 (irq = 66) is a IFX_ASC
ifx_asc_init: uart init successfully
Infineon Technologies Synchronous Serial Controller (SSC) driver version 1.0.3
ifx_sscAllocConnection: device spi-flash register sucessfully!
Creating 4 MTD partitions on "spi-flash":
0x00040000-0x00100000 : "userfs"
0x00020000-0x00040000 : "mtdss"
0x001d0000-0x00800000 : "rootfs"
0x00100010-0x001d0000 : "kernel"
mtd: partition "kernel" doesn't start on an erase block boundary -- force read-only
Infineon Technologies Synchronous SPI flash driver version 1.0.0
dwc_otg: version 2.40a 10-APR-2006 (ker:20614)
data_fifo[0]=0xbe120000
data_fifo[1]=0xbe121000
data_fifo[2]=0xbe122000
data_fifo[3]=0xbe123000
data_fifo[4]=0xbe124000
data_fifo[5]=0xbe125000
data_fifo[6]=0xbe126000
data_fifo[7]=0xbe127000
data_fifo[8]=0xbe128000
data_fifo[9]=0xbe129000
data_fifo[10]=0xbe12a000
data_fifo[11]=0xbe12b000
data_fifo[12]=0xbe12c000
data_fifo[13]=0xbe12d000
data_fifo[14]=0xbe12e000
data_fifo[15]=0xbe12f000
USB_WLAN_HOST_SUPPORT
DWC_otg: Using DMA mode
dwc_otg dwc_otg: DWC OTG Controller
dwc_otg dwc_otg: new USB bus registered, assigned bus number 1
dwc_otg dwc_otg: irq 31, io mem 0x00000000
DWC_otg: Init: Port Power? op_state=1
DWC_otg: Init: Power Port (0)
usb usb1: configuration #1 chosen from 1 choice
hub 1-0:1.0: USB hub found
hub 1-0:1.0: 1 port detected
NET: Registered protocol family 26
TCP cubic registered
NET: Registered protocol family 1
NET: Registered protocol family 17
802.1Q VLAN Support v1.8 Ben Greear <greearb@candelatech.com>
All bugs added by David S. Miller <davem@redhat.com>
Time: MIPS clocksource has been installed.
VFS: Mounted root (squashfs filesystem) readonly.
Freeing unused kernel memory: 112k freed
dwc_otg_hcd_handle_port_intr=======set hfir ea60
usb 1-1: new high speed USB device using dwc_otg and address 2
dwc_otg_hcd_handle_port_intr=======set hfir 7530
usb 1-1: configuration #1 chosen from 1 choice
Algorithmics/MIPS FPU Emulator v1.5
init started:  BusyBox v1.00 (2011.01.19-15:01+0000) multi-call binary
init started:  BusyBox v1.00 (2011.01.19-15:01+0000) multi-call binary
Starting pid 100, console /dev/ttyS0: '/etc/init.d/rcS'
Using /nmon/nmon.ko
nmon: module license 'unspecified' taints kernel.
Button: Character device registered successfully.
SWWD: Enable guarding (ticks: 400 threshold: 280)
IPSecControl Character device registered successfully.
NET: Registered protocol family 3
NET: Registered protocol family 9
NET: Registered protocol family 4
NET: Registered protocol family 5
NET: Registered protocol family 18
NET: Registered protocol family 25
modprobe: could not parse modules.dep

modprobe: could not parse modules.dep

Using /lib/modules/rt3070ap.ko
Symbol usb_register_driver is being used by a non-GPL module, which will not be allowed in the future
Please see the file Documentation/feature-removal-schedule.txt in the kernel source tree for more details.
Symbol usb_deregister is being used by a non-GPL module, which will not be allowed in the future
Please see the file Documentation/feature-removal-schedule.txt in the kernel source tree for more details.
usbcore: registered new interface driver rt2870
Device ikanos not present.
Starting pid 257, console /dev/ttyS0: '/bin/sh'
Switching to RUNLEVEL 1 ...
route: SIOC[ADD|DEL]RT: File exists
route: SIOC[ADD|DEL]RT: File exists
linux application start ...
wait for linux_appl to initialize (1)
wait for linux_appl to initialize (2)

************* ERROR RECORD *************
000000:00:00.000000
Application NMON started after POWERON.
****************** END *****************

appl_init: BUILD VERIFIED!
dip_init() : button[FACTORY_RESET] has number[1]
End of initialisation
wait for linux_appl to initialize (3)
 start storagepl ...
/etc/rc1.d/S45storagepl: 17: /usr/bin/storagepl: not found
 storagepl is started
checkd start ...
wait until configuration load reaches phase 9 (now -1, 1s)
linuxappl: start loading after [  3006ms ]
Could not read ex-RIP, code 0x10003
WARNING: Unknown Parameter Type brgroup
WARNING: Unknown Parameter Type brgroup
IOCTL: ADSLIOCTL_SET_TMM_SET_DIAG_MODE
wait until configuration load reaches phase 9 (now 3, 2s)
wait until configuration load reaches phase 9 (now 3, 3s)
wait until configuration load reaches phase 9 (now 3, 4s)
wait until configuration load reaches phase 9 (now 3, 5s)
wait until configuration load reaches phase 9 (now 3, 6s)
wait until configuration load reaches phase 9 (now 3, 7s)
IOCTL: ADSLIOCTL_SET_TMM_SET_DIAG_MODE
IOCTL: ADSLIOCTL_SET_TMM_SET_DIAG_MODE

Username :

There are a few things that may be interesting:

  • it is booting a Linux Kernel on a MIPS architecture;
  • it is using Busybox;
  • the partition addresses are printed (rootfs being 0x001d0000-0x00800000);
  • it is probably using squashfsΒ for the root partition, which is read-only.

Bypassing the authentication #

After the whole booting sequence, the device asks for a username and a password. A quick search online told me that the user may be admin and the password an eleven-character string. It may be the serial number of the device but unfortunately after many attempts (using capital and lower letters, and any other strings printed on the device) I did not manage to log in.

My next idea was to try shorting the flash memory to GND during boot time to prevent the firmware from loading and make the init fail. This may make us end in a fail mode with a prompt. I identified the flash memory chip, a Windbond W25Q64BVFIG (16-pin SOIC) and found its datasheet.

Next, we have to identify the pin to short:

The pins layout used by the SOIC16 chip for flash memory.

I grounded the DO (Data Out) pin and powered-on the router, and indeed the boot process failed:

ROM VER: 1.2.0
CFG 04
EEPROM Data OK
.............................

Version BL: 1.0.6
BOOTP reason : BLFLAG_FLASH_FAIL (bad FVP)
Ctrl: BOOTP initiated.

But from there, I couldn’t do anything. Trying later during the boot process made it crash in numerous other ways, none of them apparently being exploitable:

ROM VER: 1.2.0
CFG 04
EEPROM Data OK
.............................

Version BL: 1.0.6
BOOTP reason : BLFLAG_SWSTART_FAIL
Skip BOOTP kick Rescue OS

Multicore disable; Booting Linux kernel

BOOTING THE THOMSON LINUX KERNEL

[ERROR] :segment load failed !!!
loader returned a NULL pointer***Reseting the board***
ROM VER: 1.2.0
CFG 04
UART

In despair, I went again trying various username/password pairs, and surprise… IT WORKED! πŸ€―πŸŽ‰

I’ve never been happier to see ASCII art!

The credentials are Administrator/<empty>… Yes, an empty password.

After a quick exploration, I discovered that you can fetch environment variables from the host:

{Administrator}=>env list
_SW_FLAG=E1
_ETHERNET=SWITCH
_COMPANY_NAME=THOMSON
_COMPANY_URL=http://www.thomson.net
_PROD_NAME=Thomson TG
_PROD_URL=http://www.thomson-broadband.com
_PROD_DESCRIPTION=DSL Internet Gateway Device
_PROD_NUMBER=585 v8
...

and it’s possible to override existing environment variables permanently:

{Administrator}=>env get
var = WL0_SSID
:env get var=WL0_SSID
Thomson536040
{Administrator}=>env set
var = WL0_SSID
value = toto
{Administrator}=>saveall

After a reboot, the WL0_SSID will still be set to toto! However, this doesn’t work with the system variables:

{Administrator}=>env set
var = _PROD_NAME
value = Evil Corp.
:env set var=_PROD_NAME value="Evil Corp."
Failed to set variable: can't set/change system variable.

There’s an overridable variable, UPGRADE_URL, that caught my eye. Modifying this variable and upgrading the router may be a good way to modify the firmware, but I currently do not have an appropriate network on which I can safely plug the router, so I’ll keep that for a future attempt if nothing else worked.

In the next part, I’ll try to dump the firmware, reverse it and try to find ways vulnerabilities! Stay tuned πŸ₯Έ