Reversing the TC7210 Embedded Linux Firmware

Reversing the TC7210 Embedded Linux Firmware

In this article I will explain how to reverse the firmware of the embedded Linux part of the Technicolor (TC) 7210 router by leveraging the usual tools of the trade.

In a previous article, I explained how to get root on the embedded Linux part of the TC7210 router by leveraging a remote code execution (RCE). With that level of access, I was able to image the various flash partitions of the router. The first thing I tried was to use binwalk to identify what files may be contained in the images.

 1#!/bin/bash
 2ls -l
 3# total 132416
 4# -rw-r--r-- 1 501 dialout    65536 Jul 25  2017 mtd0.img
 5# -rw-r--r-- 1 501 dialout 33423360 Jul 25  2017 mtd10.img
 6# -rw-r--r-- 1 501 dialout   131072 Jul 25  2017 mtd1.img
 7# -rw-r--r-- 1 501 dialout   262144 Jul 25  2017 mtd2.img
 8# -rw-r--r-- 1 501 dialout  1048576 Jul 25  2017 mtd3.img
 9# -rw-r--r-- 1 501 dialout   393216 Jul 25  2017 mtd4.img
10# -rw-r--r-- 1 501 dialout 16777216 Jul 25  2017 mtd5.img
11# -rw-r--r-- 1 501 dialout 16777216 Jul 25  2017 mtd6.img
12# -rw-r--r-- 1 501 dialout  8257536 Jul 25  2017 mtd7.img
13# -rw-r--r-- 1 501 dialout 46006272 Jul 25  2017 mtd8.img
14# -rw-r--r-- 1 501 dialout 12451840 Jul 25  2017 mtd9.img
15
16binwalk mtd8.img mtd10.img
17#
18# Scan Time:     2018-08-09 14:52:38
19# Target File:   /mnt/hgfs/Temporary/tc7210/mtd8.img
20# MD5 Checksum:  09a51282fcc93a2f0fd02736e0e0f313
21# Signatures:    344
22#
23# DECIMAL       HEXADECIMAL     DESCRIPTION
24# --------------------------------------------------------------------------------
25# 0             0x0             UBI erase count header, version: 1, EC: 0x2, VID header offset: 0x800, data offset: 0x1000
26#
27#
28# Scan Time:     2018-08-09 14:52:42
29# Target File:   /mnt/hgfs/Temporary/tc7210/mtd10.img
30# MD5 Checksum:  b78fa525eba4d388a9aeb74db578b189
31# Signatures:    344
32#
33# DECIMAL       HEXADECIMAL     DESCRIPTION
34# --------------------------------------------------------------------------------
35# 0             0x0             UBI erase count header, version: 1, EC: 0x2, VID header offset: 0x800, data offset: 0x1000

The partitions mtd8.img and mtd10.img appear to have a valid UBIFS file system on them. Using ubidump it is possible to extract the file system. First, we download the tool repository and then setup a virtual Python environment to where the tool dependencies are installed.

 1#!/bin/bash
 2git clone https://github.com/nlitsme/ubidump.git
 3# Cloning into 'ubidump'...
 4# remote: Counting objects: 53, done.
 5# remote: Total 53 (delta 0), reused 0 (delta 0), pack-reused 53
 6# Unpacking objects: 100% (53/53), done.
 7
 8virtualenv .tc7210-env
 9# Running virtualenv with interpreter /usr/bin/python2
10# New python executable in /mnt/hgfs/Temporary/tc7210/.tc7210-env/bin/python2
11# Also creating executable in /mnt/hgfs/Temporary/tc7210/.tc7210-env/bin/python
12# Installing setuptools, pkg_resources, pip, wheel...done.
13
14. .tc7210-env/bin/activate
15
16pip install -r ubidump/requirements.txt
17# Collecting python-lzo>=1.11 (from -r ubidump/requirements.txt (line 1))
18# Collecting crcmod>=1.7 (from -r ubidump/requirements.txt (line 2))
19# Installing collected packages: python-lzo, crcmod
20# Successfully installed crcmod-1.7 python-lzo-1.12

