Some time in December 2018, people on xda-developers managed to root the Sony DPT-RP1 e-reader. So I wanted to understand how they discovered it, and how the exploit works.
How the exploit works
The first step of the exploit is to flash the device with two (bogus) firmware updates. According to exploit instructions, we are supposed to use a Python program to flash two bogus firmware updates, which are supposed to fail.
It seems like a parsing vulnerability, How does that work, and how does it get past the firmware digest verification?
The DPT firmware update format
The DPT update package has the following format, reverse engineered from start_eufwupdater.sh
Offset (incl-incl) | Purpose |
---|---|
0x00-0x03 | “DPU8” (Digital Paper Update) |
0x04-0x07 | DATA_OFFSET (e.g. ” 568″) |
0x08-0x0b | BODY_SIZE (e.g. ” 232976″) |
0x0c-0x0f | ???? |
0x14-0x113 | Signature (size: 256) |
0x114-0x117 | ENC_KEY_SIZE (4 bytes e.g. 256) |
0x118-0x217 | AES256_KEY |
0x218-0x237 | IV |
0x238- | Data content |
Diffing FactoryReset.pkg vs JB.pkg
Let’s diff the two pkg files and see where the update script treats them differently. We’ll run through the dd
commands in start_eufwupdater.sh
, and see where the outputs start to differ.
$ DATA_OFFSET=`dd if=FactoryReset.pkg bs=4 skip=1 count=1 2>/dev/null | od -A n -t d4 -v` $ echo "$DATA_OFFSET" 568 $ DATA_OFFSET=`dd if=JB.pkg bs=4 skip=1 count=1 2>/dev/null | od -A n -t d4 -v` $ echo "$DATA_OFFSET" 568 $ BODY_SIZE=`dd if=FactoryReset.pkg bs=4 skip=2 count=1 2>/dev/null | od -A n -t d4 -v` $ echo "$BODY_SIZE" 232976 $ BODY_SIZE=`dd if=JB.pkg bs=4 skip=2 count=1 2>/dev/null | od -A n -t d4 -v` $ echo "$BODY_SIZE" 232976 $ SIG_SIZE=`dd if=FactoryReset.pkg bs=4 skip=4 count=1 2>/dev/null | od -A n -t d4 -v` $ SIG_SIZE=`dd if=JB^Ckg bs=4 skip=4 count=1 2>/dev/null | od -A n -t d4 -v` $ echo "$SIG_SIZE" 256 $ SIG_SIZE=`dd if=JB.pkg bs=4 skip=4 count=1 2>/dev/null | od -A n -t d4 -v` $ echo "$SIG_SIZE" 256 $ dd if=FactoryReset.pkg bs=1 skip=20 count=$(($SIG_SIZE)) 2>/dev/null | md5sum f89f0c9059c5f7e06357b33910a18af9 - $ dd if=JB.pkg bs=1 skip=20 count=$(($SIG_SIZE)) 2>/dev/null | md5sum f89f0c9059c5f7e06357b33910a18af9 - $ dd if=FactoryReset.pkg bs=$(($DATA_OFFSET)) skip=1 2>/dev/null | head -c $(($BODY_SIZE)) | md5sum a2d4d32d98f96242b83b8621616abfb1 - $ dd if=JB.pkg bs=$(($DATA_OFFSET)) skip=1 2>/dev/null | head -c $(($BODY_SIZE)) | md5sum a2d4d32d98f96242b83b8621616abfb1 - $ ENC_KEY_SIZE=`dd if=FactoryReset.pkg bs=1 skip=${ENC_KEY_OFFSET} count=4 2>/dev/null | od -A n -t d4 -v` $ echo "$ENC_KEY_SIZE" 256 $ ENC_KEY_SIZE=`dd if=JB.pkg bs=1 skip=${ENC_KEY_OFFSET} count=4 2>/dev/null | od -A n -t d4 -v` $ echo "$ENC_KEY_SIZE" 256 $ dd if=FactoryReset.pkg bs=1 skip=${ENC_KEY_OFFSET} count=$(($ENC_KEY_SIZE)) 2>/dev/null | md5sum cac05d9116db1420e21f379d88939274 - $ dd if=JB.pkg bs=1 skip=${ENC_KEY_OFFSET} count=$(($ENC_KEY_SIZE)) 2>/dev/null | md5sum f02f33824e15d65cfdaf208606793fc1 -
Here we see that the only difference between FactoryReset.pkg and JB.pkg is the encryption key.
The DPT update package embeds the symmetric encryption key for the firmware, and the embedded key is encrypted again with a static symmetric decryption key stored on the device. We can change the encryption key by encrypting the desired plaintext with the leaked static decryption key.
Shell injection vulnerability
To figure out why JB.pkg’s encryption key is special, I decrypted it, and found this:
00000000: ZGFlbW9uOng6MTox 00000010: OjovdXNyL3NiaW46 00000020: L2Jpbi9mYWxzZQpu 00000030: b2JvZH -k.6eDo2N 00000040: TUzNDo2NTUzNDo6L 00000050: 25vbmV4aXN0ZW50O 00000060: i9iaW4vZmFsc2UKc 00000070: m9vdDo -k.MSQxJE 00000080: FlZ2VZbTFRYWFUeW 00000090: QvMWZPaVhWZDA6MD 000000a0: owOjovcm9vdDovYm 000000b0: luL3NoCg== -none 000000c0: -in /tmp/aes256 000000d0: .key -out /etc/p 000000e0: asswd -a
It’s obvious that this is a shell injection vulnerability.
Looking at the start_eufwupdater.sh
:
openssl enc -d -aes-256-cbc -K `cat ${AES256_KEY}` -iv `cat ${IV}`
Because we have control of the AES256_KEY, and it’s never digest verified (only the firmware data is verified), we can inject any arguments we want.
How the exploit was discovered
Somehow acquire FactoryReset.pkg along with decryption keys
The very first sign of useful information seems to have come when someone acquired a Chinese-rooted DPT-RP1 device, and extracted the firmware update mechanism and published it.
It seems someone rooted a copy of the device using hardware methods, and extracted the FactoryReset.pkg along with keys used to verify and decrypt the package.
This shows the value of hardware hacking, even when trying to develop software exploits.
Reference
https://forum.xda-developers.com/showpost.php?p=78140924&postcount=68
https://github.com/octavianx/Unpack-and-rebuild-the-DPT-RP1-upgrade-firmware