fix: catch BrokenPipeErrors when connecting via SMB

This commit is contained in:
Marshall Hallenbeck
2023-03-10 01:12:59 -05:00
parent 3ecf54bb3f
commit 4b4e2126cd
5 changed files with 55 additions and 34 deletions
+3 -1
View File
@@ -259,7 +259,9 @@ class ldap(connection):
self.local_ip = self.conn.getSMBServer().get_socket().getsockname()[0]
try:
self.conn.login('' , '')
self.conn.login('', '')
except BrokenPipeError as e:
self.logger.error(f"Broken Pipe Error while attempting to login")
except Exception as e:
if "STATUS_NOT_SUPPORTED" in str(e):
self.no_ntlm = True
+4
View File
@@ -251,6 +251,8 @@ class mssql(connection):
add_user_bh(self.username, self.domain, self.logger, self.config)
if not self.args.continue_on_success:
return True
except BrokenPipeError as e:
self.logger.error(f"Broken Pipe Error while attempting to login")
except Exception as e:
self.logger.error(u'{}\\{}:{} {}'.format(domain,
username,
@@ -298,6 +300,8 @@ class mssql(connection):
add_user_bh(self.username, self.domain, self.logger, self.config)
if not self.args.continue_on_success:
return True
except BrokenPipeError as e:
self.logger.error(f"Broken Pipe Error while attempting to login")
except Exception as e:
self.logger.error(u'{}\\{}:{} {}'.format(domain,
username,
+1 -1
View File
@@ -362,7 +362,7 @@ class database:
# if we're filtering by domain controllers
elif filter_term == 'dc':
q = q.filter(
self.ComputersTable.c.dc == 1
self.ComputersTable.c.dc == True
)
if domain:
q = q.filter(
+10 -2
View File
@@ -264,6 +264,8 @@ class smb(connection):
no_ntlm = False
try:
self.conn.login('', '')
except BrokenPipeError as e:
self.logger.error(f"Broken Pipe Error while attempting to login")
except Exception as e:
if "STATUS_NOT_SUPPORTED" in str(e):
# no ntlm supported
@@ -481,7 +483,10 @@ class smb(connection):
self.password = password
self.username = username
self.domain = domain
self.conn.login(self.username, self.password, domain)
try:
self.conn.login(self.username, self.password, domain)
except BrokenPipeError as e:
self.logger.error(f"Broken Pipe Error while attempting to login")
self.check_if_admin()
self.db.add_credential('plaintext', domain, self.username, self.password)
@@ -548,7 +553,10 @@ class smb(connection):
nthash = self.hash
self.domain = domain
self.conn.login(self.username, '', domain, lmhash, nthash)
try:
self.conn.login(self.username, '', domain, lmhash, nthash)
except BrokenPipeError as e:
self.logger.error(f"Broken Pipe Error while attempting to login")
self.check_if_admin()
self.db.add_credential('hash', domain, self.username, nthash)
+37 -30
View File
@@ -17,11 +17,6 @@ from sqlalchemy.ext.compiler import compiles
warnings.filterwarnings("ignore", category=SAWarning)
@compiles(insert)
def _prefix_insert_with_ignore(ins, compiler, **kw):
return compiler.visit_insert(ins.prefix_with('OR IGNORE'), **kw)
class database:
def __init__(self, db_engine):
self.ComputersTable = None
@@ -35,8 +30,14 @@ class database:
self.db_engine = db_engine
self.metadata = MetaData()
asyncio.run(self.reflect_tables())
session_factory = sessionmaker(bind=self.db_engine, expire_on_commit=True, class_=AsyncSession)
# we don't use async_sessionmaker or async_scoped_session because when `database` is initialized,
# there is no running async loop
session_factory = sessionmaker(
bind=self.db_engine,
expire_on_commit=True,
class_=AsyncSession
)
Session = scoped_session(session_factory)
# this is still named "conn" when it is the session object; TODO: rename
self.conn = Session()
@@ -77,7 +78,6 @@ class database:
"zerologon" boolean,
"petitpotam" boolean
)''')
# type = hash, plaintext
db_conn.execute('''CREATE TABLE "users" (
"id" integer PRIMARY KEY,
@@ -88,7 +88,6 @@ class database:
"pillaged_from_computerid" integer,
FOREIGN KEY(pillaged_from_computerid) REFERENCES computers(id)
)''')
db_conn.execute('''CREATE TABLE "groups" (
"id" integer PRIMARY KEY,
"domain" text,
@@ -96,7 +95,6 @@ class database:
"member_count_ad" integer,
"last_query_time" text
)''')
# 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,
@@ -105,7 +103,6 @@ class database:
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,
@@ -113,7 +110,6 @@ class database:
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,
@@ -121,7 +117,6 @@ class database:
FOREIGN KEY(userid) REFERENCES users(id),
FOREIGN KEY(groupid) REFERENCES groups(id)
)''')
db_conn.execute('''CREATE TABLE "shares" (
"id" integer PRIMARY KEY,
"computerid" text,
@@ -133,7 +128,6 @@ class database:
FOREIGN KEY(userid) REFERENCES users(id)
UNIQUE(computerid, userid, name)
)''')
db_conn.execute('''CREATE TABLE "dpapi_secrets" (
"id" integer PRIMARY KEY,
"computer" text,
@@ -144,15 +138,13 @@ class database:
"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,
# "computerid", integer,
# "domain" text,
@@ -162,7 +154,8 @@ class database:
# )''')
# pull/545
def add_computer(self, ip, hostname, domain, os, smbv1, signing, spooler=None, zerologon=None, petitpotam=None, dc=None):
def add_computer(self, ip, hostname, domain, os, smbv1, signing, spooler=None, zerologon=None, petitpotam=None,
dc=None):
"""
Check if this host has already been added to the database, if not, add it in.
TODO: return inserted or updated row ids as a list
@@ -308,15 +301,16 @@ class database:
q = update(self.GroupRelationsTable).values(gr_data)
asyncio.run(self.conn.execute(q))
logging.debug('add_credential(credtype={}, domain={}, username={}, password={}, groupid={}, pillaged_from={}) => {}'.format(
credtype,
domain,
username,
password,
group_id,
pillaged_from,
user_rowid
))
logging.debug(
'add_credential(credtype={}, domain={}, username={}, password={}, groupid={}, pillaged_from={}) => {}'.format(
credtype,
domain,
username,
password,
group_id,
pillaged_from,
user_rowid
))
return user_rowid
def remove_credentials(self, creds_id):
@@ -397,7 +391,7 @@ class database:
elif host_ids:
for host_id in host_ids:
q = q.filter(
self.AdminRelationsTable.c.hostid == host_id
self.AdminRelationsTable.c.hostid == host_id
)
asyncio.run(self.conn.execute(q))
@@ -465,12 +459,24 @@ class database:
# if we're filtering by domain controllers
elif filter_term == 'dc':
q = q.filter(
self.ComputersTable.c.dc == 1
self.ComputersTable.c.dc == True
)
if domain:
q = q.filter(
func.lower(self.ComputersTable.c.domain) == func.lower(domain)
)
elif filter_term == 'spooler':
q = q.filter(
self.ComputersTable.c.spooler == True
)
elif filter_term == 'zerologon':
q = q.filter(
self.ComputersTable.c.zerologon == True
)
elif filter_term == 'petitpotam':
q = q.filter(
self.ComputersTable.c.petitpotam == True
)
# if we're filtering by ip/hostname
elif filter_term and filter_term != "":
like_term = func.lower(f"%{filter_term}%")
@@ -589,7 +595,8 @@ class database:
results = asyncio.run(self.conn.execute(q)).all()
logging.debug(f"get_groups(filterTerm={filter_term}, groupName={group_name}, groupDomain={group_domain}) => {results}")
logging.debug(
f"get_groups(filterTerm={filter_term}, groupName={group_name}, groupDomain={group_domain}) => {results}")
return results
def get_group_relations(self, user_id=None, group_id=None):