Merge pull request #32 from mpgn/linting_marshall

Perflint Speed/Memory Efficiencies
This commit is contained in:
Marshall Hallenbeck
2023-05-06 15:25:26 -04:00
committed by GitHub
15 changed files with 230 additions and 370 deletions
+53 -51
View File
@@ -5,8 +5,11 @@ import cmd
import configparser
import csv
import os
from os import listdir
from os.path import exists
from os.path import join as path_join
import shutil
import sqlite3
from sqlite3 import connect
import sys
from textwrap import dedent
@@ -70,7 +73,7 @@ def complete_import(text, line):
"""
Tab-complete 'import' commands
"""
commands = ["empire", "metasploit"]
commands = ("empire", "metasploit")
mline = line.partition(" ")[2]
offs = len(mline) - len(text)
return [s[offs:] for s in commands if s.startswith(mline)]
@@ -80,7 +83,7 @@ def complete_export(text, line):
"""
Tab-complete 'creds' commands.
"""
commands = [
commands = (
"creds",
"plaintext",
"hashes",
@@ -88,7 +91,7 @@ def complete_export(text, line):
"local_admins",
"signing",
"keys",
]
)
mline = line.partition(" ")[2]
offs = len(mline) - len(text)
return [s[offs:] for s in commands if s.startswith(mline)]
@@ -111,7 +114,8 @@ class DatabaseNavigator(cmd.Cmd):
self.db.shutdown_db()
sys.exit()
def help_exit(self):
@staticmethod
def help_exit():
help_string = """
Exits
"""
@@ -138,14 +142,14 @@ class DatabaseNavigator(cmd.Cmd):
filename = line[2]
creds = self.db.get_credentials()
csv_header = [
csv_header = (
"id",
"domain",
"username",
"password",
"credtype",
"pillaged_from",
]
)
if line[1].lower() == "simple":
write_csv(filename, csv_header, creds)
@@ -178,7 +182,7 @@ class DatabaseNavigator(cmd.Cmd):
)
return
csv_header_simple = [
csv_header_simple = (
"id",
"ip",
"hostname",
@@ -187,8 +191,8 @@ class DatabaseNavigator(cmd.Cmd):
"dc",
"smbv1",
"signing",
]
csv_header_detailed = [
)
csv_header_detailed = (
"id",
"ip",
"hostname",
@@ -200,7 +204,7 @@ class DatabaseNavigator(cmd.Cmd):
"spooler",
"zerologon",
"petitpotam",
]
)
filename = line[2]
if line[1].lower() == "simple":
@@ -228,7 +232,7 @@ class DatabaseNavigator(cmd.Cmd):
return
shares = self.db.get_shares()
csv_header = ["id", "host", "userid", "name", "remark", "read", "write"]
csv_header = ("id", "host", "userid", "name", "remark", "read", "write")
filename = line[2]
if line[1].lower() == "simple":
@@ -240,7 +244,7 @@ class DatabaseNavigator(cmd.Cmd):
for share in shares:
user = self.db.get_users(share[2])[0]
entry = [
entry = (
share[0], # shareID
self.db.get_hosts(share[1])[0][2], # hosts
f"{user[1]}\{user[2]}", # userID
@@ -248,7 +252,7 @@ class DatabaseNavigator(cmd.Cmd):
share[4], # remark
bool(share[5]), # read
bool(share[6]), # write
]
)
formatted_shares.append(entry)
write_csv(filename, csv_header, formatted_shares)
else:
@@ -265,7 +269,7 @@ class DatabaseNavigator(cmd.Cmd):
# These values don't change between simple and detailed
local_admins = self.db.get_admin_relations()
csv_header = ["id", "userid", "host"]
csv_header = ("id", "userid", "host")
filename = line[2]
if line[1].lower() == "simple":
@@ -275,11 +279,11 @@ class DatabaseNavigator(cmd.Cmd):
for entry in local_admins:
user = self.db.get_users(filter_term=entry[1])[0]
formatted_entry = [
formatted_entry = (
entry[0], # Entry ID
f"{user[1]}/{user[2]}", # DOMAIN/Username
self.db.get_hosts(filter_term=entry[2])[0][2], # Hostname
]
)
# Can't modify a tuple which is what self.db.get_admin_relations() returns
formatted_local_admins.append(formatted_entry)
write_csv(filename, csv_header, formatted_local_admins)
@@ -296,7 +300,7 @@ class DatabaseNavigator(cmd.Cmd):
# These values don't change between simple and detailed
dpapi_secrets = self.db.get_dpapi_secrets()
csv_header = [
csv_header = (
"id",
"host",
"dpapi_type",
@@ -304,7 +308,7 @@ class DatabaseNavigator(cmd.Cmd):
"username",
"password",
"url",
]
)
filename = line[2]
if line[1].lower() == "simple":
@@ -312,7 +316,7 @@ class DatabaseNavigator(cmd.Cmd):
elif line[1].lower() == "detailed":
formatted_dpapi_secret = []
for entry in dpapi_secrets:
formatted_entry = [
formatted_entry = (
entry[0], # Entry ID
self.db.get_hosts(filter_term=entry[1])[0][2], # Hostname
entry[2], # DPAPI type
@@ -320,12 +324,12 @@ class DatabaseNavigator(cmd.Cmd):
entry[4], # Username
entry[5], # Password
entry[6], # URL
]
)
# Can't modify a tuple which is what self.db.get_admin_relations() returns
formatted_dpapi_secret.append(formatted_entry)
write_csv(filename, csv_header, formatted_dpapi_secret)
else:
print("[-] No such export option: %s" % line[1])
print(f"[-] No such export option: {line[1]}")
return
print("[+] DPAPI secrets exported")
elif command == "keys":
@@ -341,7 +345,8 @@ class DatabaseNavigator(cmd.Cmd):
"[-] Invalid argument, specify creds, hosts, local_admins, shares or dpapi"
)
def help_export(self):
@staticmethod
def help_export():
help_string = """
export [creds|hosts|local_admins|shares|signing|keys] [simple|detailed|*] [filename]
Exports information to a specified file
@@ -436,8 +441,8 @@ class CMEDBMenu(cmd.Cmd):
if not proto:
return
proto_db_path = os.path.join(WORKSPACE_DIR, self.workspace, proto + ".db")
if os.path.exists(proto_db_path):
proto_db_path = path_join(WORKSPACE_DIR, self.workspace, f"{proto}.db")
if exists(proto_db_path):
self.conn = create_db_engine(proto_db_path)
db_nav_object = self.p_loader.load_protocol(self.protocols[proto]["nvpath"])
db_object = self.p_loader.load_protocol(self.protocols[proto]["dbpath"])
@@ -451,7 +456,8 @@ class CMEDBMenu(cmd.Cmd):
except UserExitedProto:
pass
def help_proto(self):
@staticmethod
def help_proto():
help_string = """
proto [smb|mssql|winrm]
*unimplemented protocols: ftp, rdp, ldap, ssh
@@ -474,27 +480,30 @@ class CMEDBMenu(cmd.Cmd):
self.do_workspace(new_workspace)
elif subcommand == "list":
print("[*] Enumerating Workspaces")
for workspace in os.listdir(os.path.join(WORKSPACE_DIR)):
for workspace in listdir(path_join(WORKSPACE_DIR)):
if workspace == self.workspace:
print("==> " + workspace)
else:
print(workspace)
elif os.path.exists(os.path.join(WORKSPACE_DIR, line)):
elif exists(path_join(WORKSPACE_DIR, line)):
self.config.set("CME", "workspace", line)
self.write_configfile()
self.workspace = line
self.prompt = f"cmedb ({line}) > "
def help_workspace(self):
@staticmethod
def help_workspace():
help_string = """
workspace [create <targetName> | workspace list | workspace <targetName>]
"""
print_help(help_string)
def do_exit(self, line):
@staticmethod
def do_exit(line):
sys.exit()
def help_exit(self):
@staticmethod
def help_exit():
help_string = """
Exits
"""
@@ -502,18 +511,15 @@ class CMEDBMenu(cmd.Cmd):
def create_workspace(workspace_name, p_loader, protocols):
os.mkdir(os.path.join(WORKSPACE_DIR, workspace_name))
os.mkdir(path_join(WORKSPACE_DIR, workspace_name))
for protocol in protocols.keys():
try:
protocol_object = p_loader.load_protocol(protocols[protocol]["dbpath"])
except KeyError:
continue
proto_db_path = os.path.join(WORKSPACE_DIR, workspace_name, protocol + ".db")
protocol_object = p_loader.load_protocol(protocols[protocol]["dbpath"])
proto_db_path = path_join(WORKSPACE_DIR, workspace_name, f"{protocol}.db")
if not os.path.exists(proto_db_path):
if not exists(proto_db_path):
print(f"[*] Initializing {protocol.upper()} protocol database")
conn = sqlite3.connect(proto_db_path)
conn = connect(proto_db_path)
c = conn.cursor()
# try to prevent some weird sqlite I/O errors
@@ -528,27 +534,23 @@ def create_workspace(workspace_name, p_loader, protocols):
def delete_workspace(workspace_name):
shutil.rmtree(os.path.join(WORKSPACE_DIR, workspace_name))
shutil.rmtree(path_join(WORKSPACE_DIR, workspace_name))
def initialize_db(logger):
if not os.path.exists(os.path.join(WS_PATH, "default")):
if not exists(path_join(WS_PATH, "default")):
logger.debug("Creating default workspace")
os.mkdir(os.path.join(WS_PATH, "default"))
os.mkdir(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"])
except KeyError:
continue
protocol_object = p_loader.load_protocol(protocols[protocol]["dbpath"])
proto_db_path = path_join(WS_PATH, "default", f"{protocol}.db")
proto_db_path = os.path.join(WS_PATH, "default", protocol + ".db")
if not os.path.exists(proto_db_path):
if not exists(proto_db_path):
logger.debug(f"Initializing {protocol.upper()} protocol database")
conn = sqlite3.connect(proto_db_path)
conn = connect(proto_db_path)
c = conn.cursor()
# try to prevent some weird sqlite I/O errors
c.execute(
@@ -564,7 +566,7 @@ def initialize_db(logger):
def main():
if not os.path.exists(CONFIG_PATH):
if not exists(CONFIG_PATH):
print("[-] Unable to find config file")
sys.exit(1)
try:
+12 -11
View File
@@ -3,7 +3,8 @@
import random
import socket
import sys
from socket import AF_INET, AF_INET6, SOCK_DGRAM, IPPROTO_IP, AI_CANONNAME
from socket import getaddrinfo
from os.path import isfile
from threading import BoundedSemaphore
from functools import wraps
@@ -21,23 +22,23 @@ user_failed_logins = {}
def gethost_addrinfo(hostname):
try:
for res in socket.getaddrinfo(
for res in getaddrinfo(
hostname,
None,
socket.AF_INET6,
socket.SOCK_DGRAM,
socket.IPPROTO_IP,
socket.AI_CANONNAME,
AF_INET6,
SOCK_DGRAM,
IPPROTO_IP,
AI_CANONNAME,
):
af, socktype, proto, canonname, sa = res
except socket.gaierror:
for res in socket.getaddrinfo(
for res in getaddrinfo(
hostname,
None,
socket.AF_INET,
socket.SOCK_DGRAM,
socket.IPPROTO_IP,
socket.AI_CANONNAME,
AF_INET,
SOCK_DGRAM,
IPPROTO_IP,
AI_CANONNAME,
):
af, socktype, proto, canonname, sa = res
if canonname == "":
+21 -21
View File
@@ -15,16 +15,17 @@ from cme.paths import CME_PATH, DATA_PATH
from cme.console import cme_console
from cme.logger import cme_logger
from cme.config import cme_config, cme_workspace, config_log, ignore_opsec
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import ThreadPoolExecutor, as_completed
import asyncio
import cme.helpers.powershell as powershell
import shutil
import webbrowser
import random
import os
import sys
from os.path import exists
from os.path import join as path_join
from sys import exit
import logging
import concurrent.futures
import sqlalchemy
from rich.progress import Progress
@@ -34,7 +35,7 @@ except:
print(
"Incompatible python version, try with another python version or another binary 3.8 / 3.9 / 3.10 / 3.11 that match your python version (python -V)"
)
sys.exit(1)
exit(1)
def create_db_engine(db_path):
@@ -64,7 +65,7 @@ async def start_run(protocol_obj, args, db, targets):
executor.submit(protocol_obj, args, db, target)
for target in targets
]
for future in concurrent.futures.as_completed(futures):
for future in as_completed(futures):
current += 1
progress.update(tasks, completed=current)
@@ -84,7 +85,8 @@ def main():
cme_logger.logger.setLevel(logging.ERROR)
root_logger.setLevel(logging.ERROR)
# if these are the same, it might double log to file (two FileHandlers will be added), but this should never happen by accident
# if these are the same, it might double log to file (two FileHandlers will be added)
# but this should never happen by accident
if config_log:
cme_logger.add_file_log()
if hasattr(args, "log") and args.log:
@@ -94,16 +96,14 @@ def main():
if args.darrell:
links = (
open(os.path.join(DATA_PATH, "videos_for_darrell.harambe"))
.read()
.splitlines()
open(path_join(DATA_PATH, "videos_for_darrell.harambe")).read().splitlines()
)
try:
webbrowser.open(random.choice(links))
sys.exit(1)
exit(1)
except Exception as e:
cme_logger.error(f"Error opening le dank meme: {e}")
sys.exit(1)
exit(1)
if args.protocol == "ssh":
if args.key_file:
@@ -111,11 +111,11 @@ def main():
cme_logger.fail(
f"Password is required, even if a key file is used - if no passphrase for key, use `-p ''`"
)
sys.exit(1)
exit(1)
if args.use_kcache and not os.environ.get("KRB5CCNAME"):
cme_logger.error("KRB5CCNAME environment variable is not set")
sys.exit(1)
exit(1)
module_server = None
targets = []
@@ -131,11 +131,11 @@ def main():
args.cred_id.remove(cred_id)
except Exception as e:
cme_logger.error(f"Error parsing database credential id: {e}")
sys.exit(1)
exit(1)
if hasattr(args, "target") and args.target:
for target in args.target:
if os.path.exists(target):
if exists(target):
target_file_type = identify_target_file(target)
if target_file_type == "nmap":
targets.extend(parse_nmap_xml(target, args.protocol))
@@ -169,7 +169,7 @@ def main():
protocol_db_object = getattr(p_loader.load_protocol(protocol_db_path), "database")
cme_logger.debug(f"Protocol DB Object: {protocol_db_object}")
db_path = os.path.join(CME_PATH, "workspaces", cme_workspace, args.protocol + ".db")
db_path = path_join(CME_PATH, "workspaces", cme_workspace, f"{args.protocol}.db")
cme_logger.debug(f"DB Path: {db_path}")
db_engine = create_db_engine(db_path)
@@ -186,13 +186,13 @@ def main():
for name, props in sorted(modules.items()):
if args.protocol in props["supported_protocols"]:
cme_logger.display(f"{name:<25} {props['description']}")
sys.exit(0)
exit(0)
elif args.module and args.show_module_options:
for module in args.module:
cme_logger.display(
f"{module} module options:\n{modules[module]['options']}"
)
sys.exit(0)
exit(0)
elif args.module:
cme_logger.debug(f"Modules to be Loaded: {args.module}, {type(args.module)}")
for m in map(str.lower, args.module):
@@ -219,7 +219,7 @@ def main():
)
)
if ans.lower() not in ["y", "yes", ""]:
sys.exit(1)
exit(1)
if not module.multiple_hosts and len(targets) > 1:
ans = input(
@@ -229,7 +229,7 @@ def main():
)
)
if ans.lower() not in ["y", "yes", ""]:
sys.exit(1)
exit(1)
if hasattr(module, "on_request") or hasattr(module, "has_response"):
if hasattr(module, "required_server"):
@@ -274,7 +274,7 @@ def main():
)
)
if ans.lower() not in ["y", "yes", ""]:
sys.exit(1)
exit(1)
try:
asyncio.run(start_run(protocol_object, args, db, targets))
+16 -13
View File
@@ -2,6 +2,9 @@
# -*- coding: utf-8 -*-
import os
from os import mkdir
from os.path import exists
from os.path import join as path_join
import shutil
import configparser
from configparser import NoSectionError, NoOptionError
@@ -11,32 +14,32 @@ from cme.logger import cme_logger
def first_run_setup(logger=cme_logger):
if not os.path.exists(TMP_PATH):
os.mkdir(TMP_PATH)
if not exists(TMP_PATH):
mkdir(TMP_PATH)
if not os.path.exists(CME_PATH):
if not exists(CME_PATH):
logger.display("First time use detected")
logger.display("Creating home directory structure")
os.mkdir(CME_PATH)
mkdir(CME_PATH)
folders = [
folders = (
"logs",
"modules",
"protocols",
"workspaces",
"obfuscated_scripts",
"screenshots",
]
)
for folder in folders:
if not os.path.exists(os.path.join(CME_PATH, folder)):
if not exists(path_join(CME_PATH, folder)):
logger.display(f"Creating missing folder {folder}")
os.mkdir(os.path.join(CME_PATH, folder))
mkdir(path_join(CME_PATH, folder))
initialize_db(logger)
if not os.path.exists(CONFIG_PATH):
if not exists(CONFIG_PATH):
logger.display("Copying default configuration file")
default_path = os.path.join(DATA_PATH, "cme.conf")
default_path = path_join(DATA_PATH, "cme.conf")
shutil.copy(default_path, CME_PATH)
else:
# This is just a quick check to make sure the config file isn't the old 3.x format
@@ -52,10 +55,10 @@ def first_run_setup(logger=cme_logger):
logger.display(
"Old configuration file detected, replacing with new version"
)
default_path = os.path.join(DATA_PATH, "cme.conf")
default_path = path_join(DATA_PATH, "cme.conf")
shutil.copy(default_path, CME_PATH)
# if not os.path.exists(CERT_PATH):
# if not exists(CERT_PATH):
# logger.display('Generating SSL certificate')
# try:
# check_output(['openssl', 'help'], stderr=PIPE)
@@ -66,7 +69,7 @@ def first_run_setup(logger=cme_logger):
# except OSError as e:
# if e.errno == errno.ENOENT:
# logger.error('OpenSSL command line utility is not installed, could not generate certificate, using default certificate')
# default_path = os.path.join(DATA_PATH, 'default.pem')
# default_path = path_join(DATA_PATH, 'default.pem')
# shutil.copy(default_path, CERT_PATH)
# else:
# logger.error('Error while generating SSL certificate: {}'.format(e))
+7 -5
View File
@@ -1,7 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import importlib
import os
from os import listdir
from os.path import dirname
from os.path import join as path_join
import sys
import cme
@@ -130,14 +132,14 @@ class ModuleLoader:
"""
modules = {}
modules_paths = [
os.path.join(os.path.dirname(cme.__file__), "modules"),
os.path.join(CME_PATH, "modules"),
path_join(dirname(cme.__file__), "modules"),
path_join(CME_PATH, "modules"),
]
for path in modules_paths:
for module in os.listdir(path):
for module in listdir(path):
if module[-3:] == ".py" and module != "example_module.py":
module_path = os.path.join(path, module)
module_path = path_join(path, module)
module_data = self.get_module_info(module_path)
modules.update(module_data)
return modules
+12 -10
View File
@@ -2,13 +2,15 @@
# -*- coding: utf-8 -*-
import types
from importlib.machinery import SourceFileLoader
import os
from os import listdir
from os.path import join as path_join
from os.path import dirname, exists, expanduser
import cme
class ProtocolLoader:
def __init__(self):
self.cme_path = os.path.expanduser("~/.cme")
self.cme_path = expanduser("~/.cme")
def load_protocol(self, protocol_path):
loader = SourceFileLoader("protocol", protocol_path)
@@ -19,23 +21,23 @@ class ProtocolLoader:
def get_protocols(self):
protocols = {}
protocol_paths = [
os.path.join(os.path.dirname(cme.__file__), "protocols"),
os.path.join(self.cme_path, "protocols"),
path_join(dirname(cme.__file__), "protocols"),
path_join(self.cme_path, "protocols"),
]
for path in protocol_paths:
for protocol in os.listdir(path):
for protocol in listdir(path):
if protocol[-3:] == ".py" and protocol[:-3] != "__init__":
protocol_path = os.path.join(path, protocol)
protocol_path = path_join(path, protocol)
protocol_name = protocol[:-3]
protocols[protocol_name] = {"path": protocol_path}
db_file_path = os.path.join(path, protocol_name, "database.py")
db_nav_path = os.path.join(path, protocol_name, "db_navigator.py")
if os.path.exists(db_file_path):
db_file_path = path_join(path, protocol_name, "database.py")
db_nav_path = path_join(path, protocol_name, "db_navigator.py")
if exists(db_file_path):
protocols[protocol_name]["dbpath"] = db_file_path
if os.path.exists(db_nav_path):
if exists(db_nav_path):
protocols[protocol_name]["nvpath"] = db_nav_path
return protocols
+6 -7
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import logging
from logging import LogRecord
from logging.handlers import RotatingFileHandler
import os.path
import sys
@@ -148,10 +149,10 @@ class CMEAdapter(logging.LoggerAdapter):
if self.logger.getEffectiveLevel() >= logging.INFO:
# will be 0 if it's just the console output, so only do this if we actually have file loggers
if len(self.logger.handlers):
for handler in self.logger.handlers:
try:
try:
for handler in self.logger.handlers:
handler.handle(
logging.LogRecord(
LogRecord(
"cme",
20,
"",
@@ -161,10 +162,8 @@ class CMEAdapter(logging.LoggerAdapter):
exc_info=None,
)
)
except Exception as e:
self.logger.fail(
f"Issue while trying to custom print handler: {e}"
)
except Exception as e:
self.logger.fail(f"Issue while trying to custom print handler: {e}")
else:
self.logger.info(text)
+23 -66
View File
@@ -10,22 +10,15 @@ class navigator(DatabaseNavigator):
data = [["CredID", "Admin On", "CredType", "Domain", "UserName", "Password"]]
for cred in creds:
cred_id = cred[0]
credtype = cred[1]
domain = cred[2]
username = cred[3]
password = cred[4]
# pillaged_from = cred[5]
links = self.db.get_admin_relations(user_id=cred_id)
links = self.db.get_admin_relations(user_id=cred[0])
data.append(
[
cred_id,
cred[0], # cred_id
str(len(links)) + " Host(s)",
credtype,
domain,
username,
password,
cred[1], # cred_type
cred[2], # domain
cred[3], # username
cred[4], # password
]
)
print_table(data, title="Credentials")
@@ -33,24 +26,16 @@ class navigator(DatabaseNavigator):
def display_hosts(self, hosts):
data = [["HostID", "Admins", "IP", "Hostname", "Domain", "OS", "DB Instances"]]
for host in hosts:
host_id = host[0]
ip = host[1]
hostname = host[2]
domain = host[3]
os = host[4]
instances = host[5]
links = self.db.get_admin_relations(host_id=host_id)
links = self.db.get_admin_relations(host_id=host[0])
data.append(
[
host_id,
host[0],
str(len(links)) + " Cred(s)",
ip,
hostname,
domain,
os,
instances,
host[1],
host[2],
host[3],
host[4],
host[5],
]
)
print_table(data, title="Hosts")
@@ -71,15 +56,8 @@ class navigator(DatabaseNavigator):
host_id_list = []
for host in hosts:
host_id = host[0]
host_id_list.append(host_id)
ip = host[1]
hostname = host[2]
domain = host[3]
os = host[4]
data.append([host_id, ip, hostname, domain, os])
host_id_list.append(host[0])
data.append([host[0], host[1], host[2], host[3], host[4]])
print_table(data, title="Host(s)")
@@ -92,14 +70,7 @@ class navigator(DatabaseNavigator):
creds = self.db.get_credentials(filter_term=cred_id)
for cred in creds:
cred_id = cred[0]
domain = cred[1]
username = cred[2]
password = cred[3]
credtype = cred[4]
# pillaged_from = cred[5]
data.append([cred_id, credtype, domain, username, password])
data.append([cred[0], cred[4], cred[1], cred[2], cred[3]])
print_table(data, title="Credential(s) with Admin Access")
def do_creds(self, line):
@@ -141,15 +112,8 @@ class navigator(DatabaseNavigator):
cred_id_list = []
for cred in creds:
cred_id = cred[0]
cred_id_list.append(cred_id)
credType = cred[1]
domain = cred[2]
username = cred[3]
password = cred[4]
data.append([cred_id, credType, domain, username, password])
cred_id_list.append(cred[0])
data.append([cred[0], cred[1], cred[2], cred[3], cred[4]])
print_table(data, title="Credential(s)")
@@ -162,13 +126,7 @@ class navigator(DatabaseNavigator):
hosts = self.db.get_hosts(host_id)
for host in hosts:
host_id = host[0]
ip = host[1]
hostname = host[2]
domain = host[3]
os = host[4]
data.append([host_id, ip, hostname, domain, os])
data.append([host[0], host[1], host[2], host[3], host[4]])
print_table(data, title="Admin Access to Host(s)")
def do_clear_database(self, line):
@@ -180,7 +138,8 @@ class navigator(DatabaseNavigator):
):
self.db.clear_database()
def help_clear_database(self):
@staticmethod
def help_clear_database():
help_string = """
clear_database
THIS COMPLETELY DESTROYS ALL DATA IN THE CURRENTLY CONNECTED DATABASE
@@ -192,8 +151,7 @@ class navigator(DatabaseNavigator):
"""
Tab-complete 'creds' commands
"""
commands = ["add", "remove"]
commands = ("add", "remove")
mline = line.partition(" ")[2]
offs = len(mline) - len(text)
return [s[offs:] for s in commands if s.startswith(mline)]
@@ -202,8 +160,7 @@ class navigator(DatabaseNavigator):
"""
Tab-complete 'creds' commands
"""
commands = ["add", "remove", "hash", "plaintext"]
commands = ("add", "remove", "hash", "plaintext")
mline = line.partition(" ")[2]
offs = len(mline) - len(text)
return [s[offs:] for s in commands if s.startswith(mline)]
+31 -82
View File
@@ -20,23 +20,6 @@ from asyauth.common.credentials.kerberos import KerberosCredential
from asyauth.common.constants import asyauthSecret
from asysocks.unicomm.common.target import UniTarget, UniProto
rdp_error_status = {
"0xc0000071": "STATUS_PASSWORD_EXPIRED",
"0xc0000234": "STATUS_ACCOUNT_LOCKED_OUT",
"0xc0000072": "STATUS_ACCOUNT_DISABLED",
"0xc0000193": "STATUS_ACCOUNT_EXPIRED",
"0xc000006E": "STATUS_ACCOUNT_RESTRICTION",
"0xc000006F": "STATUS_INVALID_LOGON_HOURS",
"0xc0000070": "STATUS_INVALID_WORKSTATION",
"0xc000015B": "STATUS_LOGON_TYPE_NOT_GRANTED",
"0xc0000224": "STATUS_PASSWORD_MUST_CHANGE",
"0xc0000022": "STATUS_ACCESS_DENIED",
"0xc000006d": "STATUS_LOGON_FAILURE",
"0xc000006a": "STATUS_WRONG_PASSWORD ",
"KDC_ERR_CLIENT_REVOKED": "KDC_ERR_CLIENT_REVOKED",
"KDC_ERR_PREAUTH_FAILED": "KDC_ERR_PREAUTH_FAILED",
}
class rdp(connection):
def __init__(self, args, db, host):
@@ -77,6 +60,23 @@ class rdp(connection):
self.target = None
self.auth = None
self.rdp_error_status = {
"0xc0000071": "STATUS_PASSWORD_EXPIRED",
"0xc0000234": "STATUS_ACCOUNT_LOCKED_OUT",
"0xc0000072": "STATUS_ACCOUNT_DISABLED",
"0xc0000193": "STATUS_ACCOUNT_EXPIRED",
"0xc000006E": "STATUS_ACCOUNT_RESTRICTION",
"0xc000006F": "STATUS_INVALID_LOGON_HOURS",
"0xc0000070": "STATUS_INVALID_WORKSTATION",
"0xc000015B": "STATUS_LOGON_TYPE_NOT_GRANTED",
"0xc0000224": "STATUS_PASSWORD_MUST_CHANGE",
"0xc0000022": "STATUS_ACCESS_DENIED",
"0xc000006d": "STATUS_LOGON_FAILURE",
"0xc000006a": "STATUS_WRONG_PASSWORD ",
"KDC_ERR_CLIENT_REVOKED": "KDC_ERR_CLIENT_REVOKED",
"KDC_ERR_PREAUTH_FAILED": "KDC_ERR_PREAUTH_FAILED",
}
connection.__init__(self, args, db, host)
@staticmethod
@@ -349,11 +349,7 @@ class rdp(connection):
if not self.config.get("CME", "audit_mode")
else self.config.get("CME", "audit_mode") * 8
),
highlight(
f'({self.config.get("CME", "pwn3d_label")})'
if self.admin_privs
else ""
),
self.mark_pwned(),
)
)
if not self.args.local_auth:
@@ -364,9 +360,9 @@ class rdp(connection):
except Exception as e:
if "KDC_ERR" in str(e):
reason = None
for word in rdp_error_status.keys():
for word in self.rdp_error_status.keys():
if word in str(e):
reason = rdp_error_status[word]
reason = self.rdp_error_status[word]
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"
@@ -378,24 +374,15 @@ class rdp(connection):
)
elif "Authentication failed!" in str(e):
self.logger.success(
"{}\\{}:{} {}".format(
domain,
username,
password,
highlight(
f'({self.config.get("CME", "pwn3d_label")})'
if self.admin_privs
else ""
),
)
f"{domain}\\{username}:{password} {self.mark_pwned()}"
)
elif "No such file" in str(e):
self.logger.fail(e)
else:
reason = None
for word in rdp_error_status.keys():
for word in self.rdp_error_status.keys():
if word in str(e):
reason = rdp_error_status[word]
reason = self.rdp_error_status[word]
if "cannot unpack non-iterable NoneType object" == str(e):
reason = "User valid but cannot connect"
self.logger.fail(
@@ -423,18 +410,7 @@ class rdp(connection):
asyncio.run(self.connect_rdp())
self.admin_privs = True
self.logger.success(
"{}\\{}:{} {}".format(
domain,
username,
password,
highlight(
f'({self.config.get("CME", "pwn3d_label")})'
if self.admin_privs
else ""
),
)
)
self.logger.success(f"{domain}\\{username}:{password} {self.mark_pwned()}")
if not self.args.local_auth:
add_user_bh(username, domain, self.logger, self.config)
if not self.args.continue_on_success:
@@ -442,22 +418,13 @@ class rdp(connection):
except Exception as e:
if "Authentication failed!" in str(e):
self.logger.success(
"{}\\{}:{} {}".format(
domain,
username,
password,
highlight(
f'({self.config.get("CME", "pwn3d_label")})'
if self.admin_privs
else ""
),
)
f"{domain}\\{username}:{password} {self.mark_pwned()}"
)
else:
reason = None
for word in rdp_error_status.keys():
for word in self.rdp_error_status.keys():
if word in str(e):
reason = rdp_error_status[word]
reason = self.rdp_error_status[word]
if "cannot unpack non-iterable NoneType object" == str(e):
reason = "User valid but cannot connect"
self.logger.fail(
@@ -486,16 +453,7 @@ class rdp(connection):
self.admin_privs = True
self.logger.success(
"{}\\{}:{} {}".format(
self.domain,
username,
ntlm_hash,
highlight(
f'({self.config.get("CME", "pwn3d_label")})'
if self.admin_privs
else ""
),
)
f"{self.domain}\\{username}:{ntlm_hash} {self.mark_pwned()}"
)
if not self.args.local_auth:
add_user_bh(username, domain, self.logger, self.config)
@@ -504,22 +462,13 @@ class rdp(connection):
except Exception as e:
if "Authentication failed!" in str(e):
self.logger.success(
"{}\\{}:{} {}".format(
domain,
username,
ntlm_hash,
highlight(
f'({self.config.get("CME", "pwn3d_label")})'
if self.admin_privs
else ""
),
)
f"{domain}\\{username}:{ntlm_hash} {self.mark_pwned()}"
)
else:
reason = None
for word in rdp_error_status.keys():
for word in self.rdp_error_status.keys():
if word in str(e):
reason = rdp_error_status[word]
reason = self.rdp_error_status[word]
if "cannot unpack non-iterable NoneType object" == str(e):
reason = "User valid but cannot connect"
+2 -4
View File
@@ -60,6 +60,7 @@ from datetime import datetime
from functools import wraps
from traceback import format_exc
import logging
from json import loads
smb_share_name = gen_random_string(5).upper()
smb_server = None
@@ -642,11 +643,8 @@ class smb(connection):
self.logger.fail(
"LAPS password is encrypted and currently CrackMapExec doesn't support the decryption..."
)
return False
elif "mslaps-password" in values:
from json import loads
r = loads(values["mslaps-password"])
msMCSAdmPwd = r["p"]
username = r["n"]
@@ -1746,7 +1744,7 @@ class smb(connection):
sids = list()
for i in range(so_far, so_far + sids_to_check):
sids.append(domain_sid + "-%d" % i)
sids.append(f"{domain_sid}-{i:d}")
try:
lsat.hLsarLookupSids(
dce, policy_handle, sids, lsat.LSAP_LOOKUP_LEVEL.LsapLookupWksta
+26 -75
View File
@@ -175,16 +175,11 @@ class navigator(DatabaseNavigator):
data = [["ShareID", "Name", "Remark"], [share_id, name, remark]]
print_table(data, title="Share")
host = self.db.get_hosts(filter_term=host_id)[0]
data = [["HostID", "IP", "Hostname", "Domain", "OS", "DC"]]
data = [
["HostID", "IP", "Hostname", "Domain", "OS", "DC"],
[host[0], host[1], host[2], host[3], host[4], host[5]],
]
host_id = host[0]
ip = host[1]
hostname = host[2]
domain = host[3]
os = host[4]
dc = host[5]
data.append([host_id, ip, hostname, domain, os, dc])
print_table(data, title="Share Location")
if users_r_access:
@@ -194,12 +189,7 @@ class navigator(DatabaseNavigator):
creds = self.db.get_credentials(filter_term=userid)
for cred in creds:
cred_id = cred[0]
domain = cred[1]
username = cred[2]
password = cred[3]
credtype = cred[4]
data.append([cred_id, credtype, domain, username, password])
data.append([cred[0], cred[4], cred[1], cred[2], cred[3]])
print_table(data, title="Users(s) with Read Access")
if users_w_access:
@@ -209,13 +199,7 @@ class navigator(DatabaseNavigator):
creds = self.db.get_credentials(filter_term=userid)
for cred in creds:
cred_id = cred[0]
domain = cred[1]
username = cred[2]
password = cred[3]
credtype = cred[4]
data.append([cred_id, credtype, domain, username, password])
data.append([cred[0], cred[4], cred[1], cred[2], cred[3]])
print_table(data, title="Users(s) with Write Access")
def help_shares(self):
@@ -251,23 +235,15 @@ class navigator(DatabaseNavigator):
]
for group in groups:
group_id = group[0]
domain = group[1]
name = group[2]
rid = group[3]
members = len(self.db.get_group_relations(group_id=group_id))
ad_members = group[4]
last_query_time = group[5]
data.append(
[
group_id,
domain,
name,
rid,
members,
ad_members,
last_query_time,
group[0],
group[1],
group[2],
group[3],
len(self.db.get_group_relations(group_id=group_id)),
group[4],
group[5],
]
)
print_table(data, title="Group")
@@ -290,21 +266,14 @@ class navigator(DatabaseNavigator):
creds = self.db.get_credentials(filter_term=userid)
for cred in creds:
cred_id = cred[0]
domain = cred[1]
username = cred[2]
password = cred[3]
credtype = cred[4]
pillaged_from = cred[5]
data.append(
[
cred_id,
credtype,
pillaged_from,
domain,
username,
password,
cred[0],
cred[4],
cred[5],
cred[1],
cred[2],
cred[3],
]
)
print_table(data, title="Member(s)")
@@ -402,13 +371,8 @@ class navigator(DatabaseNavigator):
creds = self.db.get_credentials(filter_term=cred_id)
for cred in creds:
cred_id = cred[0]
domain = cred[1]
username = cred[2]
password = cred[3]
credtype = cred[4]
# pillaged_from = cred[5]
data.append([cred_id, credtype, domain, username, password])
data.append([cred[0], cred[4], cred[1], cred[2], cred[3]])
print_table(data, title="Credential(s) with Admin Access")
def help_hosts(self):
@@ -632,17 +596,9 @@ class navigator(DatabaseNavigator):
cred_id_list = []
for cred in creds:
cred_id = cred[0]
cred_id_list.append(cred_id)
domain = cred[1]
username = cred[2]
password = cred[3]
credtype = cred[4]
pillaged_from = cred[5]
cred_id_list.append(cred[0])
data.append([cred[0], cred[4], cred[5], cred[1], cred[2], cred[3]])
data.append(
[cred_id, credtype, pillaged_from, domain, username, password]
)
print_table(data, title="Credential(s)")
data = [["GroupID", "Domain", "Name"]]
@@ -670,13 +626,8 @@ class navigator(DatabaseNavigator):
hosts = self.db.get_hosts(host_id)
for host in hosts:
host_id = host[0]
ip = host[1]
hostname = host[2]
domain = host[3]
os = host[4]
data.append([host[0], host[1], host[2], host[3], host[4]])
data.append([host_id, ip, hostname, domain, os])
print_table(data, title="Admin Access to Host(s)")
def help_creds(self):
@@ -720,7 +671,7 @@ class navigator(DatabaseNavigator):
"""
Tab-complete 'hosts' commands.
"""
commands = ["add", "remove", "dc"]
commands = ("add", "remove", "dc")
mline = line.partition(" ")[2]
offs = len(mline) - len(text)
@@ -730,7 +681,7 @@ class navigator(DatabaseNavigator):
"""
Tab-complete 'creds' commands.
"""
commands = ["add", "remove", "hash", "plaintext"]
commands = ("add", "remove", "hash", "plaintext")
mline = line.partition(" ")[2]
offs = len(mline) - len(text)
+12 -14
View File
@@ -31,7 +31,7 @@ class FirefoxTriage:
firefox_generic_path = "Users\\{}\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles"
share = "C$"
false_positive = [
false_positive = (
".",
"..",
"desktop.ini",
@@ -39,7 +39,7 @@ class FirefoxTriage:
"Default",
"Default User",
"All Users",
]
)
def __init__(self, target, logger, conn: DPLootSMBConnection = None):
self.target = target
@@ -127,23 +127,21 @@ class FirefoxTriage:
except Exception as e:
if "STATUS_OBJECT_PATH_NOT_FOUND" in str(e):
continue
print(e)
self.logger.debug(e)
self.logger.exception(e)
return firefox_data
def get_login_data(self, logins_data):
logins = []
json_logins = json.loads(logins_data)
if "logins" not in json_logins:
return logins # No logins key in logins.json file
for row in json_logins["logins"]:
logins.append(
(
self.decode_login_data(row["encryptedUsername"]),
self.decode_login_data(row["encryptedPassword"]),
row["hostname"],
)
return [] # No logins key in logins.json file
logins = [
(
self.decode_login_data(row["encryptedUsername"]),
self.decode_login_data(row["encryptedPassword"]),
row["hostname"],
)
for row in json_logins["logins"]
]
return logins
def get_key(self, key4_data, master_password=b""):
@@ -204,10 +202,10 @@ class FirefoxTriage:
directories = self.conn.listPath(
shareName=self.share, path=ntpath.normpath(users_dir_path)
)
for d in directories:
if d.get_longname() not in self.false_positive and d.is_directory() > 0:
users.append(d.get_longname())
return users
@staticmethod
+2 -2
View File
@@ -28,7 +28,7 @@
#
import logging
import os
from os.path import join as path_join
from time import sleep
from cme.helpers.misc import gen_random_string
@@ -241,7 +241,7 @@ class MMCEXEC:
while True:
try:
with open(
os.path.join("/tmp", "cme_hosted", self.__output), "r"
path_join("/tmp", "cme_hosted", self.__output), "r"
) as output:
self.output_callback(output.read())
break
+5 -8
View File
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
import os
from os.path import join as path_join
from time import sleep
from impacket.dcerpc.v5 import transport, scmr
from cme.helpers.misc import gen_random_string
@@ -120,9 +121,7 @@ class SMBEXEC:
else:
command = self.__shell + data
with open(
os.path.join("/tmp", "cme_hosted", self.__batchFile), "w"
) as batch_file:
with open(path_join("/tmp", "cme_hosted", self.__batchFile), "w") as batch_file:
batch_file.write(command)
self.logger.debug("Hosting batch file with command: " + command)
@@ -168,7 +167,7 @@ class SMBEXEC:
sleep(2)
pass
else:
logger.debug(e)
self.logger.debug(e)
pass
self.__smbconnection.deleteFile(self.__share, self.__output)
@@ -187,9 +186,7 @@ class SMBEXEC:
else:
command = self.__shell + data
with open(
os.path.join("/tmp", "cme_hosted", self.__batchFile), "w"
) as batch_file:
with open(path_join("/tmp", "cme_hosted", self.__batchFile), "w") as batch_file:
batch_file.write(command)
self.logger.debug("Hosting batch file with command: " + command)
@@ -227,7 +224,7 @@ class SMBEXEC:
while True:
try:
with open(
os.path.join("/tmp", "cme_hosted", self.__output), "rb"
path_join("/tmp", "cme_hosted", self.__output), "rb"
) as output:
self.output_callback(output.read())
break
+2 -1
View File
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
import threading
from threading import enumerate
from sys import exit
from impacket import smbserver
@@ -46,7 +47,7 @@ class CMESMBServer(threading.Thread):
def shutdown(self):
# TODO: should fine the proper way
# make sure all the threads are killed
for thread in threading.enumerate():
for thread in enumerate():
if thread.is_alive():
try:
self._stop()