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:

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:

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