The next step is to extract the file system of both images.

 1#!/bin/bash
 2python ubidump/ubidump.py -s . mtd8.img mtd10.img
 3# ==> mtd8.img <==
 4# 1 named volumes found, 2 physical volumes, blocksize=0x20000
 5# == volume linuxapps ==
 6# saved 75 files
 7# ==> mtd10.img <==
 8# 1 named volumes found, 2 physical volumes, blocksize=0x20000
 9# == volume rootfs ==
10# saved 173 files

This creates two directories, rootfs and linuxapps. The first contains the root file system and the second ancillary applications and files. From this point onwards, it is possible to read configuration files, reverse engineer executables, libraries, etc.

 1#!/bin/bash
 2ls -l linuxapps/ rootfs/
 3# linuxapps/:
 4# total 2
 5# drwxr-xr-x 1 501 dialout 256 Aug  9 17:16 CVS
 6# -rw-r--r-- 1 501 dialout 306 Aug  9 17:16 readme.txt
 7# drwxr-xr-x 1 501 dialout 128 Aug  9 17:16 usr
 8# -rw-r--r-- 1 501 dialout 184 Aug  9 17:16 version.txt
 9#
10# rootfs/:
11# total 5
12# drwxr-xr-x 1 501 dialout  768 Aug  9 17:16 bin
13# drwxr-xr-x 1 501 dialout  256 Aug  9 17:16 CVS
14# drwxr-xr-x 1 501 dialout  960 Aug  9 17:16 etc
15# drwxr-xr-x 1 501 dialout 1568 Aug  9 17:16 lib
16# drwxr-xr-x 1 501 dialout  224 Aug  9 17:16 sbin

After this, I wanted to see if I could run the executables using QEMU (similar to what I did when reversing the ArubaOS). To do that, I needed to confirm the processor architecture of the executables, copy the respective statically linked QEMU executable to the rootfs directory and then use the chroot command.

 1#!/bin/bash
 2file rootfs/bin/busybox
 3# rootfs/bin/busybox: ELF 32-bit MSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
 4
 5which qemu-mips-static
 6# /usr/bin/qemu-mips-static
 7
 8cp /usr/bin/qemu-mips-static rootfs/
 9
