commit
d9fffd39ec
|
@ -1180,6 +1180,7 @@ class smb(connection):
|
|||
if self.args.pvk is not None:
|
||||
try:
|
||||
self.pvkbytes = open(self.args.pvk, 'rb').read()
|
||||
self.logger.success("Loading domain backupkey from {}".format(self.args.pvk))
|
||||
except Exception as 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:
|
||||
try:
|
||||
dc_target = Target.create(
|
||||
domain = self.domain,
|
||||
username = self.username,
|
||||
password = self.password,
|
||||
target = self.domain,
|
||||
lmhash = self.lmhash,
|
||||
nthash = self.nthash,
|
||||
do_kerberos = self.kerberos,
|
||||
aesKey = self.aesKey,
|
||||
no_pass = True,
|
||||
use_kcache = self.use_kcache,
|
||||
)
|
||||
dc_conn = DPLootSMBConnection(dc_target)
|
||||
dc_conn.connect() # Connect to DC
|
||||
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
|
||||
else:
|
||||
self.no_da = False
|
||||
except Exception as e:
|
||||
self.logger.debug("Could not get domain backupkey: {}".format(e))
|
||||
pass
|
||||
results = self.db.get_domain_backupkey(self.domain)
|
||||
except:
|
||||
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")
|
||||
return False
|
||||
if len(results) > 0:
|
||||
self.logger.success("Loading domain backupkey from cmedb...")
|
||||
self.pvkbytes = results[0][2]
|
||||
else:
|
||||
try:
|
||||
dc_target = Target.create(
|
||||
domain = self.domain,
|
||||
username = self.username,
|
||||
password = self.password,
|
||||
target = self.domain,
|
||||
lmhash = self.lmhash,
|
||||
nthash = self.nthash,
|
||||
do_kerberos = self.kerberos,
|
||||
aesKey = self.aesKey,
|
||||
no_pass = True,
|
||||
use_kcache = self.use_kcache,
|
||||
)
|
||||
dc_conn = DPLootSMBConnection(dc_target)
|
||||
dc_conn.connect() # Connect to DC
|
||||
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(
|
||||
domain = self.domain,
|
||||
|
@ -1268,9 +1279,11 @@ class smb(connection):
|
|||
self.logger.debug("Error while looting credentials: {}".format(e))
|
||||
for credential in credentials:
|
||||
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:
|
||||
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:
|
||||
# Collect Chrome Based Browser stored secrets
|
||||
dump_cookies = True if self.args.dpapi == "cookies" else False
|
||||
|
@ -1280,6 +1293,8 @@ class smb(connection):
|
|||
self.logger.debug("Error while looting browsers: {}".format(e))
|
||||
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.db.add_dpapi_secrets(target.address, credential.browser.upper(), credential.winuser, credential.username, credential.password, credential.url)
|
||||
|
||||
if dump_cookies:
|
||||
self.logger.info("Start Dumping Cookies")
|
||||
for cookie in cookies:
|
||||
|
@ -1295,7 +1310,7 @@ class smb(connection):
|
|||
for vault in vaults:
|
||||
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.db.add_dpapi_secrets(target.address, 'IEX', vault.winuser, vault.username, vault.password, vault.resource)
|
||||
|
||||
try:
|
||||
# Collect Firefox stored secrets
|
||||
|
@ -1305,7 +1320,7 @@ class smb(connection):
|
|||
self.logger.debug("Error while looting firefox: {}".format(e))
|
||||
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.db.add_dpapi_secrets(target.address, 'FIREFOX', credential.winuser, credential.username, credential.password, credential.url)
|
||||
|
||||
@requires_admin
|
||||
def lsa(self):
|
||||
|
|
|
@ -76,6 +76,24 @@ class database:
|
|||
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" (
|
||||
# "id" integer PRIMARY KEY,
|
||||
# "computerid", integer,
|
||||
|
@ -537,3 +555,89 @@ class database:
|
|||
cur.close()
|
||||
logging.debug('get_groups(filterTerm={}, groupName={}, groupDomain={}) => {}'.format(filterTerm, groupName, groupDomain, 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')
|
||||
|
||||
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):
|
||||
|
||||
filterTerm = line.strip()
|
||||
|
|
|
@ -43,7 +43,7 @@ aioconsole = "^0.3.3"
|
|||
pywerview = "^0.3.3"
|
||||
minikerberos = "0.3.5"
|
||||
aardwolf = "0.2.5"
|
||||
dploot = "^2.1.14"
|
||||
dploot = "^2.1.16"
|
||||
bloodhound = "^1.6.1"
|
||||
asyauth = "^0.0.12"
|
||||
masky = "^0.2.0"
|
||||
|
|
Loading…
Reference in New Issue