diff --git a/donpapi/collectors/Certificates.py b/donpapi/collectors/Certificates.py index 34d5ffc..0d7d328 100644 --- a/donpapi/collectors/Certificates.py +++ b/donpapi/collectors/Certificates.py @@ -28,12 +28,12 @@ def certificate_callback(certificate): filename = f"{cert_username}_{certificate.filename[:16]}.pfx" self.print_and_store(certificate, cert_username, filename) - certificates_triage = CertificatesTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys, per_certificate_callback=certificate_callback,) + certificates_triage = CertificatesTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys, per_certificate_callback=certificate_callback, false_positive=self.false_positive) + certificates_triage.triage_certificates() if self.context.remoteops_allowed: certificates_triage.triage_system_certificates() - - + def print_and_store(self, certificate, cert_username, filename) -> None: absolute_local_filepath = path.join(self.context.target_output_dir, filename) dump_file_to_loot_directories(absolute_local_filepath, certificate.pfx) diff --git a/donpapi/collectors/Chromium.py b/donpapi/collectors/Chromium.py index 556048a..8038505 100644 --- a/donpapi/collectors/Chromium.py +++ b/donpapi/collectors/Chromium.py @@ -45,6 +45,7 @@ def browser_callback(credential): last_access_utc=credential.last_access_utc, ) - browser_triage = BrowserTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys, per_secret_callback=browser_callback) + browser_triage = BrowserTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys, per_secret_callback=browser_callback, false_positive=self.false_positive) + browser_triage.triage_browsers(gather_cookies=True) - dump_looted_files_to_disk(self.context.target_output_dir, browser_triage.looted_files) \ No newline at end of file + dump_looted_files_to_disk(self.context.target_output_dir, browser_triage.looted_files) diff --git a/donpapi/collectors/CredMan.py b/donpapi/collectors/CredMan.py index 762c3f3..60e0080 100644 --- a/donpapi/collectors/CredMan.py +++ b/donpapi/collectors/CredMan.py @@ -21,12 +21,15 @@ def __init__(self, target: Target, conn: DPLootSMBConnection, masterkeys: list, def run(self): self.logger.display(f"Dumping User{' and Machine' if self.context.remoteops_allowed else ''} Credential Manager") + def credman_callback(credential): self.logger.secret(f"[{credential.winuser}] {credential.target} - {credential.username}:{credential.password}", self.tag) self.context.db.add_secret(computer=self.context.host, collector=self.tag, windows_user=credential.winuser, username=credential.username.rstrip("\x00"), password=credential.password.rstrip("\x00"), target=credential.target.rstrip("\x00")) - credentials_triage = CredentialsTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys, per_credential_callback=credman_callback) + + credentials_triage = CredentialsTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys, per_credential_callback=credman_callback, false_positive=self.false_positive) + credentials_triage.triage_credentials() if self.context.remoteops_allowed: credentials_triage.triage_system_credentials() - dump_looted_files_to_disk(self.context.target_output_dir, credentials_triage.looted_files) \ No newline at end of file + dump_looted_files_to_disk(self.context.target_output_dir, credentials_triage.looted_files) diff --git a/donpapi/collectors/MobaXTerm.py b/donpapi/collectors/MobaXTerm.py index d08c4ed..dd8d5fd 100644 --- a/donpapi/collectors/MobaXTerm.py +++ b/donpapi/collectors/MobaXTerm.py @@ -22,6 +22,7 @@ def __init__(self, target: Target, conn: DPLootSMBConnection, masterkeys: list, def run(self): if self.context.remoteops_allowed: self.logger.display("Dumping MobaXterm credentials") + def mobaxterm_callback(credential): if isinstance(credential, MobaXtermCredential): self.logger.secret(f"[Credential] [{credential.winuser}] {credential.name} - {credential.username}:{credential.password.decode('latin-1')}", self.tag) @@ -29,10 +30,12 @@ def mobaxterm_callback(credential): elif isinstance(credential, MobaXtermPassword): self.logger.secret(f"[Password] [{credential.winuser}] {credential.username}:{credential.password.decode('latin-1')}", self.tag) self.context.db.add_secret(computer=self.context.host, collector=self.tag, windows_user=credential.winuser, program=self.tag, username=credential.username, password=credential.password.decode('latin-1')) - mobaxterm_triage = MobaXtermTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys, per_secret_callback=mobaxterm_callback) + + mobaxterm_triage = MobaXtermTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys, per_secret_callback=mobaxterm_callback, false_positive=self.false_positive) + try: mobaxterm_triage.triage_mobaxterm() dump_looted_files_to_disk(self.context.target_output_dir, mobaxterm_triage.looted_files) except Exception as e: if "ERROR_FILE_NOT_FOUND" not in str(e): - self.logger.error(f"Error while dumping mobaxterm: {e}") \ No newline at end of file + self.logger.error(f"Error while dumping mobaxterm: {e}") diff --git a/donpapi/collectors/RDCMan.py b/donpapi/collectors/RDCMan.py index 00f8949..20261d7 100644 --- a/donpapi/collectors/RDCMan.py +++ b/donpapi/collectors/RDCMan.py @@ -21,7 +21,7 @@ def __init__(self, target: Target, conn: DPLootSMBConnection, masterkeys: list, def run(self): self.logger.display("Dumping User's RDCManager") - rdg_triage = RDGTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys) + rdg_triage = RDGTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys, false_positive=self.false_positive) rdcman_files, rdgfiles = rdg_triage.triage_rdcman() for rdcman_file in rdcman_files: if rdcman_file is None: diff --git a/donpapi/collectors/Vaults.py b/donpapi/collectors/Vaults.py index 2a7ca88..0d23f6e 100644 --- a/donpapi/collectors/Vaults.py +++ b/donpapi/collectors/Vaults.py @@ -47,10 +47,10 @@ def vaults_callback(vault): target=vault.resource ) - vaults_triage = VaultsTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys) + vaults_triage = VaultsTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys, per_vault_callback=vaults_callback, false_positive=self.false_positive) + vaults_triage.triage_vaults() if self.context.remoteops_allowed: vaults_triage.triage_system_vaults() dump_looted_files_to_disk(self.context.target_output_dir, vaults_triage.looted_files) - \ No newline at end of file diff --git a/donpapi/collectors/Wifi.py b/donpapi/collectors/Wifi.py index b620090..4741bc1 100644 --- a/donpapi/collectors/Wifi.py +++ b/donpapi/collectors/Wifi.py @@ -24,7 +24,7 @@ def run(self): if self.context.remoteops_allowed: self.logger.display("Dumping Wifi profiles") try: - wifi_triage = WifiTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys) + wifi_triage = WifiTriage(target=self.target, conn=self.conn, masterkeys=self.masterkeys, false_positive=self.false_positive) wifi_creds = wifi_triage.triage_wifi() except Exception as e: self.logger.debug(f"Error while looting wifi: {e}") diff --git a/donpapi/core.py b/donpapi/core.py index 01eb776..ba75357 100644 --- a/donpapi/core.py +++ b/donpapi/core.py @@ -250,6 +250,7 @@ def get_masterkeys(self): passwords=self.plaintexts, nthashes=self.nthashes, dpapiSystem=self.dpapi_systemkey, + false_positive=self.false_positive, ) masterkeys += masterkeys_triage.triage_masterkeys() if self.remoteops_allowed and self.lsa_dump is not None: diff --git a/donpapi/entry.py b/donpapi/entry.py index 1f36707..e1eca8b 100644 --- a/donpapi/entry.py +++ b/donpapi/entry.py @@ -35,6 +35,7 @@ from donpapi.lib.first_run import first_run, init_output_dir from donpapi.lib.utils import create_recover_file, load_recover_file, parse_credentials_files, parse_targets, update_recover_file from donpapi.lib.logger import donpapi_logger, donpapi_console +from donpapi.lib.consts import FALSE_POSITIVES from pkgutil import iter_modules from importlib import import_module @@ -197,6 +198,7 @@ def main(): collect_subparser.add_argument("--keep-collecting", type=int, action="store", metavar="seconds", help="Rerun the attack against all targets after X seconds, X being the value") collect_subparser.add_argument("--threads", default=50, type=int, metavar="Number of threads", help="Number of threads (default: 50)") collect_subparser.add_argument('--no-config', action="store_true", help="Do not load donpapi config file (~/.donpapi/donpapi.conf)") + collect_subparser.add_argument("--false-positive", default=FALSE_POSITIVES, action="extend", nargs="+", help=f"Specify a list of \"false positive\" usernames to avoid collecting them (will extend the existing list: {FALSE_POSITIVES}).") group_authent = collect_subparser.add_argument_group("authentication") @@ -231,19 +233,7 @@ def main(): set_main_logger(donpapi_logger) - - # Stores the list of false positives usernames: - false_positivee = [ - ".", - "..", - "desktop.ini", - "Public", - "Default", - "Default User", - "All Users", - ".NET v4.5", - ".NET v4.5 Classic" - ] + # Stores the maximum filesize max_filesize = 5000000 @@ -251,9 +241,9 @@ def main(): if len(sys.argv)==1: parser.print_help() sys.exit(1) - + options = parser.parse_args() - + # Init Logger if options.v == 1: donpapi_logger.logger.setLevel(logging.INFO) @@ -292,6 +282,9 @@ def main(): options = argparse.Namespace(**options_recovered) current_target_recovered = target_recovered + # Remove duplicates from false positive list. + false_positive = list(set(options.false_positive)) + # Handle account if options.domain is None: options.domain = '' @@ -376,7 +369,7 @@ def main(): nthashes, masterkeys, donpapi_config, - false_positivee, + false_positive, max_filesize, output_dir ) diff --git a/donpapi/lib/consts.py b/donpapi/lib/consts.py new file mode 100644 index 0000000..8ccfb45 --- /dev/null +++ b/donpapi/lib/consts.py @@ -0,0 +1,15 @@ +"""consts.py +File containing constant values that are used in the DonPAPI project. +""" + +FALSE_POSITIVES = [ + ".", + "..", + "desktop.ini", + "Public", + "Default", + "Default User", + "All Users", + ".NET v4.5", + ".NET v4.5 Classic" +]