Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python
- # Origin: https://github.com/santoku/Santoku-Linux.git
- # Authors: Thomas Cannon <tcannon@viaforensics.com>
- # Seyton Bradford <sbradford@viaforensics.com>
- # TAGS: Android, Device, Decryption, Crespo, Bruteforce
- #
- # Parses the footer from the encrypted userdata partition
- # Decrypts the master key found in the footer using a supplied password
- # Bruteforces the pin number using the header from the encrypted partition
- # Written for Nexus S (crespo) running Android 4.0.4
- # Footer is located in file userdata_footer on the efs partition
- #
- # --
- # Revision 0.3 (shipped with Santoku Alpha 0.3)
- # ------------
- # Added support for more than 4-digit PINs
- # Speed improvements
- # ------------
- # 2014/5/14 Added 4.4 support (scrypt, etc.) [Nikolay Elenkov]
- # ------------
- # 2014/9/21 Hardcoded support for HTC ONE on Android 4.4.3 [Dmitry Ananyev]
- # --
- #
- # ======= Mount Insctruction =======
- # URL: http://esec-lab.sogeti.com/post/Exploiting-a-vulnerability-in-HTC-One-bootloader-and-bruteforcing-the-PIN-password
- #
- # (root)
- #
- # # mount encrypted volume
- # mkdir /mnt/decrypted_userdata
- # losetup -f encrypted_userdata.img
- # cryptsetup plainOpen -c aes-cbc-essiv:sha256 -d /path/to/decrypted_key /dev/loop0 decrypted_userdata
- # mount /dev/mapper/decrypted_userdata /mnt/decrypted_userdata
- #
- # # unmount encrypted volume
- # umount /mnt/decrypted_userdata
- # cryptsetup close decrypted_userdata
- # losetup -D
- #
- from os import path
- import sys, itertools
- import time
- from struct import Struct
- from M2Crypto import EVP
- import hashlib
- import scrypt
- HASH_COUNT = 2000
- KEY_LEN_BYTES = 32
- IV_LEN_BYTES = 32
- SCRYPT_ADDED_MINOR = 2
- KDF_PBKDF = 1
- KDF_SCRYPT = 2
- class CryptoFooter:
- def __init__(self):
- self.kdf = KDF_SCRYPT
- def unpack(self, data):
- # structure taken from cryptfs.h in crespo source.
- s = Struct('<'+'L H H')
- ftrMagic, majorVersion, minorVersion = s.unpack_from(data)
- if minorVersion < SCRYPT_ADDED_MINOR:
- s = Struct('<'+'L H H L L L L L L L 64s L 48s 16s')
- (self.ftrMagic, self.majorVersion, self.minorVersion,
- self.ftrSize, self.flags, self.keySize, self.spare1,
- self.fsSize1, self.fsSize2, self.failedDecrypt, self.cryptoType,
- self.spare2, self.cryptoKey, self.cryptoSalt) = s.unpack_from(data)
- self.cryptoKey = self.cryptoKey[0:self.keySize]
- else:
- s = Struct('<'+'L H H L L L L L L L 64s L 48s 16s 2Q L B B B B')
- (self.ftrMagic, self.majorVersion, self.minorVersion, self.ftrSize,
- self.flags, self.keySize, self.spare1, self.fsSize1, self.fsSize2,
- self.failedDecrypt, self.cryptoType, self.spare2, self.cryptoKey,
- self.cryptoSalt, self.persistDataOffset1, self.persistDataOffset2,
- self.persistDataSize, self.kdf, self.N_factor, self.r_factor,
- self.p_factor) = s.unpack_from(data)
- self.cryptoKey = self.cryptoKey[0:self.keySize]
- self.N = 1 << self.N_factor
- self.r = 1 << self.r_factor
- self.p = 1 << self.p_factor
- def dump(self):
- print "Android FDE crypto footer"
- print '-------------------------'
- print 'Magic :', "0x%0.8X" % self.ftrMagic
- print 'Major Version :', self.majorVersion
- print 'Minor Version :', self.minorVersion
- print 'Footer Size :', self.ftrSize, "bytes"
- print 'Flags :', "0x%0.8X" % self.flags
- print 'Key Size :', self.keySize * 8, "bits"
- print 'Failed Decrypts:', self.failedDecrypt
- print 'Crypto Type :', self.cryptoType.rstrip("\0")
- print 'Encrypted Key :', "0x" + self.cryptoKey.encode("hex").upper()
- print 'Salt :', "0x" + self.cryptoSalt.encode("hex").upper()
- if self.minorVersion >= SCRYPT_ADDED_MINOR:
- print 'KDF :', "PBKDF2" if self.kdf == KDF_PBKDF else "scrypt"
- print 'N_factor :', "%u (N=%u)" % (self.N_factor, self.N)
- print 'r_factor :', "%u (r=%u)" % (self.r_factor, self.r)
- print 'p_factor :', "%u (p=%u)" % (self.p_factor, self.p)
- print '-------------------------'
- def main(args):
- # default value
- maxpin_digits = 4
- if len(args) < 3:
- print 'Usage: python bruteforce_stdcrypto.py [header file] [footer file] (max PIN digits)'
- print ''
- print '[] = Mandatory'
- print '() = Optional (default is 4)'
- else:
- # use inputed filenames for the two files
- footerFile = args[2]
- headerFile = args[1]
- try:
- if args[3] != None:
- try:
- maxpin_digits = int(args[3])
- if maxpin_digits <= 3:
- print 'PIN has to be >= to 4 digits - defaulting your entered value to 4'
- time.sleep(2)
- maxpin_digits = 4
- except:
- print 'You did not entered a valid maximum number of digits to bruteforce'
- print '=> Defaulting your value to 4.'
- time.sleep(2)
- maxpin_digits = 4
- else:
- # default value
- maxpin_digits = 4
- except:
- print 'Defaulting max PIN digits to 4'
- time.sleep(2)
- maxpin_digits = 4
- assert path.isfile(footerFile), "Footer file '%s' not found." % footerFile
- assert path.isfile(headerFile), "Header file '%s' not found." % headerFile
- fileSize = path.getsize(footerFile)
- assert (fileSize >= 16384), "Input file '%s' must be at least 16384 bytes" % footerFile
- # retrive the key and salt from the footer file
- cf = getCryptoData(footerFile)
- # load the header data for testing the password
- headerData = open(headerFile, 'rb').read(KEY_LEN_BYTES+IV_LEN_BYTES)
- passwd = raw_input("Enter PIN: ")
- result = bruteforcePIN(headerData, cf, 4, passwd)
- if result:
- print 'Found PIN!: ' + result
- else:
- print 'Not correct pass'
- def bruteforcePIN(headerData, cryptoFooter, maxdigits, passwd):
- print 'Trying to Bruteforce Password... please wait'
- # try all possible 4 to maxdigits digit PINs, returns value immediately when found
- #for j in itertools.product(xrange(1),repeat=1):
- if 1:
- # decrypt password
- #passwdTry = ''.join(str(elem) for elem in j)
- passwdTry = passwd
- print 'Trying: ' + passwdTry
- # -- In case you prefer printing every 100
- #try:
- # if (int(passwdTry) % 100) == 0:
- # print 'ok'
- # print 'Trying passwords from %d to %d' %(int(passwdTry),int(passwdTry)+100)
- #except:
- # pass
- # make the decryption key from the password
- decKey = ''
- if cryptoFooter.kdf == KDF_PBKDF:
- decKey = decryptDecodePbkdf2Key(cryptoFooter, passwdTry)
- elif cryptoFooter.kdf == KDF_SCRYPT:
- decKey = decryptDecodeScryptKey(cryptoFooter, passwdTry)
- else:
- raise "Unknown KDF: " + cf.kdf
- print 'Decrypted Key :', "0x" + decKey.encode("hex").upper()
- # try to decrypt the frist 32 bytes of the header data (we don't need the iv)
- decData = decryptData(decKey, "", headerData)
- print 'Decrypted Data :', "0x" + decData.encode("hex").upper()
- #print 'Decrypted Data :', decData
- # has the test worked?
- if decData[KEY_LEN_BYTES+IV_LEN_BYTES-16:KEY_LEN_BYTES+IV_LEN_BYTES] == "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0":
- open("decypted_key", 'wb').write(decKey)
- return passwdTry
- return None
- def getCryptoData(filename):
- data = open(filename, 'rb').read()
- cf = CryptoFooter()
- cf.unpack(data)
- cf.dump()
- return cf
- def decryptDecodePbkdf2Key(cf,password):
- # make the key from the password
- pbkdf2 = EVP.pbkdf2(password, cf.cryptoSalt, iter=HASH_COUNT, keylen=KEY_LEN_BYTES+IV_LEN_BYTES)
- key = pbkdf2[:KEY_LEN_BYTES]
- iv = pbkdf2[KEY_LEN_BYTES:]
- # do the decrypt
- cipher = EVP.Cipher(alg='aes_256_cbc', key=key, iv=iv, op=0) # 0 is DEC
- cipher.set_padding(padding=0)
- decKey = cipher.update(cf.cryptoKey)
- decKey = decKey + cipher.final()
- return decKey
- def decryptDecodeScryptKey(cf,password):
- print '... pass :', password
- print '... pass :', "0x" + password.encode("hex").upper()
- print '... salt :', "0x" + cf.cryptoSalt.encode("hex").upper()
- print '... N :', cf.N
- print '... r :', cf.r
- print '... p :', cf.p
- derived = scrypt.hash(password, cf.cryptoSalt, cf.N, cf.r, cf.p)
- print '... derived :', "0x" + derived.encode("hex").upper()
- key = derived[:KEY_LEN_BYTES]
- iv = derived[KEY_LEN_BYTES:]
- print '... key :', "0x" + key.encode("hex").upper()
- print '... iv :', "0x" + iv.encode("hex").upper()
- # do the decrypt
- cipher = EVP.Cipher(alg='aes_256_cbc', key=key, iv=iv, op=0) # 0 is DEC
- cipher.set_padding(padding=0)
- decKey = cipher.update(cf.cryptoKey)
- decKey = decKey + cipher.final()
- return decKey
- def decryptData(decKey,essiv,data):
- # try to decrypt the actual data
- cipher = EVP.Cipher(alg='aes_256_cbc', key=decKey, iv=essiv, op=0) # 0 is DEC
- cipher.set_padding(padding=0)
- decData = cipher.update(data)
- decData = decData + cipher.final()
- return decData
- if __name__ == "__main__":
- main(sys.argv)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement