mirror of
https://github.com/Pennyw0rth/NetExec
synced 2026-06-06 16:34:30 +00:00
Merge pull request #32 from mpgn/linting_marshall
Perflint Speed/Memory Efficiencies
This commit is contained in:
+53
-51
@@ -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
@@ -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
@@ -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
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user