10sudo chroot rootfs/ ./qemu-mips-static /bin/busybox
11# Error while loading /bin/busybox: Permission denied
12
13ls -l rootfs/bin/
14# total 8684
15# -rw-r--r-- 1 501 dialout   39964 Aug  9 17:16 brctl
16# -rw-r--r-- 1 501 dialout  526180 Aug  9 17:16 busybox
17# -rw-r--r-- 1 501 dialout   61640 Aug  9 17:16 ebtables
18# -rw-r--r-- 1 501 dialout  210764 Aug  9 17:16 ip
19# -rw-r--r-- 1 501 dialout    7808 Aug  9 17:16 lxginit
20# -rw-r--r-- 1 501 dialout   14876 Aug  9 17:16 mscapp
21# -rw-r--r-- 1 501 dialout 1219244 Aug  9 17:16 nmbd
22# -rw-r--r-- 1 501 dialout  154140 Aug  9 17:16 ntfs-3g
23# -rw-r--r-- 1 501 dialout   24200 Aug  9 17:16 ntfslabel
24# -rw-r--r-- 1 501 dialout   14204 Aug  9 17:16 portmap
25# -rw-r--r-- 1 501 dialout   18988 Aug  9 17:16 remoteapi
26# -rw-r--r-- 1 501 dialout    4612 Aug  9 17:16 rmtshutdown
27# -rw-r--r-- 1 501 dialout    4924 Aug  9 17:16 rpc_test_client
28# -rw-r--r-- 1 501 dialout    5408 Aug  9 17:16 rpc_test_service
29# -rw-r--r-- 1 501 dialout    4444 Aug  9 17:16 setappsver
30# -rw-r--r-- 1 501 dialout  114004 Aug  9 17:16 smbapp
31# -rw-r--r-- 1 501 dialout 3900616 Aug  9 17:16 smbd
32# -rw-r--r-- 1 501 dialout 2203476 Aug  9 17:16 smbpasswd
33# -rw-r--r-- 1 501 dialout  242844 Aug  9 17:16 tc
34# -rw-r--r-- 1 501 dialout   37716 Aug  9 17:16 ubimkvol
35# -rw-r--r-- 1 501 dialout   39748 Aug  9 17:16 ubinfo
36# -rw-r--r-- 1 501 dialout   35344 Aug  9 17:16 ubirmvol
37
38chmod +x rootfs/bin/*
39
40sudo chroot rootfs/ ./qemu-mips-static /bin/busybox
41# BusyBox v1.19.3 (2015-03-04 13:37:03 CST) multi-call binary.
42# Copyright (C) 1998-2011 Erik Andersen, Rob Landley, Denys Vlasenko
43# and others. Licensed under GPLv2.
44# See source distribution for full notice.
45#
46# Usage: busybox [function] [arguments]...
47#    or: busybox --list[-full]
48#    or: function [arguments]...
49#
50# 	BusyBox is a multi-call binary that combines many common Unix
51#	utilities into a single executable.  Most people will create a
52#	link to busybox for each function they wish to use and BusyBox
53#	will act like whatever it was invoked as.
54#
55# Currently defined functions:
56#	[, [[, arp, arping, ash, awk, basename, cat, chgrp, chmod, chown,
57#	chroot, cp, cttyhack, cut, date, dd, deluser, df, dmesg, echo, egrep,
58#	expr, false, fgrep, find, flash_eraseall, free, fsync, ftpd, ftpget,
59#	getty, grep, halt, hexdump, httpd, ifconfig, inetd, init, insmod, ip,
60#	kill, killall, klogd, less, linuxrc, ln, logger, login, ls, lsmod,
61#	lsusb, mkdir, mknod, mount, mv, netstat, od, passwd, pidof, ping,
62#	ping6, poweroff, ps, pwd, reboot, rm, rmdir, rmmod, route, sed, sh,
63#	sleep, start-stop-daemon, sync, sysctl, syslogd, tail, tar, tcpsvd,
64#	telnetd, test, tftp, time, top, true, tty, udhcpc, umount, uname,
65#	uptime, vconfig, vi, which, whoami, xargs, zcip

Next, I tried to run the smbapp (the executable that was previously identified as the one responsible for managing the NAS file sharing functionality). After fixing some of the errors, I was also successful :D

 1#!/bin/bash
 2sudo chroot rootfs/ ./qemu-mips-static /bin/smbapp
 3# NAS: Network Attached Storage Control Application [Version 1.3 Build Mar  4 2015 13:42:04]
 4# found 0 USB devices
 5# NAS: Will retry bind in 15 seconds
 6# Launching mscapp
 7# MSC: Media Server Control Application [Version 1.2 Build Mar  4 2015 13:42:04]
 8# MSC: waiting for data on port UDP 49181
 9# Fatal Error: unable to open /var/tmp/smbapp_hotplug_fifo
10
11sudo chroot rootfs/ ./qemu-mips-static /bin/busybox mkdir -p /var/tmp
12
13sudo chroot rootfs/ ./qemu-mips-static /bin/busybox sh
14#
15#
16# BusyBox v1.19.3 (2015-03-04 13:37:03 CST) built-in shell (ash)
17# Enter 'help' for a list of built-in commands.
18#
19# ${debian_chroot:+($debian_chroot)}:/# busybox echo ""> /var/tmp/smbapp_hotplug_fifo
20# ${debian_chroot:+($debian_chroot)}:/# exit
21
22sudo chroot rootfs/ ./qemu-mips-static /bin/smbapp
23# NAS: Network Attached Storage Control Application [Version 1.3 Build Mar  4 2015 13:42:04]
24# found 0 USB devices
25# NAS: Will retry bind in 15 seconds
26# Launching mscapp
27# SmbApp: waiting on port 49182 to recvfrom...
28# MSC: Media Server Control Application [Version 1.2 Build Mar  4 2015 13:42:04]
29# MSC: waiting for data on port UDP 49181
30# smbapp: sigterm_handler(): Caught signal(2)
31# smbapp: shuting down child processes
32# umounting all USB devices
33# pid 3756 terminated by a signal 2
34# smbapp: mscapp pid 3756 exit status 0
35# smbapp: closing the socket to ecos

The ability to run the executables makes the reverse engineering and vulnerability finding process a lot easier. In upcoming posts, I will detail how I found and exploited some yet to be released vulnerabilities. Cheers :)