From 389591ac5b7e7c9f11d0a353cf4b18d4750d2688 Mon Sep 17 00:00:00 2001 From: robert-todora Date: Mon, 24 Jul 2023 13:56:31 -0400 Subject: [PATCH] Runbook build --- .../Discard/DiscardNonFileShares.toml | 3 +- .../ShareRules/Keep/KeepDollarShares.toml | 2 +- snaffcore/classifier.py | 47 ++++++++++++++++++- snaffcore/go_snaffle.py | 27 +++++++---- snaffcore/smb.py | 6 ++- 5 files changed, 70 insertions(+), 15 deletions(-) diff --git a/snaffcore/DefaultRules/ShareRules/Discard/DiscardNonFileShares.toml b/snaffcore/DefaultRules/ShareRules/Discard/DiscardNonFileShares.toml index 347d395..c3ee505 100644 --- a/snaffcore/DefaultRules/ShareRules/Discard/DiscardNonFileShares.toml +++ b/snaffcore/DefaultRules/ShareRules/Discard/DiscardNonFileShares.toml @@ -6,6 +6,5 @@ Description = "Skips scanning inside shares ending with these words." MatchLocation = "ShareName" WordListType = "EndsWith" MatchLength = 0 -WordList = ["\\\\print\\$", -"\\\\ipc\\$"] +WordList = ["\\\\print\\$", "\\\\ipc\\$", "PRINT\\$", "IPC\\$"] Triage = "Green" \ No newline at end of file diff --git a/snaffcore/DefaultRules/ShareRules/Keep/KeepDollarShares.toml b/snaffcore/DefaultRules/ShareRules/Keep/KeepDollarShares.toml index d0e3e7a..3a072a4 100644 --- a/snaffcore/DefaultRules/ShareRules/Keep/KeepDollarShares.toml +++ b/snaffcore/DefaultRules/ShareRules/Keep/KeepDollarShares.toml @@ -7,5 +7,5 @@ MatchLocation = "ShareName" WordListType = "EndsWith" MatchLength = 0 WordList = ["\\\\C\\$", -"\\\\ADMIN\\$"] +"\\\\ADMIN\\$", "ADMIN\\$", "C\\$"] Triage = "Black" \ No newline at end of file diff --git a/snaffcore/classifier.py b/snaffcore/classifier.py index 52b4d04..adff61e 100644 --- a/snaffcore/classifier.py +++ b/snaffcore/classifier.py @@ -12,7 +12,8 @@ log = logging.getLogger('snafflepy.classifier') # TODO -class Rules: +class Rules: + def __init__(self) -> None: self.classifier_rules = [] self.share_classifiers = [] @@ -53,7 +54,7 @@ class Rules: # TODO -def is_interest(file, rules): +def is_interest_file(file, rules) -> bool: # massive_wordlist = prepare_classifiers() # print(massive_wordlist) # for root, dirs, files in os.walk(snafflepy_path, topdown=False): @@ -66,3 +67,45 @@ def is_interest(file, rules): return True else: return False + +def is_interest_share(share, rules: Rules): + regex_rules = [] + # Tedium City to find match in wordlist. Did not prepare rules beforehand except by putting each MatchLocation in its own list + # so I have to do more work here before I can find the match + + for rule in rules.share_classifiers: + if rule['WordListType'] == "Regex": + regex_rules = rule['WordList'] + for pattern in regex_rules: + if re.search(str(pattern), str(share)) is not None: + log.info(f"{share} matched {rule['RuleName']}:{rule['Description']}") + + elif rule['WordListType'] == "EndsWith": + regex_rules = rule['WordList'] + for pattern in regex_rules: + if re.search(str(pattern + "$"), str(share)) is not None: + if rule['MatchAction'] == 'Snaffle': + log.info(f"{share} matched rule {rule['RuleName']}:{rule['Description']}") + else: + log.debug(f"{rule['MatchAction']} {share} matched rule {rule['RuleName']}:{rule['Description']}") + + elif rule['WordListType'] == "StartsWith": + regex_rules = rule['WordList'] + for pattern in regex_rules: + if re.search(str("^" + pattern), str(share)) is not None: + log.warning(f"{share} matched rule {rule['RuleName']}: {rule['Description']}") + + elif rule['WordListType'] == "Contains": + regex_rules = rule['WordList'] + for pattern in regex_rules: + if re.search(str(pattern), str(share)) is not None: + log.warning(f"{share} matched rule {rule['RuleName']}:{rule['Description']}") + + elif rule['WordListType'] == "Exact": + regex_rules = rule['WordList'] + for pattern in regex_rules: + if re.search(str("^" + pattern + "$"), str(share)) is not None: + print(f"{share} matched {rule['RuleName']}:{rule['Description']}") + + else: + log.warning(f"{rule['RuleName']} has an invalid WordListType - valid values are Regex, EndsWith, StartsWith, Contains, or Exact") diff --git a/snaffcore/go_snaffle.py b/snaffcore/go_snaffle.py index e923a9c..b79a607 100644 --- a/snaffcore/go_snaffle.py +++ b/snaffcore/go_snaffle.py @@ -18,7 +18,7 @@ def begin_snaffle(options): # Prepare classifiers for use in naive_classify() snaff_rules = Rules() - prepped_rules = snaff_rules.prepare_classifiers() + snaff_rules.prepare_classifiers() # for dict_rules in prepped_rules: # for actual_rule in dict_rules['ClassifierRules']: # pprint.pprint(actual_rule['Triage']) @@ -78,15 +78,21 @@ def begin_snaffle(options): options.targets[0], options.username, options.password, options.domain, options.hash) except: log.error(f"Error logging in to SMB on {options.targets[0]}") - - log.warning("[GO LOUD ACTIVATED] Enumerating all shares for all files...") + if options.go_loud: + log.warning("[GO LOUD ACTIVATED] Enumerating all shares for all files...") for target in options.targets: try: smb_client = SMBClient( target, options.username, options.password, options.domain, options.hash) - smb_client.login() + if not smb_client.login(): + log.error(f" Unable to login to{target}") for share in smb_client.shares: try: + if not options.go_loud: + classify_share(share, snaff_rules) + # else: + # log.info(f"Found share: {share}") + files = smb_client.ls(share, "") for file in files: @@ -95,7 +101,7 @@ def begin_snaffle(options): if options.go_loud: log.info(f"{target}: {share}\\{file.get_longname()}") else: - naive_classify(share, file, prepped_rules) + classify_file(share, file, snaff_rules) except FileListError: log.error( @@ -152,7 +158,7 @@ def list_computers(connection: Connection, domain): # connection.search(search_base=dn,search_filter=filter,search_scope=SUBTREE,attributes=ALL_ATTRIBUTES) domain_names = [] - log.debug(connection.entries) + # log.debug(connection.entries) for entry in connection.entries: sep = str(entry).strip().split(':') domain_names.append(sep[6]) @@ -166,12 +172,15 @@ def list_computers(connection: Connection, domain): # TODO -def naive_classify(share, file, rules: Rules): - log.info(f"{share}: {file.get_longname()}") +def classify_file(share, file, rules: Rules): + # log.info(f"{share}: {file.get_longname()}") - if is_interest(file, rules): + if is_interest_file(file, rules): log.info(f"Found interesting file: {share}/{file}") + +def classify_share(share, rules: Rules): + is_interest_share(share, rules) # These functions resolve to public IP Address: ''' diff --git a/snaffcore/smb.py b/snaffcore/smb.py index fce2ad7..a3edacc 100644 --- a/snaffcore/smb.py +++ b/snaffcore/smb.py @@ -42,7 +42,11 @@ class SMBClient: for i in range(len(resp)): sharename = resp[i]['shi1_netname'][:-1] remarkname = resp[i]['shi1_remark'][:-1] - log.info(f'{self.server}: Share: {sharename}, Remark: {remarkname}') + # fullname = resp[i] + # print(fullname) + log.info(f'Found share {sharename} on {self.server}, remark {remarkname}') + # log.info(f'{self.server}: Share: {sharename}') + yield sharename except Exception as e: