Skip to content

Instantly share code, notes, and snippets.

@syskall
Last active August 29, 2015 14:21
Show Gist options
  • Save syskall/37f8ef873651a61d96f4 to your computer and use it in GitHub Desktop.
Save syskall/37f8ef873651a61d96f4 to your computer and use it in GitHub Desktop.
def banner():
print """\x1b[1;32m
██▓ ▒█████ ██▓ ██████ ███▄ █ ▄▄▄ ██▓███ ▄████▄ ██░ ██ ▄▄▄ ▄▄▄█████▓
▓██▒ ▒██▒ ██▒▓██▒ ▒██ ▒ ██ ▀█ █ ▒████▄ ▓██░ ██▒▒██▀ ▀█ ▓██░ ██▒▒████▄ ▓ ██▒ ▓▒
▒██░ ▒██░ ██▒▒██░ ░ ▓██▄ ▓██ ▀█ ██▒▒██ ▀█▄ ▓██░ ██▓▒▒▓█ ▄ ▒██▀▀██░▒██ ▀█▄ ▒ ▓██░ ▒░
▒██░ ▒██ ██░▒██░ ▒ ██▒▓██▒ ▐▌██▒░██▄▄▄▄██ ▒██▄█▓▒ ▒▒▓▓▄ ▄██▒░▓█ ░██ ░██▄▄▄▄██░ ▓██▓ ░
░██████▒░ ████▓▒░░██████▒▒██████▒▒▒██░ ▓██░ ▓█ ▓██▒▒██▒ ░ ░▒ ▓███▀ ░░▓█▒░██▓ ▓█ ▓██▒ ▒██▒ ░
░ ▒░▓ ░░ ▒░▒░▒░ ░ ▒░▓ ░▒ ▒▓▒ ▒ ░░ ▒░ ▒ ▒ ▒▒ ▓▒█░▒▓▒░ ░ ░░ ░▒ ▒ ░ ▒ ░░▒░▒ ▒▒ ▓▒█░ ▒ ░░
░ ░ ▒ ░ ░ ▒ ▒░ ░ ░ ▒ ░░ ░▒ ░ ░░ ░░ ░ ▒░ ▒ ▒▒ ░░▒ ░ ░ ▒ ▒ ░▒░ ░ ▒ ▒▒ ░ ░
░ ░ ░ ░ ░ ▒ ░ ░ ░ ░ ░ ░ ░ ░ ░ ▒ ░░ ░ ░ ░░ ░ ░ ▒ ░
░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
Decrypting Android Snapchat images. Version: %s\x1b[0m""" %(__version__)
import subprocess
import shlex
import md5
from hashlib import md5
from Crypto.Cipher import AES
from Crypto import Random
import json
import time
import os
import base64
import re
def run_get_output(command):
try:
return subprocess.check_output(shlex.split(command),stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as grepexc:
return grepexc.output
def run_blocking_cmd(command):
return subprocess.call(shlex.split(command),stdout=subprocess.PIPE,stderr=subprocess.PIPE)
def get_android_id():
try:
android_id = run_get_output(''' adb shell content query --uri content://settings/secure --projection name:value --where "name=\\'android_id\\'" ''')
p = re.compile('Row: 0 name=android_id, value=(.*)')
android_id = p.findall(android_id)[0].strip()
except:
android_id = run_get_output(''' adb shell settings get secure android_id ''').strip()
print android_id
return android_id
def get_version():
version = run_get_output(''' adb shell dumpsys package com.snapchat.android ''')
p = re.compile('versionName=(.*)')
print p.findall(version)[0].strip()
return p.findall(version)[0].strip()
def pull_bananas_cache_file():
version38 = ''
if VERSION >= '5.0.38.1':
version38 = '1'
run_blocking_cmd(''' adb pull /data/data/com.snapchat.android/cache/bananas%s encrypted_bananas_file ''' %version38)
def snapchat_bananas_password():
m = md5()
m.update( get_android_id() )
m.update('seems legit...')
return m.hexdigest()
def decrypt_bananas_file():
with open("encrypted_bananas_file") as encrypted_bananas:
with open("decrypted_bananas_file",'w') as decrypted_bananas:
cipher = AES.new(snapchat_bananas_password(), AES.MODE_ECB)
decrypt_file(encrypted_bananas,decrypted_bananas,cipher )
def pull_images():
run_blocking_cmd(''' adb pull /data/data/com.snapchat.android/cache/received_image_snaps/ encrypted_received_image_snaps/ ''')
def extract_key_and_iv(json_bananas):
if VERSION < '5.0.38.1':
key= json_bananas['a']
iv= json_bananas['b']
else:
key= json_bananas[0]
iv= json_bananas[1]
return (base64.b64decode(key).encode('hex'),base64.b64decode(iv).encode('hex'))
def decrypt_images():
if not os.path.exists('decrypted_received_image_snaps'): os.makedirs('decrypted_received_image_snaps')
with open("decrypted_bananas_file") as json_file:
bananas = json.loads(json_file.read().decode('utf8'))
if not bananas.get('snapKeysAndIvs',None):
print "Images were already viewed, the key and IV were deleted from the /cache/bananas file. Try reopening Snapchat to check if images where downloaded."
exit(0)
json_bananas = json.loads(bananas['snapKeysAndIvs'],encoding='utf8')
if len(json_bananas) < len( os.listdir("encrypted_received_image_snaps") ):
print "There are less keys than snaps, some snaps won't be able to be decrypted"
for file_name in os.listdir("encrypted_received_image_snaps"):
could_decrypt = False
for snap_pair in json_bananas:
(key,iv) = extract_key_and_iv( json_bananas[snap_pair] )
s = run_get_output('openssl aes-128-cbc -K %s -iv %s -d -in encrypted_received_image_snaps/%s -out decrypted_received_image_snaps/%s' %(key,iv,file_name,file_name))
if s == '':
could_decrypt = True
break
#no error then the image was decoded properly
if not could_decrypt:
print "The image %s could not be decrypted, none of the keys in the bananas file worked" %file_name
#break when decrypt work
def decrypt_file(in_file, out_file, cipher):
bs = AES.block_size
next_chunk = ''
finished = False
while not finished:
chunk, next_chunk = next_chunk, cipher.decrypt(in_file.read(1024 * bs))
if len(next_chunk) == 0:
padding_length = ord(chunk[-1])
chunk = chunk[:-padding_length]
finished = True
out_file.write(chunk)
if __name__ == '__main__':
global VERSION
VERSION = get_version()
#stop the application to save the 'bananas file'
run_blocking_cmd('adb shell am force-stop com.snapchat.android')
pull_images()
pull_bananas_cache_file()
decrypt_bananas_file()
decrypt_images()
#Cleaning up
run_blocking_cmd('rm encrypted_bananas_file decrypted_bananas_file')
run_blocking_cmd('rm -r encrypted_received_image_snaps')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment