Skip to content

Decrypting Android Full Disk Encryption

This document was written to provide some methods around decrypting Android Full Disk Encryption. The document assumes that you do not have adb, nor root access to the phone, rather, that you are trying to decrypt a file system which was retrieved by getting a physical dump from the phone (via a physical acquisition, JTAG, chip-off, etc).

Decrypting LG and other Android Full Disk Encryption (FDE)

oclHashcat includes support for decrypting PBKDF2-HMAC-SHA1 + CBC-ESSIV-AES encyption using brute force. The current version at the time of this writing is oclHashcat v1.3.0 which includes GPU accelerated 4-digit PIN brute forcing. See:

Decrypting Samsung Full Disk Encryption (FDE)

At the time of this writing, Samsung "does things different" than other Android vendors. This may change with later releases of the Android OS releases. For specifics on how Samsung does things different, see the DerbyCon 2013 talk given by László Tóth and Ferenc Spala which covers the technical details. Without their research and effort this document would not be possible!

The process for decrypting Samsung FDE is as follows:

  • Acquire a physical acquisition of the device (not covered within the scope of this document).
  • Extract the encryption key from the device image.
  • Convert the encryption key to John The Ripper format.
  • Brute force the John The Ripper format encrpytion key using a dictionary.

Getting Started

You will need the following tools before you get started:

  • Linux (Debian GNU/Linux jessie was used in this case)
  • Sandy source code (https://github.com/donctl/sandy)
  • John the Ripper jumbo community-edition source code (http://www.openwall.com/john/)
  • Python 2.7 (https://www.python.org/downloads/)

Optional:

  • crunch (https://sourceforge.net/projects/crunch-wordlist/)

Locating the Encryption Key

Depending on the model of phone and Android version on the device, the location of the password key (or password footer as it's referred to in some cases) varies. According to the research by Tóth and Spala, in some versions of Android the key is located in the last 16K of the encrypted userdata partition. In other Android versions the key is located in /efs/metadata. For these cases, their 'sandy' framework can locate your key quite effectively.

In the case of the testing below, the phone was a Samsung SII SGH-T989D running Android 4.1.2 (with an encryption passphrase of 'test123'). The key in this case was not located in the userdata partition nor /efs/metadata so we manually searched for the ASCII string 'aes-cbc-essiv:sha256' using grep (or the tool of your choice) to locate it. The 'DumpData.bin' is a single 16GB raw image which was acquired from the device.

dk@anubis:~/Samsung_SII_SGHT989D$ strings -t d DumpData.bin | grep aes-cbc-essiv:sha256 402653220 aes-cbc-essiv:sha256 663743397 aes-cbc-essiv:sha256

Let's check the first hit, is that the key? It is! You can tell as the key area we need starts with 0xC5B1B5D0, contains some "whitespace", then the 'aes-cbc-essiv:sha256', followed by more "whitespace", and finally the encryption key or encrypted footer which starts with 0xF1DA84CF in our case and is 80 bytes in length. Ultimately, these 80 bytes are all that we want to extract and brute force with JtR. If you want to skip ahead slightly, at this point you could select those 80 hex bytes, save them to a file in JtF format, and start brute forcing with JtR. These steps are covered in more detail below.

Note: The starting 0xC5B1B5D0 bytes were noted to vary slightly between different Android dumps however the variations were minor. Just note that a search for '0xC5B1B5D0' will not always work hence why we search for 'aes-cbc-essiv:sha256'.

In this example we are using 'dd' to extract 512 bytes around the area where our ASCII string was located. In order to show the entire match including the 0xC5B1B5D0 starting point, I am skipping back 36 bytes before the ASCII string hit. 36 is an arbitrary number which is enough in this case to show the starting 0xC5B1B5D0 which is indicative of the start of the area which contains the encryption key.

dk@anubis:~/Samsung_SII_SGHT989D$ dd bs=1 skip=$((402653220-36)) count=512 if=DumpData.bin | hexdump -C 00000000  c5 b1 b5 d0 01 00 00 00  d8 00 00 00 00 00 00 00  |................| 00000010  20 00 00 00 00 00 00 00  00 10 40 00 00 00 00 00  | .........@.....| 00000020  00 00 00 00 61 65 73 2d  63 62 63 2d 65 73 73 69  |....aes-cbc-essi| 00000030  76 3a 73 68 61 32 35 36  00 00 00 00 00 00 00 00  |v:sha256........| 00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................| * 00000060  00 00 00 00 b1 e4 01 10  02 00 00 00 00 00 00 00  |................| 00000070  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................| 00000080  00 00 00 00 f1 da 84 cf  0f 82 30 b9 a7 bc 16 d4  |..........0.....| 00000090  bc ed 22 20 43 6d 7c b7  50 aa a3 f2 6f 41 d3 28  |.." Cm|.P...oA.(| 000000a0  48 cc 63 0d 10 30 ae 35  b6 e3 73 b2 5c 1c 61 dd  |H.c..0.5..s.\.a.| 000000b0  34 80 07 89 67 34 2e 47  23 68 20 ac df cf 41 3a  |4...g4.G#h ...A:| 000000c0  10 0c 2d f4 18 9e 50 a0  47 09 ca 5c 90 1d 42 84  |..-...P.G..\..B.| 000000d0  8a 48 62 7c 00 00 00 00  00 00 00 00 00 00 00 00  |.Hb|............| 000000e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................| * 00000200 512+0 records in 512+0 records out 512 bytes (512 B) copied, 0.00276768 s, 185 kB/s

For fun, let's check the second hit from our previous strings + grep search. You can see that this is not the key as expected so we can ignore this hit.

dk@anubis:~/Samsung_SII_SGHT989D$ dd bs=1 skip=$((663743397-36)) count=512 if=DumpData.bin | hexdump -C 00000000  70 20 2f 64 61 74 61 20  69 6e 20 74 69 6d 65 21  |p /data in time!| 00000010  00 61 65 73 2d 63 62 63  2d 70 6c 61 69 6e 3a 73  |.aes-cbc-plain:s| 00000020  68 61 31 00 61 65 73 2d  63 62 63 2d 65 73 73 69  |ha1.aes-cbc-essi| 00000030  76 3a 73 68 61 32 35 36  00 6e 6f 74 20 72 75 6e  |v:sha256.not run| 00000040  6e 69 6e 67 20 77 69 74  68 20 65 6e 63 72 79 70  |ning with encryp| 00000050  74 69 6f 6e 2c 20 61 62  6f 72 74 69 6e 67 00 6d  |tion, aborting.m| 00000060  61 73 74 65 72 20 6b 65  79 20 66 69 6c 65 20 64  |aster key file d| 00000070  6f 65 73 20 6e 6f 74 20  65 78 69 73 74 2c 20 61  |oes not exist, a| 00000080  62 6f 72 74 69 6e 67 00  45 72 72 6f 72 20 67 65  |borting.Error ge| 00000090  74 74 69 6e 67 20 63 72  79 70 74 20 66 6f 6f 74  |tting crypt foot| 000000a0  65 72 20 61 6e 64 20 6b  65 79 0a 00 64 65 63 72  |er and key..decr| 000000b0  79 70 74 69 6e 67 00 44  65 63 72 79 70 74 69 6f  |ypting.Decryptio| 000000c0  6e 20 70 72 6f 63 65 73  73 20 64 69 64 6e 27 74  |n process didn't| 000000d0  20 66 69 6e 69 73 68 20  73 75 63 63 65 73 73 66  | finish successf| 000000e0  75 6c 6c 79 0a 00 45 6e  63 72 79 70 74 69 6f 6e  |ully..Encryption| 000000f0  20 70 72 6f 63 65 73 73  20 64 69 64 6e 27 74 20  | process didn't | 00000100  66 69 6e 69 73 68 20 73  75 63 63 65 73 73 66 75  |finish successfu| 00000110  6c 6c 79 0a 00 72 6f 2e  63 72 79 70 74 6f 2e 73  |lly..ro.crypto.s| 00000120  75 70 70 6f 72 74 00 72  65 63 6f 76 65 72 79 5f  |upport.recovery_| 00000130  6d 6f 75 6e 74 00 43 6c  65 61 72 20 52 65 63 6f  |mount.Clear Reco| 00000140  76 65 72 79 20 6d 6f 75  6e 74 21 28 25 64 29 0a  |very mount!(%d).| 00000150  00 66 69 6e 64 20 70 72  6f 63 65 73 73 20 25 73  |.find process %s| 00000160  20 28 25 64 29 20 68 61  73 20 6f 70 65 6e 20 66  | (%d) has open f| 00000170  69 6c 65 20 25 73 00 66  69 6e 64 20 70 72 6f 63  |ile %s.find proc| 00000180  65 73 73 20 25 73 20 28  25 64 29 20 68 61 73 20  |ess %s (%d) has | 00000190  6f 70 65 6e 20 66 69 6c  65 6d 61 70 20 66 6f 72  |open filemap for| 000001a0  20 25 73 00 66 69 6e 64  20 70 72 6f 63 65 73 73  | %s.find process| 000001b0  20 25 73 20 28 25 64 29  20 68 61 73 20 63 77 64  | %s (%d) has cwd| 000001c0  20 77 69 74 68 69 6e 20  25 73 00 66 69 6e 64 20  | within %s.find | 000001d0  70 72 6f 63 65 73 73 20  25 73 20 28 25 64 29 20  |process %s (%d) | 000001e0  68 61 73 20 63 68 72 6f  6f 74 20 77 69 74 68 69  |has chroot withi| 000001f0  6e 20 25 73 00 66 69 6e  64 20 70 72 6f 63 65 73  |n %s.find proces| 00000200 512+0 records in 512+0 records out 512 bytes (512 B) copied, 0.000387062 s, 1.3 MB/s

Now, using the forensic tool of your choice, extract 16384 bytes starting from the 0xC5B1B5D0 hex and save that out as a new file. You don't necessarily need to take all 16384 bytes but for the purpose of this document, let's take 16384 bytes as that number emulates pulling out the last 16K of the encrypted userdata partition.

dk@anubis:~/Samsung_SII_SGHT989D$ dd bs=1 skip=$((402653220-36)) count=16384 if=DumpData.bin of=encryption_footer 16384+0 records in 16384+0 records out 16384 bytes (16 kB) copied, 0.0167546 s, 978 kB/s

Your extracted chunk containing the encryption key should look as follows:

dk@anubis:~/Samsung_SII_SGHT989D$ hexdump -C encryption_footer 00000000  c5 b1 b5 d0 01 00 00 00  d8 00 00 00 00 00 00 00  |................| 00000010  20 00 00 00 00 00 00 00  00 10 40 00 00 00 00 00  | .........@.....| 00000020  00 00 00 00 61 65 73 2d  63 62 63 2d 65 73 73 69  |....aes-cbc-essi| 00000030  76 3a 73 68 61 32 35 36  00 00 00 00 00 00 00 00  |v:sha256........| 00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................| * 00000060  00 00 00 00 b1 e4 01 10  02 00 00 00 00 00 00 00  |................| 00000070  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................| 00000080  00 00 00 00 f1 da 84 cf  0f 82 30 b9 a7 bc 16 d4  |..........0.....| 00000090  bc ed 22 20 43 6d 7c b7  50 aa a3 f2 6f 41 d3 28  |.." Cm|.P...oA.(| 000000a0  48 cc 63 0d 10 30 ae 35  b6 e3 73 b2 5c 1c 61 dd  |H.c..0.5..s.\.a.| 000000b0  34 80 07 89 67 34 2e 47  23 68 20 ac df cf 41 3a  |4...g4.G#h ...A:| 000000c0  10 0c 2d f4 18 9e 50 a0  47 09 ca 5c 90 1d 42 84  |..-...P.G..\..B.| 000000d0  8a 48 62 7c 00 00 00 00  00 00 00 00 00 00 00 00  |.Hb|............| 000000e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................| * 00001000  4b 44 4f 00 00 00 00 00  00 00 00 00 00 00 00 00  |KDO.............| 00001010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................| * 00004000

Finally, let's further extract just the encryption key and save it out in JtR format. This can either be done with the Sandy framework, a hex editor, dd, xxd, etc. Let's do it with xxd in this case. Note: We are seeking 132 bytes (jumping 132 bytes) into our file for the purpose that we want to grab the 80 bytes starting at 0xF1DA84CF. You can do this manually using the forensic tool of your choice as well. Just make sure you save those 80 bytes to a flat text file like the file we create below.

dk@anubis:~/Samsung_SII_SGHT989D$ xxd -seek 132 -len 80 -c 128 -p encryption_footer f1da84cf0f8230b9a7bc16d4bced2220436d7cb750aaa3f26f41d32848cc630d1030ae35b6e373b25c1c61dd3480078967342e47236820acdfcf413a100c2df4189e50a04709ca5c901d42848a48627c

You can see we have extracted just the key. Let's repeat that and write it out to a file.

dk@anubis:~/Samsung_SII_SGHT989D$ xxd -seek 132 -len 80 -c 128 -p encryption_footer > encryption_footer.jtr dk@anubis:~/Samsung_SII_SGHT989D$ cat encryption_footer.jtr f1da84cf0f8230b9a7bc16d4bced2220436d7cb750aaa3f26f41d32848cc630d1030ae35b6e373b25c1c61dd3480078967342e47236820acdfcf413a100c2df4189e50a04709ca5c901d42848a48627c

That's it! This .jtr file will later be fed into JtR for our brute force attempt.

Installing Sandy

Installing Sandy involves cloning the 'sandy' repository from github. After cloning the repository you will have a 'sandy' directory which contains the Python application.

dk@anubis:/sc/git-ext$ git clone https://github.com/donctl/sandy Cloning into 'sandy'... remote: Counting objects: 95, done. remote: Total 95 (delta 0), reused 0 (delta 0) Unpacking objects: 100% (95/95), done. Checking connectivity... done.

On supported devices, 'sandy' can be used to extract the encrypted key and convert it to JtR format however as noted previously this did not work in our case. If you are interested in playing with 'sandy', you can start it as follows:

dk@anubis:/sc/git-ext$ cd sandy dk@anubis:~/sc/git-ext/sandy$ python main.py -p sandy

Compiling John The Ripper (JtR) Jumbo with the sandcrypt plugin

Start by exploding your JtR jumbo source code and copying the sandcrypt plugin (from the 'sandy' installation) into the 'src' tree of JtR. After that you can start your compilation:

dk@anubis:~/src$ tar -zxf john-1.7.9-jumbo-7.tar.gz dk@anubis:~/src$ cd john-1.7.9-jumbo-7/src/ dk@anubis:~/src/john-1.7.9-jumbo-7/src$ cp ~/sc/git-ext/sandy/jtr_plugin/sandcrypt_fmt_plug.c . dk@anubis:~/src/john-1.7.9-jumbo-7/src$ make linux-x86-64-native

Note: If you receive an error about '\<openssl/HMAC.h>' when JtR is compiling 'sandcrypt_fmt_plug.c', please ensure you have pulled the latest version of 'sandy' from github. An issue previously existed which was corrected in the latest revision.

Brute forcing the Samsung Android FDE

The first thing you will need is a wordlist file. For the purpose of this document, my sample wordlist file is as follows. If you would like to generate a wordlist you can use the tool of your choice or use 'crunch' which is optionally included at the bottom of this document. When generating your wordlist please keep in mind that Samsung FDE specifically requires 6 characters including 1 digit. Note: The wordlist is likely the most important part of the decryption and should be crafted with care.

dk@anubis:~/Samsung_SII_SGHT989D$ cat wordlist.txt this5215 is23123 a000000 test10101 test123 test321 test2test bleck2bleck end2end

Once the compilation of 'john' has completed you can change into the 'run' directory which is where the 'john' binary is located. Now you can run 'john', provide it your wordlist, the sandcrypt plugin, and the Samsung FDE .jtr you created earlier.

dk@anubis:~/src/john-1.7.9-jumbo-7/run$ ./john --wordlist=/home/dk/Samsung_SII_SGHT989D/wordlist.txt --format=sandcrypt /home/dk/Samsung_SII_SGHT989D/encryption_footer.jtr Loaded 1 password hash (Samsung phone encryption [32/64]) test123          (?) guesses: 1  time: 0:00:00:00 DONE (Mon Sep 22 14:23:52 2014)  c/s: 71.42  trying: test123 Use the "--show" option to display all of the cracked passwords reliably

Success! JtR identified 'test123' as the decryption password.

Note: If you run 'john' a second time you will notice that it doesn't crack any passwords! The reason being is that 'john' is smart enough to keep track of passwords it has already processed and it stores these in a 'john.pot' file. If you delete the 'john.pot' file then 'john' will reattempt cracking the passwords again.

dk@anubis:~/src/john-1.7.9-jumbo-7/run$ ./john --wordlist=/home/dk/Samsung_SII_SGHT989D/wordlist.txt --format=sandcrypt /home/dk/Samsung_SII_SGHT989D/encryption_footer.jtr Loaded 1 password hash (Samsung phone encryption [32/64]) No password hashes left to crack (see FAQ) dk@anubis:~/src/john-1.7.9-jumbo-7/run$ cat john.pot f1da84cf0f8230b9a7bc16d4bced2220436d7cb750aaa3f26f41d32848cc630d1030ae35b6e373b25c1c61dd3480078967342e47236820acdfcf413a100c2df4189e50a04709ca5c901d42848a48627c:test123 dk@anubis:~/src/john-1.7.9-jumbo-7/run$ rm john.pot dk@anubis:~/src/john-1.7.9-jumbo-7/run$ ./john --wordlist=/home/dk/Samsung_SII_SGHT989D/wordlist.txt --format=sandcrypt /home/dk/Samsung_SII_SGHT989D/encryption_footer.jtr Loaded 1 password hash (Samsung phone encryption [32/64]) test123          (?) guesses: 1  time: 0:00:00:00 DONE (Tue Sep 23 13:39:45 2014)  c/s: 83.33  trying: test123 Use the "--show" option to display all of the cracked passwords reliably

Optional: Wordlist generation

You can use 'crunch' to generate a '6 character including 1 digit' wordlist or bigger wordlist. Start by extracting the crunch source code and compiling it as follows:

dk@anubis:~/src$ tar -zxf crunch-3.6.tgz dk@anubis:~/src$ cd crunch-3.6/ dk@anubis:~/src/crunch-3.6$ make Building binary... /usr/bin/gcc  -pthread -Wall -pedantic -std=c99   crunch.c -lm  -o crunch

Now, as an example, let's generate a wordlist that is 6 -> 8 characters in length which is just comprised of digits.

dk@anubis:~/src/crunch-3.6$ ./crunch 6 8 -f charset.lst numeric -o wordlist.txt Crunch will now generate the following amount of data: 987000000 bytes 941 MB 0 GB 0 TB 0 PB Crunch will now generate the following number of lines: 111000000 `crunch:  22% completed generating output` crunch:  43% completed generating output `crunch:  65% completed generating output` crunch:  86% completed generating output `crunch: 100% completed generating output`

Here is what our wordlist.txt looks like:

dk@anubis:~/src/crunch-3.6$ head wordlist.txt 000000 000001 000002 000003 000004 000005 000006 000007 000008 000009

You can now use this wordlist for your JtR attempt to crack the FDE.