feat(console): initial console implementation with progress bar

main
Marshall Hallenbeck 2023-03-27 12:18:29 -04:00
parent 2612c5931e
commit 1ade25cc10
3 changed files with 47 additions and 32 deletions

3
cme/console.py Normal file
View File

@ -0,0 +1,3 @@
from rich.console import Console
console = Console()

View File

@ -17,6 +17,7 @@ from cme.servers.http import CMEServer
from cme.first_run import first_run_setup
from cme.context import Context
from cme.paths import CME_PATH
from cme.console import console
from concurrent.futures import ThreadPoolExecutor
from pprint import pformat
from decimal import Decimal
@ -35,6 +36,7 @@ from sqlalchemy.orm import declarative_base
from sqlalchemy.exc import SAWarning
import warnings
from tqdm import tqdm
from rich.progress import Progress
Base = declarative_base()
@ -59,12 +61,20 @@ def create_db_engine(db_path):
return db_engine
async def start_scan(protocol_obj, args, db, targets):
with tqdm(total=len(targets), disable=args.progress) as pbar:
async def start_run(protocol_obj, args, db, targets):
# with tqdm(total=len(targets), disable=args.progress) as pbar:
# with console.status("Running CME") as cme_console:
with Progress(console=console) as progress:
with ThreadPoolExecutor(max_workers=args.threads + 1) as executor:
current = 0
# cme_console.update(f"Running CME {current}/{len(targets)}")
total = len(targets)
tasks = progress.add_task(f"[green]Running CME against {total} targets", total=total)
futures = [executor.submit(protocol_obj, args, db, target) for target in targets]
for future in concurrent.futures.as_completed(futures):
pbar.update(1)
current += 1
# cme_console.update(f"Running CME {current}/{len(targets)}")
progress.update(tasks, completed=current)
def main():
@ -87,6 +97,7 @@ def main():
config = configparser.ConfigParser()
config.read(os.path.join(CME_PATH, 'cme.conf'))
#with console.status("Running CME") as cme_console:
module = None
module_server = None
targets = []
@ -227,7 +238,7 @@ def main():
try:
asyncio.run(
start_scan(protocol_object, args, db, targets)
start_run(protocol_object, args, db, targets)
)
except KeyboardInterrupt:
logging.debug("Got keyboard interrupt")

View File

@ -19,6 +19,7 @@ from impacket.dcerpc.v5.samr import SID_NAME_USE
from impacket.dcerpc.v5.dtypes import MAXIMUM_ALLOWED
from impacket.krb5.kerberosv5 import SessionKeyDecryptionError
from impacket.krb5.types import KerberosException
from cme.console import console
from cme.connection import *
from cme.logger import CMEAdapter
from cme.protocols.smb.firefox import FirefoxTriage
@ -410,7 +411,7 @@ class smb(connection):
return True
def print_host_info(self):
self.logger.info(
console.log(
u"{}{} (name:{}) (domain:{}) (signing:{}) (SMBv1:{})".format(
self.server_os,
' x{}'.format(self.os_arch) if self.os_arch else '',
@ -467,7 +468,7 @@ class smb(connection):
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.success(out)
console.log(out)
if not self.args.local_auth:
add_user_bh(self.username, domain, self.logger, self.config)
if not self.args.continue_on_success:
@ -553,7 +554,7 @@ class smb(connection):
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
)
self.logger.success(out)
console.log(out)
if not self.args.local_auth:
add_user_bh(self.username, self.domain, self.logger, self.config)
if not self.args.continue_on_success:
@ -625,7 +626,7 @@ class smb(connection):
highlight('({})'.format(self.config.get('CME', 'pwn3d_label')) if self.admin_privs else '')
)
self.logger.success(out)
console.log(out)
if not self.args.local_auth:
add_user_bh(self.username, self.domain, self.logger, self.config)
if not self.args.continue_on_success:
@ -826,7 +827,7 @@ class smb(connection):
output = u'{}'.format(output.strip())
if self.args.execute or self.args.ps_execute:
self.logger.success(f"Executed command {self.args.exec_method if self.args.exec_method else ''}")
console.log(f"Executed command {self.args.exec_method if self.args.exec_method else ''}")
buf = StringIO(output).readlines()
for line in buf:
self.logger.highlight(line.strip())
@ -933,7 +934,7 @@ class smb(connection):
logging.debug(f"Error adding share: {error}")
pass
self.logger.success('Enumerated shares')
console.log('Enumerated shares')
self.logger.highlight('{:<15} {:<15} {}'.format('Share', 'Permissions', 'Remark'))
self.logger.highlight('{:<15} {:<15} {}'.format('-----', '-----------', '------'))
for share in permissions:
@ -960,7 +961,7 @@ class smb(connection):
def sessions(self):
try:
sessions = get_netsession(self.host, self.domain, self.username, self.password, self.lmhash, self.nthash)
self.logger.success('Enumerated sessions')
console.log('Enumerated sessions')
for session in sessions:
if session.sesi10_cname.find(self.local_ip) == -1:
self.logger.highlight('{:<25} User:{}'.format(session.sesi10_cname, session.sesi10_username))
@ -972,7 +973,7 @@ class smb(connection):
disks = []
try:
disks = get_localdisks(self.host, self.domain, self.username, self.password, self.lmhash, self.nthash)
self.logger.success('Enumerated disks')
console.log('Enumerated disks')
for disk in disks:
self.logger.highlight(disk.disk)
except Exception as e:
@ -1004,9 +1005,9 @@ class smb(connection):
)
if self.args.local_groups:
self.logger.success('Enumerated members of local group')
console.log('Enumerated members of local group')
else:
self.logger.success('Enumerated local groups')
console.log('Enumerated local groups')
for group in groups:
if group.name:
@ -1054,7 +1055,7 @@ class smb(connection):
self.logger.info('Trying with SAMRPC protocol')
groups = SamrFunc(self).get_local_groups()
if groups:
self.logger.success('Enumerated local groups')
console.log('Enumerated local groups')
logging.debug(f"Local groups: {groups}")
for group_name, group_rid in groups.items():
@ -1105,7 +1106,7 @@ class smb(connection):
custom_filter=str()
)
self.logger.success('Enumerated members of domain group')
console.log('Enumerated members of domain group')
for group in groups:
member_count = len(group.member) if hasattr(group, 'member') else 0
self.logger.highlight('{}\\{}'.format(
@ -1153,7 +1154,7 @@ class smb(connection):
custom_filter=str()
)
self.logger.success('Enumerated domain group(s)')
console.log('Enumerated domain group(s)')
for group in groups:
member_count = len(group.member) if hasattr(group, 'member') else 0
self.logger.highlight('{:<40} membercount: {}'.format(
@ -1197,7 +1198,7 @@ class smb(connection):
custom_filter=str()
)
self.logger.success('Enumerated domain computer(s)')
console.log('Enumerated domain computer(s)')
for hosts in hosts:
domain, host_clean = self.domainfromdnshostname(hosts.dnshostname)
self.logger.highlight('{}\\{:<30}'.format(domain, host_clean))
@ -1218,7 +1219,7 @@ class smb(connection):
lmhash=self.lmhash,
nthash=self.nthash
)
self.logger.success('Enumerated logged_on users')
console.log('Enumerated logged_on users')
if self.args.loggedon_users_filter:
for user in logged_on:
if re.match(self.args.loggedon_users_filter, user.wkui1_username):
@ -1393,7 +1394,7 @@ class smb(connection):
with open(self.args.put_file[0], 'rb') as file:
try:
self.conn.putFile(self.args.share, self.args.put_file[1], file.read)
self.logger.success(f"Created file {self.args.put_file[0]} on \\\\{elf.args.share}\\{self.args.put_file[1]}")
console.log(f"Created file {self.args.put_file[0]} on \\\\{elf.args.share}\\{self.args.put_file[1]}")
except Exception as e:
self.logger.error('Error writing file to share {}: {}'.format(self.args.share, e))
@ -1405,7 +1406,7 @@ class smb(connection):
with open(file_handle, 'wb+') as file:
try:
self.conn.getFile(self.args.share, self.args.get_file[0], file.write)
self.logger.success('File {} was transferred to {}'.format(self.args.get_file[0], file_handle))
console.log('File {} was transferred to {}'.format(self.args.get_file[0], file_handle))
except Exception as e:
self.logger.error('Error reading file {}: {}'.format(self.args.share, e))
@ -1441,10 +1442,10 @@ class smb(connection):
perSecretCallback=lambda secret: add_sam_hash(secret, host_id))
self.logger.success('Dumping SAM hashes')
console.log('Dumping SAM hashes')
SAM.dump()
SAM.export(self.output_filename)
self.logger.success('Added {} SAM hashes to the database'.format(highlight(add_sam_hash.sam_hashes)))
console.log('Added {} SAM hashes to the database'.format(highlight(add_sam_hash.sam_hashes)))
try:
self.remote_ops.finish()
@ -1459,7 +1460,7 @@ class smb(connection):
if self.args.pvk is not None:
try:
self.pvkbytes = open(self.args.pvk, 'rb').read()
self.logger.success(f"Loading domain backupkey from {self.args.pvk}")
console.log(f"Loading domain backupkey from {self.args.pvk}")
except Exception as e:
logging.error(str(e))
@ -1478,7 +1479,7 @@ class smb(connection):
'workspace create dpapi' then re-run the dpapi option")
return False
if len(results) > 0:
self.logger.success("Loading domain backupkey from cmedb...")
console.log("Loading domain backupkey from cmedb...")
self.pvkbytes = results[0][2]
else:
try:
@ -1497,7 +1498,7 @@ class smb(connection):
dc_conn = DPLootSMBConnection(dc_target)
dc_conn.connect() # Connect to DC
if dc_conn.is_admin():
self.logger.success("User is Domain Administrator, exporting domain backupkey...")
console.log("User is Domain Administrator, exporting domain backupkey...")
backupkey_triage = BackupkeyTriage(target=dc_target, conn=dc_conn)
backupkey = backupkey_triage.triage_backupkey()
self.pvkbytes = backupkey.backupkey_v2
@ -1550,7 +1551,7 @@ class smb(connection):
# Collect User and Machine masterkeys
try:
self.logger.success("Collecting User and Machine masterkeys, grab a coffee and be patient...")
console.log("Collecting User and Machine masterkeys, grab a coffee and be patient...")
masterkeys_triage = MasterkeysTriage(
target=target,
conn=conn,
@ -1568,7 +1569,7 @@ class smb(connection):
logging.error("No masterkeys looted")
return
self.logger.success(f"Got {highlight(len(masterkeys))} decrypted masterkeys. Looting secrets")
console.log(f"Got {highlight(len(masterkeys))} decrypted masterkeys. Looting secrets")
try:
# Collect User and Machine Credentials Manager secrets
@ -1706,12 +1707,12 @@ class smb(connection):
isRemote=True,
perSecretCallback=lambda secret_type, secret: add_lsa_secret(secret)
)
self.logger.success('Dumping LSA secrets')
console.log('Dumping LSA secrets')
LSA.dumpCachedHashes()
LSA.exportCached(self.output_filename)
LSA.dumpSecrets()
LSA.exportSecrets(self.output_filename)
self.logger.success(
console.log(
f"Dumped {highlight(add_lsa_secret.secrets)} \
LSA secrets to {self.output_filename + '.secrets'} \
and {self.output_filename + '.cached'}"
@ -1793,9 +1794,9 @@ class smb(connection):
)
try:
self.logger.success('Dumping the NTDS, this could take a while so go grab a redbull...')
console.log('Dumping the NTDS, this could take a while so go grab a redbull...')
NTDS.dump()
self.logger.success('Dumped {} NTDS hashes to {} of which {} were added to the database'.format(
console.log('Dumped {} NTDS hashes to {} of which {} were added to the database'.format(
highlight(add_ntds_hash.ntds_hashes),
self.output_filename + '.ntds',
highlight(add_ntds_hash.added_to_db)