feat(console): initial console implementation with progress bar
parent
2612c5931e
commit
1ade25cc10
|
@ -0,0 +1,3 @@
|
|||
from rich.console import Console
|
||||
|
||||
console = Console()
|
|
@ -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")
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue