For this device, we managed to gain root access directly to the device,
that's rare.
And later,
we'll take a look at ways to access the firmware
without requiring command line access.
But what if we wanted to extract parts of the firmware such as the file
system to our lab environment for detailed analysis?
For this demo, we'll, again,
use the FT232 USB to TTL serial adapter to connect the router
serial interface to our lab environment.
The setup is the same as the previous demo so we need to
ensure the transmit pin on one device is connected to the
receive pin on the other and vice versa.
And again, we don't need to connect VCC.
Using PuTTY, we connect to the router serial interface.
It was noted earlier that we can gather lots of useful and
interesting information during boot.
If we pause here, we can see that 5 MTD partitions are created.
MTD stands for memory technology device,
a type of device file in Linux for interacting with flash memory.
This subsystem was created to provide an abstraction layer between the
hardware‑specific device drivers and high‑level applications.
Once at the command line,
we can find the MTD partition information inside the proc virtual file system.
These MTD subsystems within proc are mounted within the devices folder.
Listing dev with the ‑al option,
we can see in the file permissions that MTD block 0 to 6 are block devices.
Block devices are non‑volatile mass storage devices whose
information can be accessed in any order.
And more specifically,
an MTD block is used to present flash memory as a block device that
can be formatted and mounted as a file system.
Looking inside one of these blocks,
we see a mixture of human‑readable characters and garbage,
which is as much as you'd expect if you were to
read the contents of a hard drive, a USB drive,
or an SD card.
So how can we extract and make sense of this information?
There are a number of ways, but in this demo,
we'll look at how to extract it directly over serial.
We know that if we issue the cat /dev/mtdblock0,
1, 2, and so on command, we dump the contents of that file to standard output.
We can write a Python script that makes a serial connection,
issues the cat command, captures the response,
and stores it in the file that we can process with Binwalk.
Let's take a look at what that script might look like.
I'll be honest up front,
this isn't the prettiest or best way to achieve this task,
but it's effective and illustrative.
We begin by importing the serial modules that we can make
connections and communicate over serial.
We also import the time module.
Serial connections are pretty slow so we need to pause between issuing commands.
Next,
we establish a connection to the device by specifying a
variable called port and opening the connection by
specifying the device and the board rate.
Our target device doesn't require a username and password
so the next few lines are commented out,
but if a device you test requires this,
then you can uncomment these lines and specify the
relevant username and password.
Next,
we drop into a loop that first empties the connection of any data
that might be waiting and creates a writeable file called rootfs
image via_serial with an image file extension because we're
expecting a binary image to be captured.
Because we're capturing data in a buffer and processing it a piece at a time,
the image file needs to be appendable.
So we close the file that we've just created and reopen it as appendable.
We then issue the cat /dev/mtdblock2 command to dump the contents of the
virtual file system to standard output for us to capture.
Next,
we create a variable called buffer that will be used to store
pieces of data received over the serial connection for us to
process and append to the file.
We now create a loop that allows us to read the data that's waiting on our
serial connection into a variable called bytes to read.
If the bytes to read variable is not empty,
we print the number of bytes waiting to be read to screen before
reading the actual data into the buffer variable.
Although not necessary, the contents of the buffer is printed to screen.
These print statements are confirmatory only,
and if removed, will lead to the code running much more quickly.
The data that we printed to screen is then written to the
appendable file we created earlier.
This final section of code carries out a couple of checks.
First,
it determines if we've reached the end of the file
capture using the tell function,
which is essentially looking to see if the file handle,
which is a bit like a cursor that defines the point in the file
that we're currently reading or writing to,
is further into the file than we would expect.
Or secondly, if we're still receiving data,
then we conclude that the file is too big and the program exits.
Launching our script,
we see that a connection is successfully established and
data is read across the serial connection,
printing the number of bytes, typically 496 in each loop,
and printing those bytes to screen as hexadecimal.
Before moving on,
it's worth pointing out that it took about 27 minutes to
transfer this file from the router across the UART serial
connection to the lab machine.
This device has a relatively small flash memory chip,
but it's still painfully slow compared to other techniques.
Before analyzing the file with Binwalk,
it's necessary to trim out any unnecessary data and
convert the hexadecimal to binary.
How that's done is beyond the scope of this course,
but there are plenty of scripts online to achieve it.
Running Binwalk against our post processed file,
we see we have a 3 MB Squashfs file system.
We can now continue to analyze and extract the contents of the file system
and hunt for sensitive information and vulnerabilities.