\n"""
data += """
\n""" % date.today().strftime("%d/%m/%Y") data += """
\n""" self.add_to_resultpage(data) #JS Stuff data = """ """ self.add_to_resultpage(data) results = self.get_credz() data = """ """ self.add_to_resultpage(data) ### ##### List cookies results = self.get_cookies() data = """ """ self.add_to_resultpage(data) ##### List gathered files results = self.get_file() data = """
""" self.add_to_resultpage(data) ##### Identify user / IP relations # Confirm audited scope : results = self.get_connected_user() data = """
""" self.add_to_resultpage(data) ##### Identify Local hash reuse results = self.get_credz_sam() data = """
""" self.add_to_resultpage(data) # Confirm audited scope : results = self.get_computers() data = """
\n""" self.add_to_resultpage(data) #Etat des masterkeyz if self.options.debug : results=self.get_masterkeys() data = """
\n""" self.add_to_resultpage(data) # finalise result page data = "" self.add_to_resultpage(data) def get_dpapi_hashes(self): user_hashes=[] with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT sid,hash FROM dpapi_hash") results = cur.fetchall() for line in results: sid=line[0] hash=line[1] with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT user_id FROM user_sid WHERE LOWER(sid)=LOWER('{sid}')") res1 = cur.fetchall() if len(res1) > 0: result = res1[0] user_id = result[0] with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT username FROM users WHERE id={user_id}") res2 = cur.fetchall() if len(res2) > 0: result = res2[0] username = result[0] user_hashes.append((username,hash)) return user_hashes def export_MKF_hashes(self): user_hashes=self.get_dpapi_hashes() self.logging.debug(f"Exporting {len(user_hashes)} MKF Dpapi hash to {self.options.output_directory}") for algo_type in [1,2]: for context in [1,2,3]: filename = os.path.join(self.options.output_directory, 'MKFv%i_type_%i' % (algo_type,context)) if os.path.exists(filename): os.remove(filename) for entry in user_hashes: try: username=entry[0] hash=entry[1] #on retire les hash "MACHINE$" if username != "MACHINE$": #Pour le moment on copie juste les hash. voir pour faire evoluer CrackHash et prendrre username:hash algo_type=int(hash.split('*')[0][-1]) context=int(hash.split('*')[1]) filename = os.path.join(self.options.output_directory, 'MKFv%i_type_%i' % (algo_type, context)) filename2 = os.path.join(self.options.output_directory, 'MKFv%i_type_%i_WITH_USERNAME' % (algo_type, context)) f=open(filename,'ab') f.write(f"{hash}\n".encode('utf-8')) f.close() f = open(filename2, 'ab') f.write(f"{username}:{hash}\n".encode('utf-8')) f.close() except Exception as ex: self.logging.error(f"Exception in export dpapi hash to {filename}") self.logging.debug(ex) def get_dcc2_hashes(self): with self.conn: cur = self.conn.cursor() cur.execute("SELECT DISTINCT username,password FROM credz WHERE LOWER(type)=LOWER('DCC2') ORDER BY username ASC ") results = cur.fetchall() return results def export_dcc2_hashes(self): user_hashes=self.get_dcc2_hashes() filename = os.path.join(self.options.output_directory, 'hash_DCC2') self.logging.debug(f"Exporting {len(user_hashes)} DCC2 hash to {self.options.output_directory}") if os.path.exists(filename): os.remove(filename) for entry in user_hashes: try: username=entry[0] hash=entry[1] f=open(filename,'ab') f.write(f"{username}:{hash}\n".encode('utf-8')) f.close() except Exception as ex: self.logging.error(f"Exception in export DCC2 hash to {filename}") self.logging.debug(ex) self.logging.debug(f"Export Done!") def export_credz(self,distinct=True): user_credz=self.get_credz(distinct=True) filename = os.path.join(self.options.output_directory, 'raw_credz') self.logging.info(f"Exporting {len(user_credz)} credz to {self.options.output_directory}") if os.path.exists(filename): os.remove(filename) for index, cred in enumerate(user_credz): username, password = cred try: f=open(filename,'ab') f.write(f"{username}:{password}\n".encode('utf-8')) f.close() except Exception as ex: self.logging.error(f"Exception in export raw credz to {filename}") self.logging.debug(ex) self.logging.debug(f"Export Done!") def export_sam(self): user_credz=self.get_credz(distinct_sam=True) filename = os.path.join(self.options.output_directory, 'raw_sam') self.logging.info(f"Exporting {len(user_credz)} NTLM credz to {self.options.output_directory}") if os.path.exists(filename): os.remove(filename) for index, cred in enumerate(user_credz): username, password = cred try: f=open(filename,'ab') f.write(f"{username}:{password}\n".encode('utf-8')) f.close() except Exception as ex: self.logging.error(f"Exception in export raw sam to {filename}") self.logging.debug(ex) self.logging.debug(f"Export Done!") def export_cookies(self): user_credz=self.get_cookies() filename = os.path.join(self.options.output_directory, 'raw_cookies') self.logging.info(f"Exporting {len(user_credz)} cookies to {self.options.output_directory}") if os.path.exists(filename): os.remove(filename) for index, cred in enumerate(user_credz): name, value, expires_utc, target, type, pillaged_from_computerid, pillaged_from_userid = cred try: f=open(filename,'ab') f.write(f"{target}:{name}:{value}\n".encode('utf-8')) f.close() except Exception as ex: self.logging.error(f"Exception in export raw credz to {filename}") self.logging.debug(ex) self.logging.debug(f"Export Done!") def get_credz_count(self,current_type,extra_conditions=''): with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT count(id) FROM credz WHERE LOWER(type)=LOWER('{current_type}') {extra_conditions}") results = cur.fetchall() return results def get_credz(self, filterTerm=None, credz_type=None,distinct=False,distinct_sam=False): """ Return credentials from the database. """ if credz_type: with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT * FROM credz WHERE LOWER(type)=LOWER('{credz_type}')") # if we're filtering by username elif filterTerm and filterTerm != '': with self.conn: cur = self.conn.cursor() cur.execute("SELECT * FROM users WHERE LOWER(username) LIKE LOWER(?)", ['%{}%'.format(filterTerm)]) elif distinct : with self.conn: cur = self.conn.cursor() cur.execute("SELECT DISTINCT username,password FROM credz WHERE LOWER(type) NOT IN ('sam','lsa','dcc2') AND password NOT IN ('')") elif distinct_sam : with self.conn: cur = self.conn.cursor() cur.execute("SELECT DISTINCT username,password FROM credz WHERE LOWER(type) IN ('sam') AND password NOT IN ('')") # otherwise return all credentials else: with self.conn: cur = self.conn.cursor() cur.execute("SELECT * FROM credz ORDER BY type DESC, target ASC ") results = cur.fetchall() return results def get_credz_sam(self): all=[] with self.conn: cur = self.conn.cursor() cur.execute("SELECT count(DISTINCT pillaged_from_computerid),password FROM credz WHERE LOWER(type)=LOWER('SAM') AND LOWER(password) != LOWER('31d6cfe0d16ae931b73c59d7e0c089c0') GROUP BY password ORDER BY username ASC") results = cur.fetchall() for index, res in enumerate(results): nb,passw=res if nb>1: with self.conn: cur = self.conn.cursor() cur.execute("SELECT DISTINCT username, password, type, pillaged_from_computerid FROM credz WHERE LOWER(type)=LOWER('SAM') AND LOWER(password)=LOWER('%s') ORDER BY password ASC, username ASC "%(passw)) all+=cur.fetchall() return all def get_computers(self): with self.conn: cur = self.conn.cursor() cur.execute("SELECT ip,hostname,domain,os,smb_signing_enabled,smbv1_enabled,is_admin,connectivity from computers ORDER BY ip") results = cur.fetchall() return results def get_masterkeys(self): with self.conn: cur = self.conn.cursor() cur.execute("SELECT id,file_path,guid,status,pillaged_from_computerid,pillaged_from_userid,decrypted_with,decrypted_value from masterkey ORDER BY pillaged_from_computerid ASC, pillaged_from_userid ASC") results = cur.fetchall() return results def get_computer_infos(self,computer_id): with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT ip,hostname FROM computers WHERE id={computer_id} LIMIT 1") results = cur.fetchall() return results def get_user_infos(self,user_id): with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT username FROM users WHERE id={user_id} LIMIT 1") results = cur.fetchall() return results def get_user_id(self,username): with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT id FROM users WHERE username={username} LIMIT 1") results = cur.fetchall() return results def get_connected_user(self): with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT ip,username FROM connected_user ORDER BY username ASC, ip ASC") results = cur.fetchall() return results def get_os_from_ip(self,ip): with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT os FROM computers WHERE ip={ip} LIMIT 1") results = cur.fetchall() return results def get_file(self): with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT file_path,filename,extension,pillaged_from_computerid,pillaged_from_userid FROM files ORDER BY pillaged_from_computerid ASC, extension ASC ") results = cur.fetchall() return results def get_cookies(self): with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT name,value,expires_utc,target,type,pillaged_from_computerid,pillaged_from_userid FROM cookies ORDER BY pillaged_from_computerid ASC, expires_utc DESC ") results = cur.fetchall() return results class database: def __init__(self, conn,logger): self.conn = conn self.logging=logger def get_credz(self, filterTerm=None, credz_type=None): """ Return credentials from the database. """ if credz_type: with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT * FROM credz WHERE type='{credz_type}'") # if we're filtering by username elif filterTerm and filterTerm != '': with self.conn: cur = self.conn.cursor() cur.execute("SELECT * FROM users WHERE LOWER(username) LIKE LOWER(?)", ['%{}%'.format(filterTerm)]) # otherwise return all credentials else: with self.conn: cur = self.conn.cursor() cur.execute("SELECT * FROM credz") results = cur.fetchall() return results @staticmethod def db_schema(db_conn): db_conn.execute('''CREATE TABLE "computers" ( "id" integer PRIMARY KEY, "ip" text, "hostname" text, "domain" text, "os" text, "dc" boolean, "smb_signing_enabled" boolean, "smbv1_enabled" boolean, 'default_user_id' integer, "is_admin" boolean, "connectivity" text )''') db_conn.execute('''CREATE TABLE "compliance" ( "id" integer PRIMARY KEY, "laps_enabled" boolean DEFAULT 0, "smb_signing_enabled" boolean DEFAULT 0, "smbv1_enabled" boolean DEFAULT 0, "llmnr_disabled" boolean DEFAULT 0, "pillaged_from_computerid" integer, FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id) )''') # type = hash, plaintext db_conn.execute('''CREATE TABLE "users" ( "id" integer PRIMARY KEY, "domain" text, "username" text, "password" text, "credtype" text, "pillaged_from_computerid" integer, FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id) )''') db_conn.execute('''CREATE TABLE "groups" ( "id" integer PRIMARY KEY, "domain" text, "name" text )''') db_conn.execute('''CREATE TABLE "credz" ( "id" integer PRIMARY KEY, "file_path" text, "username" text, "password" text, "target" text, "type" text, "pillaged_from_computerid" integer, "pillaged_from_userid" integer, FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id), FOREIGN KEY(pillaged_from_userid) REFERENCES users(id) )''') db_conn.execute('''CREATE TABLE "cookies" ( "id" integer PRIMARY KEY, "file_path" text, "name" text, "value" text, "expires_utc" int, "target" text, "type" text, "pillaged_from_computerid" integer, "pillaged_from_userid" integer, FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id), FOREIGN KEY(pillaged_from_userid) REFERENCES users(id) )''') db_conn.execute('''CREATE TABLE "dpapi_hash" ( "id" integer PRIMARY KEY, "file_path" text, "sid" text, "guid" text, "hash" text, "context" text, "pillaged_from_computerid" integer, FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id) )''') db_conn.execute('''CREATE TABLE "user_sid" ( "id" integer PRIMARY KEY, "sid" text, "user_id" integer, FOREIGN KEY(user_id) REFERENCES users(id) )''') db_conn.execute('''CREATE TABLE "connected_user" ( "id" integer PRIMARY KEY, "username" text, "ip" text )''') db_conn.execute('''CREATE TABLE "files" ( "id" integer PRIMARY KEY, "file_path" text, "filename" text, "extension" text, "pillaged_from_computerid" integer, "pillaged_from_userid" integer, FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id), FOREIGN KEY(pillaged_from_userid) REFERENCES users(id) )''') db_conn.execute('''CREATE TABLE "masterkey" ( "id" integer PRIMARY KEY, "file_path" text, "guid" text, "status" integer DEFAULT 0, "pillaged_from_computerid" integer, "pillaged_from_userid" integer, "decrypted_with" text, "decrypted_value" text, FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id), FOREIGN KEY(pillaged_from_userid) REFERENCES users(id) )''') # This table keeps track of which credential has admin access over which machine and vice-versa """ db_conn.execute('''CREATE TABLE "admin_relations" ( "id" integer PRIMARY KEY, "userid" integer, "computerid" integer, FOREIGN KEY(userid) REFERENCES users(id), FOREIGN KEY(computerid) REFERENCES computers(id) )''') db_conn.execute('''CREATE TABLE "loggedin_relations" ( "id" integer PRIMARY KEY, "userid" integer, "computerid" integer, FOREIGN KEY(userid) REFERENCES users(id), FOREIGN KEY(computerid) REFERENCES computers(id) )''') db_conn.execute('''CREATE TABLE "group_relations" ( "id" integer PRIMARY KEY, "userid" integer, "groupid" integer, FOREIGN KEY(userid) REFERENCES users(id), FOREIGN KEY(groupid) REFERENCES groups(id) )''') #db_conn.execute('''CREATE TABLE "ntds_dumps" ( # "id" integer PRIMARY KEY, # "computerid", integer, # "domain" text, # "username" text, # "hash" text, # FOREIGN KEY(computerid) REFERENCES computers(id) # )''') #db_conn.execute('''CREATE TABLE "shares" ( # "id" integer PRIMARY KEY, # "hostid" integer, # "name" text, # "remark" text, # "read" boolean, # "write" boolean # )''') #def add_share(self, hostid, name, remark, read, write): # cur = self.conn.cursor() # cur.execute("INSERT INTO shares (hostid, name, remark, read, write) VALUES (?,?,?,?,?)", [hostid, name, remark, read, write]) # cur.close() """ def add_computer(self, ip, hostname='', domain='', os='', default_user_id=0, dc=0, smb_signing_enabled=False, smbv1_enabled=False,is_admin=False,connectivity='Ok'): """ Check if this host has already been added to the database, if not add it in. """ self.logging.debug(f"[{ip}] {bcolors.OKBLUE}Adding Computer {hostname}{bcolors.ENDC}") try: #domain = domain.split('.')[0].upper() with self.conn: cur = self.conn.cursor() cur.execute(f'SELECT * FROM computers WHERE ip LIKE "{ip}"') results = cur.fetchall() if not len(results): with self.conn: cur = self.conn.cursor() cur.execute(f"INSERT INTO computers (ip, hostname, domain, os, dc, default_user_id,smb_signing_enabled,is_admin,smbv1_enabled,connectivity) VALUES ('{ip}', '{hostname}', '{domain}', '{os}', {dc},{default_user_id},{smb_signing_enabled},{is_admin},{smbv1_enabled},'{connectivity}')") return cur.lastrowid except Exception as ex: self.logging.error(f"Exception in Add Computeur") self.logging.debug(ex) def update_computer(self, ip, hostname=None, domain=None, os=None, default_user_id=None, dc=None, smb_signing_enabled=None, smbv1_enabled=None,is_admin=None,connectivity=None): """ Check if this host has already been added to the database, if not add it in. """ self.logging.debug(f"Updating Computer {ip}") try: #domain = domain.split('.')[0].upper() with self.conn: cur = self.conn.cursor() cur.execute(f'SELECT * FROM computers WHERE ip LIKE "{ip}"') results = cur.fetchall() if len(results): for host in results: id_=host[0] hostname_ = host[2] domain_ = host[3] os_ = host[4] dc_ = host[5] is_admin_ = host[8] connectivity_ = host[9] for val in [(hostname,'hostname'),(domain,'domain'),(os,'os'),(hostname,'hostname'),(default_user_id,'default_user_id'),(dc,'dc'),(smb_signing_enabled,'smb_signing_enabled'),(smbv1_enabled,'smbv1_enabled'),(is_admin,'is_admin'),(connectivity,'connectivity')]: value=val[0] var=val[1] if value != None: with self.conn: cur = self.conn.cursor() cur.execute(f"UPDATE computers SET {var}='{value}' WHERE id={id_}") except Exception as ex: self.logging.error(f"Exception in Add Computeur") self.logging.debug(ex) def add_file(self, file_path, filename,extension,pillaged_from_computerid=None,pillaged_from_computer_ip=None,pillaged_from_userid=None,pillaged_from_username=None): """ Check if this host has already been added to the database, if not add it in. """ self.logging.debug(f"Adding file {filename} - path : {file_path} - {extension} - from user {pillaged_from_username}") try: #domain = domain.split('.')[0].upper() if pillaged_from_userid == None and pillaged_from_username != None: with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT id FROM users WHERE username='{pillaged_from_username}'") results = cur.fetchall() if len(results) > 0: result = results[0] pillaged_from_userid = result[0] #print(f"{pillaged_from_userid} is {pillaged_from_username}") if pillaged_from_computer_ip != None: with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT * FROM computers WHERE LOWER(ip)=LOWER('{pillaged_from_computer_ip}')") results = cur.fetchall() if len(results)>0: result=results[0] pillaged_from_computerid=result[0] if pillaged_from_computerid != None: with self.conn: cur = self.conn.cursor() cur.execute(f'SELECT * FROM files WHERE filename LIKE "{filename}" AND pillaged_from_computerid={pillaged_from_computerid}') results = cur.fetchall() if not len(results): #self.logging.debug(f"inserting file {filename} - {file_path} -{extension}") cur.execute(f"INSERT INTO files (file_path,filename,extension,pillaged_from_computerid,pillaged_from_userid) VALUES ('{file_path}', '{filename}', '{extension}', '{pillaged_from_computerid}', {pillaged_from_userid})") except Exception as ex: self.logging.error(f"Exception in Add Files") self.logging.debug(ex) def add_masterkey(self, file_path, guid,status,decrypted_with='',decrypted_value='',pillaged_from_computerid=None,pillaged_from_computer_ip=None,pillaged_from_userid=None,pillaged_from_username=None): """ Check if this host has already been added to the database, if not add it in. """ self.logging.debug(f"[{pillaged_from_computer_ip}] Adding Masterkey {guid} - path : {file_path} - from user {pillaged_from_username} - {status} ") try: #domain = domain.split('.')[0].upper() if pillaged_from_userid == None and pillaged_from_username != None: with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT id FROM users WHERE username='{pillaged_from_username}'") results = cur.fetchall() if len(results) > 0: result = results[0] pillaged_from_userid = result[0] self.logging.debug(f"[{pillaged_from_computer_ip}] {pillaged_from_userid} is {pillaged_from_username}") if pillaged_from_computer_ip != None: with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT * FROM computers WHERE LOWER(ip)=LOWER('{pillaged_from_computer_ip}')") results = cur.fetchall() if len(results)>0: result=results[0] pillaged_from_computerid=result[0] self.logging.debug( f"[{pillaged_from_computer_ip}] {pillaged_from_computer_ip} is {pillaged_from_computerid}") if pillaged_from_computerid != None: with self.conn: cur = self.conn.cursor() cur.execute(f'SELECT * FROM masterkey WHERE guid LIKE "{guid}" AND pillaged_from_computerid={pillaged_from_computerid}') results = cur.fetchall() if not len(results): with self.conn: cur = self.conn.cursor() self.logging.debug( f"[{pillaged_from_computer_ip}] inserting Masterkey {guid} - {file_path} -{status} {pillaged_from_computerid}', {pillaged_from_userid},{decrypted_with},{decrypted_value}") cur.execute(f"INSERT INTO masterkey (file_path,guid,status,pillaged_from_computerid,pillaged_from_userid,decrypted_with,decrypted_value) VALUES ('{file_path}', '{guid}', '{status}', '{pillaged_from_computerid}', {pillaged_from_userid},'{decrypted_with}','{decrypted_value}')") else: for masterkey in results: if (status != masterkey[3]) or (decrypted_with != masterkey[6]) or (decrypted_value!= masterkey[7]): with self.conn: cur = self.conn.cursor() cur.execute(f"UPDATE masterkey SET status='{status}', decrypted_with='{decrypted_with}', decrypted_value='{decrypted_value}' WHERE id={masterkey[0]}") except Exception as ex: self.logging.error(f"Exception in Add Masterkey") self.logging.debug(ex) def update_masterkey(self, file_path, guid,status,decrypted_with=None,decrypted_value=None,pillaged_from_computerid=None,pillaged_from_computer_ip=None,pillaged_from_userid=None,pillaged_from_username=None): """ Check if this host has already been added to the database, if not add it in. """ self.logging.debug(f"Updating Masterkey {guid} {status} {decrypted_value} {decrypted_with}") try: if pillaged_from_computer_ip != None: with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT * FROM computers WHERE LOWER(ip)=LOWER('{pillaged_from_computer_ip}')") results = cur.fetchall() if len(results)>0: result=results[0] pillaged_from_computerid=result[0] self.logging.debug( f"[{pillaged_from_computer_ip}] {pillaged_from_computer_ip} is {pillaged_from_computerid}") if pillaged_from_computerid != None: with self.conn: cur = self.conn.cursor() cur.execute(f'SELECT * FROM masterkey WHERE guid LIKE "{guid}" AND pillaged_from_computerid={pillaged_from_computerid}') results = cur.fetchall() if len(results): self.logging.debug("Found initial Masterkey") for masterkey in results: with self.conn: cur = self.conn.cursor() cur.execute(f"UPDATE masterkey SET status='{status}', decrypted_with='{decrypted_with}', decrypted_value='{decrypted_value}' WHERE id={masterkey[0]}") except Exception as ex: self.logging.debug(f"Exception in update Masterkey") self.logging.debug(ex) def add_connected_user(self, ip, username): """ Check if this host has already been added to the database, if not add it in. """ self.logging.debug(f"Adding connected user {username} from {ip}") ip=ip.replace('\\','') try: #domain = domain.split('.')[0].upper() with self.conn: cur = self.conn.cursor() cur.execute(f'SELECT * FROM connected_user WHERE username LIKE "{username}" AND ip LIKE "{ip}"') results = cur.fetchall() if not len(results): with self.conn: cur = self.conn.cursor() cur.execute(f"INSERT INTO connected_user (username, ip) VALUES ('{username}','{ip}')") return cur.lastrowid except Exception as ex: self.logging.error(f"Exception in Add Connected users") self.logging.debug(ex) def add_user(self, domain='', username='', password='', credtype='', pillaged_from_computerid=None,pillaged_from_computer_ip=None): try: #domain = domain.split('.')[0].upper() user_rowid = None if pillaged_from_computer_ip != None: with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT * FROM computers WHERE LOWER(ip)=LOWER('{pillaged_from_computer_ip}')") results = cur.fetchall() if len(results)>0: result=results[0] pillaged_from_computerid=result[0] if pillaged_from_computerid != None: query = f"SELECT * FROM users WHERE LOWER(domain)=LOWER('{domain}') AND LOWER(username)=LOWER('{username}') AND pillaged_from_computerid={pillaged_from_computerid}" self.logging.debug(query) with self.conn: cur = self.conn.cursor() cur.execute(query) results = cur.fetchall() if not len(results): query=f"INSERT INTO users (domain, username, password, credtype, pillaged_from_computerid) VALUES ('{domain}','{username}','{password}','{credtype}',{pillaged_from_computerid})" self.logging.debug(query) with self.conn: cur = self.conn.cursor() cur.execute(query) user_rowid = cur.lastrowid self.logging.debug('add_user(domain={}, username={}) => {}'.format(domain, username, user_rowid)) else: self.logging.debug('add_user(domain={}, username={}) ALREADY EXIST'.format(domain, username)) else: self.logging.error(f"user {username} associated computer not found ") except Exception as ex: self.logging.error(f"Exception in add_user ") self.logging.debug(ex) return user_rowid def add_sid(self,username=None,user_id=None,sid=None): try: if user_id == None and username != None: with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT id FROM users WHERE LOWER(username)=LOWER('{username}')") results = cur.fetchall() if len(results) > 0: result = results[0] user_id = result[0] if user_id != None and sid != None: #Deja en base ? query = f"SELECT * FROM user_sid WHERE LOWER(sid)=LOWER('{sid}') AND user_id={user_id}" self.logging.debug(query) with self.conn: cur = self.conn.cursor() cur.execute(query) results = cur.fetchall() if not len(results): query = f"INSERT INTO user_sid (user_id, sid) VALUES ('{user_id}','{sid}')" self.logging.debug(query) with self.conn: cur = self.conn.cursor() cur.execute(query) user_rowid = cur.lastrowid self.logging.debug(f'added SID {sid} for user id {user_id}') except Exception as ex: self.logging.error(f"Exception in add_sid ") self.logging.debug(ex) self.logging.debug(f"Added {username} sid {sid} to database") return 1 def add_dpapi_hash(self, file_path=None, sid=None, guid=None, hash=None,context=None, pillaged_from_computerid=None,pillaged_from_computer_ip=None): try: #domain = domain.split('.')[0].upper() user_rowid = None if pillaged_from_computer_ip != None: with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT * FROM computers WHERE LOWER(ip)=LOWER('{pillaged_from_computer_ip}')") results = cur.fetchall() if len(results)>0: result=results[0] pillaged_from_computerid=result[0] if pillaged_from_computerid != None and sid != None and guid != None and hash != None and context !=None: query = f"SELECT * FROM dpapi_hash WHERE LOWER(sid)=LOWER('{sid}') AND LOWER(guid)=LOWER('{guid}') AND LOWER(hash)=LOWER('{hash}') AND pillaged_from_computerid={pillaged_from_computerid} AND LOWER(context)=LOWER('{context}')" self.logging.debug(query) with self.conn: cur = self.conn.cursor() cur.execute(query) results = cur.fetchall() if not len(results): query=f"INSERT INTO dpapi_hash (file_path, sid, guid, hash, context, pillaged_from_computerid) VALUES ('{file_path}','{sid}','{guid}','{hash}','{context}',{pillaged_from_computerid})" self.logging.debug(query) with self.conn: cur = self.conn.cursor() cur.execute(query) user_rowid = cur.lastrowid self.logging.debug(f'added DPAPI hash {hash}') else: self.logging.debug(f'DPAPI hash {hash} ALREADY EXIST') else: self.logging.error(f"missing infos to register DPAPI hash {hash} - {file_path},{sid},{guid},{hash},{context},{pillaged_from_computerid}") except Exception as ex: self.logging.error(f"Exception in add_hash ") self.logging.debug(ex) return user_rowid def clear_input(self,data): if isinstance(data,int): return data if data is None: data = '' result = data.replace('\x00','') return result def add_credz(self, credz_type, credz_username, credz_password, credz_target, credz_path , pillaged_from_computerid=None,pillaged_from_userid=None,pillaged_from_computer_ip=None,pillaged_from_username=None): """ Check if this credential has already been added to the database, if not add it in. """ user_rowid=None try: credz_username=self.clear_input(credz_username) credz_password = self.clear_input(credz_password) credz_target = self.clear_input(credz_target) credz_path = self.clear_input(credz_path) self.logging.debug(f"{credz_username} - {binascii.hexlify(credz_username.encode('utf-8'))}") self.logging.debug(f"{credz_password} - {binascii.hexlify(credz_password.encode('utf-8'))}") self.logging.debug(f"{credz_target} - {binascii.hexlify(credz_target.encode('utf-8'))}") self.logging.debug(f"{credz_path} - {binascii.hexlify(credz_path.encode('utf-8'))}") self.logging.debug(f"pillaged_from_computer_ip {pillaged_from_computer_ip} - {binascii.hexlify(pillaged_from_computer_ip.encode('utf-8'))}") self.logging.debug(f"pillaged_from_username {pillaged_from_username}") if pillaged_from_computer_ip != None: with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT * FROM computers WHERE LOWER(ip)=LOWER('{pillaged_from_computer_ip}')") results = cur.fetchall() if len(results)>0: result=results[0] pillaged_from_computerid=result[0] self.logging.debug(f"[+] Resolved {pillaged_from_computer_ip} to id : {pillaged_from_computerid}") except Exception as ex: self.logging.error(f"Exception in add_credz 1") self.logging.debug(ex) try: if pillaged_from_username != None: with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT * FROM users WHERE LOWER(username)=LOWER('{pillaged_from_username}') AND pillaged_from_computerid={pillaged_from_computerid}") results = cur.fetchall() if len(results) > 0: result = results[0] pillaged_from_userid = result[0] self.logging.debug(f"[+] Resolved {pillaged_from_username} on machine {pillaged_from_computerid} to id : {pillaged_from_userid}") except Exception as ex: self.logging.error(f"Exception in add_credz 2") self.logging.debug(ex) pass if pillaged_from_computerid == None or pillaged_from_userid == None : self.logging.debug(f"[-] Missing computerId or UserId to register Credz {credz_username} {credz_password} - {credz_target}") #return None try: if pillaged_from_userid == None : query = "SELECT * FROM credz WHERE LOWER(username)=LOWER(:credz_username) AND LOWER(password)=LOWER(:credz_password) AND LOWER(type)=LOWER(:credz_type) AND LOWER(target)=LOWER(:credz_target) AND pillaged_from_computerid=:pillaged_from_computerid" parameters = { "credz_username": credz_username, "credz_password": credz_password, "credz_type": credz_type, "credz_target": credz_target, "pillaged_from_computerid": int(pillaged_from_computerid), } else: query = "SELECT * FROM credz WHERE LOWER(username)=LOWER(:credz_username) AND LOWER(password)=LOWER(:credz_password) AND LOWER(type)=LOWER(:credz_type) AND LOWER(target)=LOWER(:credz_target) AND pillaged_from_computerid=:pillaged_from_computerid AND pillaged_from_userid=:pillaged_from_userid" parameters = { "credz_username": credz_username, "credz_password": credz_password, "credz_type": credz_type, "credz_target": credz_target, "pillaged_from_computerid": int(pillaged_from_computerid), "pillaged_from_userid": int(pillaged_from_userid) } self.logging.debug(query) with self.conn: cur = self.conn.cursor() cur.execute(query, parameters) results = cur.fetchall() except Exception as ex: self.logging.error(f"Exception in add_credz 3") self.logging.debug(ex) try: if not len(results): if pillaged_from_userid == None: query = "INSERT INTO credz (username, password, target, type, pillaged_from_computerid, file_path) VALUES (:credz_username, :credz_password, :credz_target, :credz_type, :pillaged_from_computerid, :credz_path)" parameters = { "credz_username": credz_username, "credz_password": credz_password, "credz_target": credz_target, "credz_type": credz_type, "pillaged_from_computerid": int(pillaged_from_computerid), "credz_path": credz_path, } else: query = "INSERT INTO credz (username, password, target, type, pillaged_from_computerid,pillaged_from_userid, file_path) VALUES (:credz_username, :credz_password, :credz_target, :credz_type, :pillaged_from_computerid, :pillaged_from_userid, :credz_path)" parameters = { "credz_username": credz_username, "credz_password": credz_password, "credz_type": credz_type, "credz_target": credz_target, "pillaged_from_computerid": int(pillaged_from_computerid), "pillaged_from_userid": int(pillaged_from_userid), "credz_path": credz_path, } self.logging.debug(query) with self.conn: cur = self.conn.cursor() cur.execute(query, parameters) user_rowid = cur.lastrowid self.logging.debug( f'added_credential(credtype={credz_type}, target={credz_target}, username={credz_username}, password={credz_password}) => {user_rowid}') else: self.logging.debug( f'added_credential(credtype={credz_type}, target={credz_target}, username={credz_username}, password={credz_password}) => ALREADY IN DB') except Exception as ex: self.logging.error(f"Exception in add_credz 4") self.logging.debug(ex) return None def add_cookies(self, credz_type, credz_name, credz_value,credz_expires_utc, credz_target, credz_path , pillaged_from_computerid=None,pillaged_from_userid=None,pillaged_from_computer_ip=None,pillaged_from_username=None): """ Check if this credential has already been added to the database, if not add it in. """ user_rowid=None try: credz_name=self.clear_input(credz_name) self.logging.debug(f"{credz_name} - {binascii.hexlify(credz_name.encode('utf-8'))}") credz_value = self.clear_input(credz_value) self.logging.debug(f"{credz_value} - {binascii.hexlify(credz_value.encode('utf-8'))}") credz_expires_utc = self.clear_input(credz_expires_utc) self.logging.debug(f"{credz_expires_utc}") credz_target = self.clear_input(credz_target) self.logging.debug(f"{credz_target} - {binascii.hexlify(credz_target.encode('utf-8'))}") credz_path = self.clear_input(credz_path) self.logging.debug(f"{credz_path} - {binascii.hexlify(credz_path.encode('utf-8'))}") self.logging.debug(f"pillaged_from_computer_ip {pillaged_from_computer_ip} - {binascii.hexlify(pillaged_from_computer_ip.encode('utf-8'))}") self.logging.debug(f"pillaged_from_username {pillaged_from_username}") if pillaged_from_computer_ip != None: with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT * FROM computers WHERE LOWER(ip)=LOWER('{pillaged_from_computer_ip}')") results = cur.fetchall() if len(results)>0: result=results[0] pillaged_from_computerid=result[0] self.logging.debug(f"[+] Resolved {pillaged_from_computer_ip} to id : {pillaged_from_computerid}") except Exception as ex: self.logging.error(f"Exception in add_cookie 1") self.logging.debug(ex) try: if pillaged_from_username != None: with self.conn: cur = self.conn.cursor() cur.execute(f"SELECT * FROM users WHERE LOWER(username)=LOWER('{pillaged_from_username}') AND pillaged_from_computerid={pillaged_from_computerid}") results = cur.fetchall() if len(results) > 0: result = results[0] pillaged_from_userid = result[0] self.logging.debug(f"[+] Resolved {pillaged_from_username} on machine {pillaged_from_computerid} to id : {pillaged_from_userid}") except Exception as ex: self.logging.error(f"Exception in add_cookies 2") self.logging.debug(ex) pass if pillaged_from_computerid == None or pillaged_from_userid == None : self.logging.debug(f"[-] Missing computerId or UserId to register Cookie {credz_name} {credz_value} - {credz_target}") #return None try: if pillaged_from_userid == None : query = "SELECT * FROM cookies WHERE LOWER(name)=LOWER(:credz_name) AND LOWER(value)=LOWER(:credz_value) AND expires_utc=:credz_expires_utc AND LOWER(type)=LOWER(:credz_type) AND LOWER(target)=LOWER(:credz_target) AND pillaged_from_computerid=:pillaged_from_computerid" parameters = { "credz_name": credz_name, "credz_value": credz_value, "credz_expires_utc": credz_expires_utc, "credz_type": credz_type, "credz_target": credz_target, "pillaged_from_computerid": int(pillaged_from_computerid), } else: query = "SELECT * FROM cookies WHERE LOWER(name)=LOWER(:credz_name) AND LOWER(value)=LOWER(:credz_value) AND expires_utc=:credz_expires_utc AND LOWER(type)=LOWER(:credz_type) AND LOWER(target)=LOWER(:credz_target) AND pillaged_from_computerid=:pillaged_from_computerid AND pillaged_from_userid=:pillaged_from_userid" parameters = { "credz_name": credz_name, "credz_value": credz_value, "credz_expires_utc": credz_expires_utc, "credz_type": credz_type, "credz_target": credz_target, "pillaged_from_computerid": int(pillaged_from_computerid), "pillaged_from_userid": int(pillaged_from_userid) } self.logging.debug(query) with self.conn: cur = self.conn.cursor() cur.execute(query, parameters) results = cur.fetchall() except Exception as ex: self.logging.error(f"Exception in add_cookie 3") self.logging.debug(ex) try: if not len(results): if pillaged_from_userid == None: query = "INSERT INTO cookies (name, value, expires_utc, target, type, pillaged_from_computerid, file_path) VALUES (:credz_name, :credz_value, :credz_expires_utc, :credz_target, :credz_type, :pillaged_from_computerid, :credz_path)" parameters = { "credz_name": credz_name, "credz_value": credz_value, "credz_expires_utc": credz_expires_utc, "credz_target": credz_target, "credz_type": credz_type, "pillaged_from_computerid": int(pillaged_from_computerid), "credz_path": credz_path, } else: query = "INSERT INTO cookies (name, value, expires_utc, target, type, pillaged_from_computerid,pillaged_from_userid, file_path) VALUES (:credz_name, :credz_value, :credz_expires_utc, :credz_target, :credz_type, :pillaged_from_computerid, :pillaged_from_userid, :credz_path)" parameters = { "credz_name": credz_name, "credz_value": credz_value, "credz_expires_utc": credz_expires_utc, "credz_type": credz_type, "credz_target": credz_target, "pillaged_from_computerid": int(pillaged_from_computerid), "pillaged_from_userid": int(pillaged_from_userid), "credz_path": credz_path, } self.logging.debug(query) with self.conn: cur = self.conn.cursor() cur.execute(query, parameters) user_rowid = cur.lastrowid self.logging.debug( f'added_cookies(credtype={credz_type}, target={credz_target}, name={credz_name}, value={credz_value}) => {user_rowid}') else: self.logging.debug( f'added_credential(credtype={credz_type}, target={credz_target}, name={credz_name}, value={credz_value}) => ALREADY IN DB') except Exception as ex: self.logging.error(f"Exception in add_cookie 4") self.logging.debug(ex) return None def get_credz_old(self, filterTerm=None, credz_type=None): """ Return credentials from the database. """ cur = self.conn.cursor() if credz_type: cur.execute(f"SELECT * FROM credz WHERE credtype='{credz_type}'") # if we're filtering by username elif filterTerm and filterTerm != '': cur.execute("SELECT * FROM users WHERE LOWER(username) LIKE LOWER(?)", ['%{}%'.format(filterTerm)]) # otherwise return all credentials else: cur.execute("SELECT * FROM credz") results = cur.fetchall() cur.close() return results def add_group(self, domain, name): domain = domain.split('.')[0].upper() cur = self.conn.cursor() cur.execute("SELECT * FROM groups WHERE LOWER(domain)=LOWER(?) AND LOWER(name)=LOWER(?)", [domain, name]) results = cur.fetchall() if not len(results): cur.execute("INSERT INTO groups (domain, name) VALUES (?,?)", [domain, name]) cur.close() self.logging.debug('add_group(domain={}, name={}) => {}'.format(domain, name, cur.lastrowid)) return cur.lastrowid def add_admin_user(self, credtype, domain, username, password, host, userid=None): domain = domain.split('.')[0].upper() cur = self.conn.cursor() if userid: cur.execute("SELECT * FROM users WHERE id=?", [userid]) users = cur.fetchall() else: cur.execute("SELECT * FROM users WHERE credtype=? AND LOWER(domain)=LOWER(?) AND LOWER(username)=LOWER(?) AND password=?", [credtype, domain, username, password]) users = cur.fetchall() cur.execute('SELECT * FROM computers WHERE ip LIKE ?', [host]) hosts = cur.fetchall() if len(users) and len(hosts): for user, host in zip(users, hosts): userid = user[0] hostid = host[0] #Check to see if we already added this link cur.execute("SELECT * FROM admin_relations WHERE userid=? AND computerid=?", [userid, hostid]) links = cur.fetchall() if not len(links): cur.execute("INSERT INTO admin_relations (userid, computerid) VALUES (?,?)", [userid, hostid]) cur.close() def get_admin_relations(self, userID=None, hostID=None): cur = self.conn.cursor() if userID: cur.execute("SELECT * FROM admin_relations WHERE userid=?", [userID]) elif hostID: cur.execute("SELECT * FROM admin_relations WHERE computerid=?", [hostID]) results = cur.fetchall() cur.close() return results def get_group_relations(self, userID=None, groupID=None): cur = self.conn.cursor() if userID and groupID: cur.execute("SELECT * FROM group_relations WHERE userid=? and groupid=?", [userID, groupID]) elif userID: cur.execute("SELECT * FROM group_relations WHERE userid=?", [userID]) elif groupID: cur.execute("SELECT * FROM group_relations WHERE groupid=?", [groupID]) results = cur.fetchall() cur.close() return results def remove_admin_relation(self, userIDs=None, hostIDs=None): cur = self.conn.cursor() if userIDs: for userID in userIDs: cur.execute("DELETE FROM admin_relations WHERE userid=?", [userID]) elif hostIDs: for hostID in hostIDs: cur.execute("DELETE FROM admin_relations WHERE hostid=?", [hostID]) cur.close() def remove_group_relations(self, userID=None, groupID=None): cur = self.conn.cursor() if userID: cur.execute("DELETE FROM group_relations WHERE userid=?", [userID]) elif groupID: cur.execute("DELETE FROM group_relations WHERE groupid=?", [groupID]) results = cur.fetchall() cur.close() return results def is_credential_valid(self, credentialID): """ Check if this credential ID is valid. """ cur = self.conn.cursor() cur.execute('SELECT * FROM users WHERE id=? AND password IS NOT NULL LIMIT 1', [credentialID]) results = cur.fetchall() cur.close() return len(results) > 0 def is_credential_local(self, credentialID): cur = self.conn.cursor() cur.execute('SELECT domain FROM users WHERE id=?', [credentialID]) user_domain = cur.fetchall() if user_domain: cur.execute('SELECT * FROM computers WHERE LOWER(hostname)=LOWER(?)', [user_domain]) results = cur.fetchall() cur.close() return len(results) > 0 def get_credentials(self, filterTerm=None, credtype=None): """ Return credentials from the database. """ cur = self.conn.cursor() # if we're returning a single credential by ID if self.is_credential_valid(filterTerm): cur.execute("SELECT * FROM users WHERE id=?", [filterTerm]) elif credtype: cur.execute("SELECT * FROM users WHERE credtype=?", [credtype]) # if we're filtering by username elif filterTerm and filterTerm != '': cur.execute("SELECT * FROM users WHERE LOWER(username) LIKE LOWER(?)", ['%{}%'.format(filterTerm)]) # otherwise return all credentials else: cur.execute("SELECT * FROM users") results = cur.fetchall() cur.close() return results def is_user_valid(self, userID): """ Check if this User ID is valid. """ cur = self.conn.cursor() cur.execute('SELECT * FROM users WHERE id=? LIMIT 1', [userID]) results = cur.fetchall() cur.close() return len(results) > 0 def get_users(self, filterTerm=None): cur = self.conn.cursor() if self.is_user_valid(filterTerm): cur.execute("SELECT * FROM users WHERE id=? LIMIT 1", [filterTerm]) # if we're filtering by username elif filterTerm and filterTerm != '': cur.execute("SELECT * FROM users WHERE LOWER(username) LIKE LOWER(?)", ['%{}%'.format(filterTerm)]) else: cur.execute("SELECT * FROM users") results = cur.fetchall() cur.close() return results def is_computer_valid(self, hostID): """ Check if this host ID is valid. """ cur = self.conn.cursor() cur.execute('SELECT * FROM computers WHERE id=? LIMIT 1', [hostID]) results = cur.fetchall() cur.close() return len(results) > 0 def get_computers(self, filterTerm=None, domain=None): """ Return hosts from the database. """ cur = self.conn.cursor() # if we're returning a single host by ID if self.is_computer_valid(filterTerm): cur.execute("SELECT * FROM computers WHERE id=? LIMIT 1", [filterTerm]) # if we're filtering by domain controllers elif filterTerm == 'dc': if domain: cur.execute("SELECT * FROM computers WHERE dc=1 AND LOWER(domain)=LOWER(?)", [domain]) else: cur.execute("SELECT * FROM computers WHERE dc=1") # if we're filtering by ip/hostname elif filterTerm and filterTerm != "": cur.execute("SELECT * FROM computers WHERE ip LIKE ? OR LOWER(hostname) LIKE LOWER(?)", ['%{}%'.format(filterTerm), '%{}%'.format(filterTerm)]) # otherwise return all computers else: cur.execute("SELECT * FROM computers") results = cur.fetchall() cur.close() return results def get_domain_controllers(self, domain=None): return self.get_computers(filterTerm='dc', domain=domain) def is_group_valid(self, groupID): """ Check if this group ID is valid. """ cur = self.conn.cursor() cur.execute('SELECT * FROM groups WHERE id=? LIMIT 1', [groupID]) results = cur.fetchall() cur.close() self.logging.debug('is_group_valid(groupID={}) => {}'.format(groupID, True if len(results) else False)) return len(results) > 0 def get_groups(self, filterTerm=None, groupName=None, groupDomain=None): """ Return groups from the database """ if groupDomain: groupDomain = groupDomain.split('.')[0].upper() cur = self.conn.cursor() if self.is_group_valid(filterTerm): cur.execute("SELECT * FROM groups WHERE id=? LIMIT 1", [filterTerm]) elif groupName and groupDomain: cur.execute("SELECT * FROM groups WHERE LOWER(name)=LOWER(?) AND LOWER(domain)=LOWER(?)", [groupName, groupDomain]) elif filterTerm and filterTerm !="": cur.execute("SELECT * FROM groups WHERE LOWER(name) LIKE LOWER(?)", ['%{}%'.format(filterTerm)]) else: cur.execute("SELECT * FROM groups") results = cur.fetchall() cur.close() self.logging.debug('get_groups(filterTerm={}, groupName={}, groupDomain={}) => {}'.format(filterTerm, groupName, groupDomain, results)) return results |