Created
October 15, 2021 15:01
-
-
Save xperylab/7d54e35aed918c768eeb0787f6e2c118 to your computer and use it in GitHub Desktop.
PM : decrypt attachments
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pgpy | |
import pandas | |
import sqlite3 | |
from Crypto.cipher import AES | |
import os | |
from base64 import b64decode | |
import plistllib | |
import ccl_bplist | |
IVsize = 16 | |
# Decrypt values using main key and AES_CTR | |
def decryptWithMainKey(encrypted, mainKey): | |
iv = encrypted[:IVsize] | |
cipher = AES.new(mainKey, AES.MODE_CTR, initial_value=iv, nonce=b'') | |
return cipher.decrypt(encrypted[IVsize:]) | |
# Decrypt an attachment | |
def decrypt_attachment(proton_path, out_path, key, pwdKey, keyPacket, encfilename, decfilename): | |
att = None | |
for r, _, f in os.walk(proton_path): | |
for file in f: | |
if encfilename in file: | |
att=os.path.join(r,file) | |
break | |
if att: | |
with key.unlock(pwdKey): | |
assert key.is_unlocked | |
with open(att, 'rb') as attfh: | |
buf = b64decode(keyPacket) | |
buf += attfh.read() | |
att_from_blob = pgpy.PGPMessage.from_blob(buf) | |
decatt = key.decrypt(att_from_blob).message | |
with open(os.path.join(out_path,decfilename),'wb') as outatt: | |
outatt.write(decatt) | |
return decfilename | |
return None | |
# Decrypt table Attachments and attachment itself | |
def decrypt_records_attachments(row, mainKey, proton_path, out_path, key, pwdKey): | |
if row['ZFILENAME']: | |
row['ZFILENAME'] = decryptWithMainKey(row['ZFILENAME'], mainKey) | |
if row['ZHEADERINFO']: | |
row['ZHEADERINFO'] = decryptWithMainKey(row['ZHEADERINFO'], mainKey) | |
if row['ZMIMETYPE']: | |
row['ZMIMETYPE'] = decryptWithMainKey(row['ZMIMETYPE'], mainKey) | |
if row['ZFILENAME']: | |
filename = plistlib.loads(row['ZFILENAME'])[0] | |
if row['ZLOCALURL']: | |
encfilename = plistlib.loads(row['ZLOCALURL'])['$objects'][2].split('/')[-1] | |
row['decrypted_file'] = decrypt_attachment(proton_path, out_path, key, pwdKey, row['ZKEYPACKET'], encfilename, filename) | |
row['content-type'] = plistlib.loads(row['ZMIMETYPE'])[0] | |
return row | |
def main(): | |
proton_path = 'path/to/extraction' | |
out_path = 'path/to/output' | |
mainKey = 'decrypted with PMPinProtection or gathered directly from keychain' | |
with open('group.ch.protonmail.protonmail.plist','rb') as p : | |
prefplist = plistlib.load(p) | |
enc_val = prefplist['authKeychainStoreKeyProtectedWithMainKey'] | |
dec_val = decryptWithMainKey(enc_val, mainKey) | |
keychainStorePlist1 = ccl_bplist.load(BytesIO(dec_val)) | |
keychainStorePlist = ccl_bplist.load(BytesIO(keychainStorePlist1[0])) | |
keychainStore = ccl_bplist.deserialise_NsKeyedArchiver(keychainStorePlist, parse_whole_structure=True) | |
privateKeyCoderKey = keychainStore['root']['NS.objects'][0]['privateKeyCoderKey'] | |
key, _ = pgpy.PGPKey.from_blob(privateKeyCoderKey) | |
pwdKey = keychainStore['root']['NS.objects'][0]['AuthCredential.Password'] | |
with sqlite3.connect('ProtonMail.sqlite') as db: | |
df = pandas.read_sql_query(""" | |
SELECT | |
* | |
FROM | |
ZATTACHMENT | |
""",db) | |
df = df.apply(decrypt_records_attachments, args=(mainKey, proton_path, out_path, key, pwdKey, ), axis=1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment