Insomni’hack finals – InsomniDroid Level 1 Writeup

The challenge was delivered as a zip file (InsomniDroid.zip). The first challenge was perhaps to download it (with its 602.5 MiB). The zip file contains a single file: mmcblk0.dd. A file command gives some information:

$ file mmcblk0.dd

mmcblk0.dd: x86 boot sector; partition 1: ID=0xc, starthead 0, startsector 1, 212991 sectors; partition 2: ID=0x4d, active, starthead 0, startsector 212992, 1000 sectors; partition 3: ID=0x46, starthead 0, startsector 213992, 7192 sectors; partition 4: ID=0x5, starthead 0, startsector 221184, 7512064 sectors, code offset 0x0

I am using Mac OS X, but it is not the best platform to study Android. So let’s switch to Linux (for example Kali Linux or Santoku). The command fdisk gives the complete list of partitions:

$ fdisk -l mmcblk0.dd

Disk mmcblk0.dd: 3959 MB, 3959422976 bytes
1 heads, 63 sectors/track, 122749 cylinders, total 7733248 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x00000000

Device Boot Start End Blocks Id System
mmcblk0.dd1 1 212991 106495+ c W95 FAT32 (LBA)
mmcblk0.dd2 * 212992 213991 500 4d QNX4.x
mmcblk0.dd3 213992 221183 3596 46 Unknown
mmcblk0.dd4 221184 7733247 3756032 5 Extended
mmcblk0.dd5 229376 239615 5120 47 Unknown
mmcblk0.dd6 245760 285759 20000 49 Unknown
mmcblk0.dd7 286720 292863 3072 58 Unknown
mmcblk0.dd8 294912 306175 5632 48 Unknown
mmcblk0.dd9 311296 324271 6488 50 OnTrack DM
mmcblk0.dd10 327680 333823 3072 4a Unknown
mmcblk0.dd11 335872 342015 3072 4b Unknown
mmcblk0.dd12 344064 360447 8192 90 Unknown
mmcblk0.dd13 360448 375807 7680 91 Unknown
mmcblk0.dd14 376832 387071 5120 92 Unknown
mmcblk0.dd15 393216 1488895 547840 93 Amoeba
mmcblk0.dd16 1490944 1613823 61440 94 Amoeba BBT
mmcblk0.dd17 1613824 3887103 1136640 95 Unknown
mmcblk0.dd18 3891200 3993599 51200 96 Unknown
mmcblk0.dd19 3997696 3998695 500 97 Unknown
mmcblk0.dd20 4005888 4013079 3596 98 Unknown
mmcblk0.dd21 4014080 4024319 5120 99 Unknown
mmcblk0.dd22 4030464 4070463 20000 9a Unknown
mmcblk0.dd23 4071424 4081663 5120 9b Unknown
mmcblk0.dd24 4087808 4101807 7000 9c Unknown
mmcblk0.dd25 4104192 4114431 5120 9d Unknown
mmcblk0.dd26 4120576 4130815 5120 9e Unknown
mmcblk0.dd27 4136960 4147199 5120 9f BSD/OS
mmcblk0.dd28 4153344 7733247 1789952 a0 IBM Thinkpad hibernation

So we have 28 partitions with some strange systems. We can try to identify partitions one by one and hope we will be able to read something useful, but there is another way. The description of the challenge says that “The device is a Samsung W (GT-I8150) and apparently it runs the Cyanogen flavour of Android KitKat.” This device is not officially supported by Cyanogen, but there is an unofficial port:

http://wiki.cyanogenmod.org/w/Unofficial_Ports#Samsung_Galaxy_W_.28GT-I8150.29

The port is published in GitHub:

https://github.com/arco/android_device_samsung_ancora/

Be sure to select the “cm-11.0” branch and you have the complete project. Inside “rootdir”, you find:

The interesting file is “fstab.qcom”. As its name indicates, it gives the file system table:

wordpress2

So the 17th partition is /system and the 28th one is /data. In fact, it is not difficult to guess it because they are the biggest partitions. Something more interesting is the complete line regarding /data:

/dev/block/mmcblk0p28 /data ext4 noatime,nosuid,nodev,data=ordered,
noauto_da_alloc,journal_async_commit,errors=panic
wait,check,encryptable=footer,length=-16384

Data is encrypted (as stated in the description of the challenge) and the length of the footer is 16384 bytes (as it is often the case). OK, this is the lazy way (i.e. Google way). But this “fstab.qcom” file is somewhere in the bit-per-bit copy, isn’t it? Why not extract it directly to be sure of its content. Because it is more complicated and I am lazy. But since you are a nice girl, I will show you how to extract this file. This file is in the root partition. But how to identify this partition? Well, on Samsung devices, it has often ID 0x48. To confirm which partition is the right one, we can use the PIT. On Samsung devices, PIT (Partition Information Table) describes the formatting of the memory of the mobile:

wordpress3

So, the partition (boot.img) we are looking for is:

mmcblk0.dd8 294912 306175 5632 48 Unknown

To extract boot.img, we can use the famous DD command:

$ dd if=mmcblk0.dd of=boot.img bs=512 skip= 294912 count=$((306175-294912 +1))

The block size (512) is indicated by fdisk (Units = 512 bytes), skip is given by “Start” and count is simply the difference between Start and End (plus 1 because it includes the End sector).

boot.img (like recovery images) are packaged with mkbootimg. We need an equivalent tool to unpack it (personally, I am using bootimg-tools from https://github.com/pbatard/bootimg-tools):

$ ./unmkbootimg -i boot.img

It extracts the kernel and the ram disk. It is not finished! The ram disk is archived and compressed with gzip and cpio. So to decompress:

$ gunzip -c ramdisk.cpio.gz | cpio -i

We have our files! We get our fstab.qcom, identical to the other one found on GitHub. Google-way is more simple, isn’t it?

So we have to decrypt the user partition without knowing the password. We first need to extract the partition data:

$ dd if=mmcblk0.dd of=mmcblk0p28.dd bs=512 skip=4153344 count=$((7733247-4153344+1))

Again, the block size (512) is indicated by fdisk (Units = 512 bytes), skip is given by “Start” and count is simply the difference between Start and End (plus 1 because it includes the End sector). It gives a file of 1.8 GiB.

In order to decrypt it, Google is again our friend and replies with a blog article from Nikolay Elenkov

http://nelenkov.blogspot.ch/2014/10/revisiting-android-disk-encryption.html

By the way, all the articles of this blog are fantastic and Nikolay is also the author of “Android Security Internals” book. If you are interested by Android security, you have to read it.

But let’s go back to our challenge. Nikolay has adapted a script (from Santoku Linux) for KitKat (Android 4.4) and it is published on GitHub:

https://github.com/nelenkov/Santoku-Linux/blob/master/tools/android/android_bruteforce_stdcrypto/bruteforce_stdcrypto.py

To run this script, you need a header, a footer and the maximum length of the PIN code. It says that it will take around 5 minutes to try 1200 PIN combinations, so let’s try that.

The footer is… the footer. We just need its size. This is exactly what we retrieved from fstab (16384).

$ dd if=mmcblk0p28.dd bs=1 count=16384 skip=1832894464 of=footer.dd

1832894464 is the difference between the size of our encrypted file (1832910848) and 16384.

The header is the beginning of our data and is just only to check if the decryption succeed or not:

$ dd if=mmcblk0p28.dd bs=1 count=512 of=header.dd

And now, we can try to brute force, like in the example of Nikolay:

$ python bruteforce_stdcrypto.py header.dd footer.dd

Be sure to have M2Crypto and script installed. On Mac OS X, you may also need to install version 3.0.4 (not 3.0.5) of Swig. It depends of your computer, but around 6 minutes later, you will get:

…
Trying: 1964
Trying: 1965
Trying: 1966
Trying: 1967
Trying: 1968
Trying: 1969
Trying: 1970
Found PIN!: 1970

No, it is not the year of my birth. We found the PIN code, great. But we now have to decrypt the data. It is not very complicated to adapt bruteforce_stdcrypto.py to decrypt, but there is another way: using Linux and cryptsetup. All we need is the key in an appropriate format. bruteforce_stdcrypto.py already contains appropriate code (decryptDecodeScryptKey function). We can make a generic script to handle different cases such as:

import sys
from os import path
import struct
from bruteforce_stdcrypto import *


def getDecryptionKey(footer, pin):
	cryptoFooter = getCryptoData(footer)

	# make the decryption key from the password
	decKey = ''
	if cryptoFooter.kdf == KDF_PBKDF:
		decKey = decryptDecodePbkdf2Key(cryptoFooter, pin)
	elif cryptoFooter.kdf == KDF_SCRYPT:
		decKey = decryptDecodeScryptKey(cryptoFooter, pin)
	else:
		raise Exception("Unknown or unsupported KDF: " + str(cryptoFooter.kdf))

	return decKey


def main(args):

	if len(args) < 2:
		print 'Usage: python getkey.py [footer file] [pin]'
		print '[] = Mandatory'
		print ''
	else:
		# use inputed filenames for the two files
		footerFile  = args[1]
		pin 		= args[2]

		assert path.isfile(footerFile), "Footer file '%s' not found." % footerFile
		footerSize = path.getsize(footerFile)

		assert (footerSize >= 16384), "Input file '%s' must be at least 16384 bytes" % footerFile

		decKey = getDecryptionKey(footerFile, pin)
		print 'Key: ', decKey.encode('hex').upper()
		open('decryption.key','wb').write(decKey)

if __name__ == "__main__":
	main(sys.argv)

If we execute it with appropriate parameters:

$ python getkey.py footer.dd 1970

It gives:

Android FDE crypto footer
-------------------------
Magic : 0xD0B5B1C4
Major Version : 1
Minor Version : 2
Footer Size : 192 bytes
Flags : 0x00000000
Key Size : 128 bits
Failed Decrypts : 0
Crypto Type : aes-cbc-essiv:sha256
Encrypted Key : 0x0CB33742A157543F46111448FC63BC10
Salt : 0xE53FD71CF38B6E3BE0BF6C9FC824C104
KDF : scrypt
N_factor : 15 (N=32768)
r_factor : 3 (r=8)
p_factor : 1 (p=2)
-------------------------
Key: FF6D9DEB77BA1120E355D5F95F9B5BE3

We have our key! It is also saved in a file named “decryption.key”.

We now have all we need. First, we setup a loop device, then we setup disk decryption and then we mount the device:

$ losetup /dev/loop3 mmcblk0p28.dd
$ cryptsetup --type plain open -c aes-cbc-essiv:sha256 -s 128 -d decryption.key /dev/loop3 userdata
$ mkdir mnt
$ mount /dev/mapper/userdata mnt

We have now access to decrypted data:

$ ls -l mnt

total 164
drwxrwxr-x. 2 1000 inetsim 4096 Oct 6 15:45 anr
drwxrwx--x. 2 1000 inetsim 4096 Feb 24 06:20 app
drwx------. 2 root root 4096 Oct 6 15:42 app-asec
drwxrwx--x. 3 1000 inetsim 4096 Feb 24 06:20 app-lib
drwxrwx--x. 2 1000 inetsim 4096 Oct 6 15:42 app-private
drwx------. 5 1000 inetsim 4096 Oct 6 15:45 backup
lrwxrwxrwx. 1 root root 45 Oct 6 15:42 bugreports -> /data/data/com.android.shell/files/bugreports
drwxrwx--x. 2 1000 inetsim 4096 Feb 24 06:20 dalvik-cache
drwxrwx--x. 89 1000 inetsim 4096 Feb 24 06:20 data
drwxr-x---. 2 root 1007 4096 Oct 6 15:42 dontpanic
drwxrwx---. 3 1019 1019 4096 Oct 6 15:46 drm
-rw-rw-rw-. 1 root root 138 Feb 24 09:39 FLAG1.txt
drwxr-x--x. 3 root root 4096 Oct 6 15:42 local
drwxrwxr-x. 2 1000 1007 4096 Oct 6 15:42 log
drwxrwx---. 2 root root 4096 Dec 31 1969 lost+found
drwxrwx---. 5 1023 1023 4096 Oct 6 15:45 media
drwxrwx---. 2 1031 1031 4096 Oct 6 15:42 mediadrm
drwxrwx--t. 16 1000 9998 4096 Jan 7 14:15 misc
drwx------. 2 root root 4096 Feb 23 02:30 property
drwxrwx---. 2 1001 1001 4096 Oct 6 15:42 radio
drwxrwx--x. 2 1000 inetsim 4096 Oct 6 15:42 resource-cache
drwx--x--x. 2 1000 inetsim 4096 Oct 6 15:42 security
drwxr-x---. 3 root 2000 4096 Feb 19 17:16 ssh
drwxrwxr-x. 13 1000 inetsim 4096 Feb 24 09:53 system
drwx------. 2 1000 inetsim 4096 Feb 20 07:56 tombstones
drwx--x--x. 2 1000 inetsim 4096 Oct 6 15:42 user

There is a file named “FLAG1.txt” and it contains:

$ cat FLAG1.txt

Congratulations, you found the first flag:

{INS15-ANDROID-FDE-1543}
You can now try to crack the application to get the second flag...

By the way, the application in question is in app and its files in data.

When you have retrieved data, do simply:

$ umount mnt
$ cryptsetup close user data
$ losetup -d /dev/loop3

See you next year!
Britney