mirror of
https://github.com/cisagov/snafflepy
synced 2026-09-24 18:22:23 +00:00
Merge pull request #35 from robert-todora/dev
final commit before transfer
This commit is contained in:
+131
-11
@@ -58,8 +58,7 @@ def is_interest_file(file, smb_client, share, no_download: bool):
|
||||
file_text = termcolor.colored(f"[File]", "green")
|
||||
ssn_regex = str("^\d{{3}}-\d{{2}}-\d{{4}}$")
|
||||
is_interest = False
|
||||
|
||||
|
||||
|
||||
# Non-file shares
|
||||
# if str(share).lower().find("ipc") or str(share).lower().find("print"):
|
||||
# pass
|
||||
@@ -101,19 +100,120 @@ def is_interest_file(file, smb_client, share, no_download: bool):
|
||||
file_data = str(f.read(10000))
|
||||
if re.search(ssn_regex, file_data):
|
||||
file_triage = termcolor.colored(
|
||||
f"{{Red}}\\\\{file.target}\\{share}\\{file.name} <SsnRegexFound>", "light_yellow", "on_white")
|
||||
f"{{Red}}\\\\{file.target}\\{share}\\{file.name} <SsnRegexFound>", "red", "on_white")
|
||||
log.info(f"{file_text} {file_triage}")
|
||||
elif not is_interest:
|
||||
# print(file.name)
|
||||
os.remove(f"./{file.tmp_filename}")
|
||||
else:
|
||||
else:
|
||||
pass
|
||||
except FileRetrievalError as e:
|
||||
os.remove(f"./{file.tmp_filename}")
|
||||
file.handle_download_error(file.name, e, False, False)
|
||||
|
||||
|
||||
def is_interest_share(share, rules: Rules):
|
||||
# TODO
|
||||
# Implementing file name classification with snafflers ruleset here
|
||||
# Going to follow the same steps as is_interest_share
|
||||
# Call this function after every file has been downloaded in order to avoid problems with nested directories
|
||||
# Call it within the for targets loop but after the for share loop stage is over so it can still do multiple targets
|
||||
def classify_file_name(file, rules: Rules):
|
||||
for rule in rules.file_classifiers:
|
||||
pass
|
||||
|
||||
def classify_file_content(file, rules:Rules):
|
||||
for rule in rules.contents_classifiers:
|
||||
pass
|
||||
|
||||
def classify_directory(dir, rules:Rules):
|
||||
for rule in rules.directory_classifiers:
|
||||
regex_rules = []
|
||||
dir_text = termcolor.colored("[Share]", 'light_yellow')
|
||||
default_triage = termcolor.colored(f"{dir}", 'green')
|
||||
if rule['WordListType'] == "Regex":
|
||||
regex_rules = rule['WordList']
|
||||
for pattern in regex_rules:
|
||||
if re.search(str(pattern), str(dir)) is not None:
|
||||
if rule['MatchAction'] == "Snaffle":
|
||||
color = rule['Triage']
|
||||
print(dir_text, termcolor.colored(
|
||||
f"{{{rule['Triage']}}} {dir} <{rule['RuleName']}>:<{rule['Description']}>", str(color).lower(), 'on_white'))
|
||||
return True
|
||||
else:
|
||||
log.debug(
|
||||
f"{dir} matched rule {rule['RuleName']}:{rule['Description']}")
|
||||
if rule['MatchAction'] == "Discard":
|
||||
return False
|
||||
|
||||
elif rule['WordListType'] == "EndsWith":
|
||||
regex_rules = rule['WordList']
|
||||
for pattern in regex_rules:
|
||||
if re.search(str(pattern + "$"), str(dir)) is not None:
|
||||
if rule['MatchAction'] == "Snaffle":
|
||||
color = rule['Triage']
|
||||
print(dir_text, termcolor.colored(
|
||||
f"{{{rule['Triage']}}} {dir} <{rule['RuleName']}>:<{rule['Description']}>", str(color).lower(), 'on_white'))
|
||||
return True
|
||||
|
||||
else:
|
||||
log.debug(
|
||||
f"{dir} matched rule {rule['RuleName']}:{rule['Description']}")
|
||||
if rule['MatchAction'] == "Discard":
|
||||
return False
|
||||
|
||||
elif rule['WordListType'] == "StartsWith":
|
||||
regex_rules = rule['WordList']
|
||||
for pattern in regex_rules:
|
||||
if re.search(str("^" + pattern), str(dir)) is not None:
|
||||
if rule['MatchAction'] == "Snaffle":
|
||||
color = rule['Triage']
|
||||
print(dir_text, termcolor.colored(
|
||||
f"{{{rule['Triage']}}} {dir} <{rule['RuleName']}>:<{rule['Description']}>", str(color).lower(), 'on_white'))
|
||||
return True
|
||||
|
||||
else:
|
||||
log.debug(
|
||||
f"{dir} matched rule {rule['RuleName']}:{rule['Description']}")
|
||||
if rule['MatchAction'] == "Discard":
|
||||
return False
|
||||
|
||||
elif rule['WordListType'] == "Contains":
|
||||
regex_rules = rule['WordList']
|
||||
for pattern in regex_rules:
|
||||
if re.search(str(pattern), str(dir)) is not None:
|
||||
if rule['MatchAction'] == "Snaffle":
|
||||
color = rule['Triage']
|
||||
print(dir_text, termcolor.colored(
|
||||
f"{{{rule['Triage']}}} {dir} <{rule['RuleName']}>:<{rule['Description']}>", str(color).lower(), 'on_white'))
|
||||
return True
|
||||
else:
|
||||
log.debug(
|
||||
f"{rule['MatchAction']} {dir} matched rule {rule['RuleName']}:{rule['Description']}")
|
||||
if rule['MatchAction'] == "Discard":
|
||||
return False
|
||||
|
||||
elif rule['WordListType'] == "Exact":
|
||||
regex_rules = rule['WordList']
|
||||
for pattern in regex_rules:
|
||||
if re.search(str("^" + pattern + "$"), str(dir)) is not None:
|
||||
if rule['MatchAction'] == "Snaffle":
|
||||
color = rule['Triage']
|
||||
print(dir_text, termcolor.colored(
|
||||
f"{{{rule['Triage']}}} {dir} <{rule['RuleName']}>:<{rule['Description']}>", str(color).lower(), 'on_white'))
|
||||
return True
|
||||
else:
|
||||
log.debug(
|
||||
f"{dir} matched rule {rule['RuleName']}:{rule['Description']}")
|
||||
if rule['MatchAction'] == "Discard":
|
||||
return False
|
||||
|
||||
else:
|
||||
log.warning(
|
||||
f"{rule['RuleName']} has an invalid WordListType - valid values are Regex, EndsWith, StartsWith, Contains, or Exact")
|
||||
raise Exception("Invalid WordListType")
|
||||
|
||||
|
||||
def is_interest_share(share, rules: Rules) -> bool:
|
||||
|
||||
# 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
|
||||
@@ -130,9 +230,12 @@ def is_interest_share(share, rules: Rules):
|
||||
color = rule['Triage']
|
||||
print(share_text, termcolor.colored(
|
||||
f"{{{rule['Triage']}}} {share} <{rule['RuleName']}>:<{rule['Description']}>", str(color).lower(), 'on_white'))
|
||||
return True
|
||||
else:
|
||||
log.debug(
|
||||
f"{rule['MatchAction']} {share} matched rule {rule['RuleName']}:{rule['Description']}")
|
||||
f"{share} matched rule {rule['RuleName']}:{rule['Description']}")
|
||||
if rule['MatchAction'] == "Discard":
|
||||
return False
|
||||
|
||||
elif rule['WordListType'] == "EndsWith":
|
||||
regex_rules = rule['WordList']
|
||||
@@ -142,18 +245,29 @@ def is_interest_share(share, rules: Rules):
|
||||
color = rule['Triage']
|
||||
print(share_text, termcolor.colored(
|
||||
f"{{{rule['Triage']}}} {share} <{rule['RuleName']}>:<{rule['Description']}>", str(color).lower(), 'on_white'))
|
||||
return True
|
||||
|
||||
else:
|
||||
log.debug(
|
||||
f"{rule['MatchAction']} {share} matched rule {rule['RuleName']}:{rule['Description']}")
|
||||
f"{share} matched rule {rule['RuleName']}:{rule['Description']}")
|
||||
if rule['MatchAction'] == "Discard":
|
||||
return False
|
||||
|
||||
elif rule['WordListType'] == "StartsWith":
|
||||
regex_rules = rule['WordList']
|
||||
for pattern in regex_rules:
|
||||
if re.search(str("^" + pattern), str(share)) is not None:
|
||||
color = rule['Triage']
|
||||
print(share_text, termcolor.colored(
|
||||
f"{{{rule['Triage']}}} {share} <{rule['RuleName']}>:<{rule['Description']}>", str(color).lower(), 'on_white'))
|
||||
if rule['MatchAction'] == "Snaffle":
|
||||
color = rule['Triage']
|
||||
print(share_text, termcolor.colored(
|
||||
f"{{{rule['Triage']}}} {share} <{rule['RuleName']}>:<{rule['Description']}>", str(color).lower(), 'on_white'))
|
||||
return True
|
||||
|
||||
else:
|
||||
log.debug(
|
||||
f"{share} matched rule {rule['RuleName']}:{rule['Description']}")
|
||||
if rule['MatchAction'] == "Discard":
|
||||
return False
|
||||
|
||||
elif rule['WordListType'] == "Contains":
|
||||
regex_rules = rule['WordList']
|
||||
@@ -163,9 +277,12 @@ def is_interest_share(share, rules: Rules):
|
||||
color = rule['Triage']
|
||||
print(share_text, termcolor.colored(
|
||||
f"{{{rule['Triage']}}} {share} <{rule['RuleName']}>:<{rule['Description']}>", str(color).lower(), 'on_white'))
|
||||
return True
|
||||
else:
|
||||
log.debug(
|
||||
f"{rule['MatchAction']} {share} matched rule {rule['RuleName']}:{rule['Description']}")
|
||||
if rule['MatchAction'] == "Discard":
|
||||
return False
|
||||
|
||||
elif rule['WordListType'] == "Exact":
|
||||
regex_rules = rule['WordList']
|
||||
@@ -175,9 +292,12 @@ def is_interest_share(share, rules: Rules):
|
||||
color = rule['Triage']
|
||||
print(share_text, termcolor.colored(
|
||||
f"{{{rule['Triage']}}} {share} <{rule['RuleName']}>:<{rule['Description']}>", str(color).lower(), 'on_white'))
|
||||
return True
|
||||
else:
|
||||
log.debug(
|
||||
f"{rule['MatchAction']} {share} matched rule {rule['RuleName']}:{rule['Description']}")
|
||||
f"{share} matched rule {rule['RuleName']}:{rule['Description']}")
|
||||
if rule['MatchAction'] == "Discard":
|
||||
return False
|
||||
|
||||
else:
|
||||
log.warning(
|
||||
|
||||
@@ -65,7 +65,6 @@ class RemoteFile():
|
||||
|
||||
def handle_download_error(self, dir_path, err, is_from_go_loud: bool, add_err: bool):
|
||||
# subfiles = []
|
||||
|
||||
if str(err).find("DIRECTORY"):
|
||||
dir_text = termcolor.colored("[Directory]", 'light_blue')
|
||||
|
||||
|
||||
+15
-3
@@ -57,10 +57,15 @@ def begin_snaffle(options):
|
||||
continue
|
||||
|
||||
for share in smb_client.shares:
|
||||
files = []
|
||||
try:
|
||||
if not options.go_loud:
|
||||
is_interest_share(share, snaff_rules)
|
||||
if is_interest_share(share, snaff_rules) == False:
|
||||
log.debug(f"{share} matched a Discard rule, skipping files inside of this share...")
|
||||
continue
|
||||
|
||||
files = smb_client.ls(share, "")
|
||||
|
||||
|
||||
|
||||
for file in files:
|
||||
@@ -78,9 +83,11 @@ def begin_snaffle(options):
|
||||
f"{file_text} \\\\{target}\\{share}\\{name}")
|
||||
|
||||
except FileRetrievalError as e:
|
||||
no_add_error = False
|
||||
keep_dir_name = True
|
||||
# Check if its a directory, and try to list files/more directories here
|
||||
file.handle_download_error(
|
||||
file.name, e, True, False)
|
||||
file.name, e, options.go_loud, no_add_error)
|
||||
|
||||
else:
|
||||
if size >= options.max_file_snaffle:
|
||||
@@ -89,11 +96,16 @@ def begin_snaffle(options):
|
||||
try:
|
||||
is_interest_file(file, smb_client, share, options.no_download)
|
||||
except FileRetrievalError as e:
|
||||
# Error will trigger if access denied to file, or the file is actually a directory
|
||||
# File
|
||||
# keep_dir_name = classify_directory(file.name, snaff_rules)
|
||||
no_add_error = False
|
||||
file.handle_download_error(
|
||||
file.name, e, False, False)
|
||||
file.name, e, options.go_loud, no_add_error)
|
||||
|
||||
except FileListError as e:
|
||||
log.error(f"Cannot list files at {share} {e}")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import argparse
|
||||
import sys
|
||||
import logging
|
||||
import termcolor
|
||||
import os
|
||||
|
||||
from snaffcore.go_snaffle import *
|
||||
from snaffcore.utilities import *
|
||||
@@ -93,7 +93,7 @@ def main():
|
||||
print("\nI snaffled 'til the snafflin was done")
|
||||
print("View log file at ~/.snafflepy/logs/")
|
||||
print("Files snaffled from targets are available in <PATH-TO-SNAFFLEPY>/remotefiles/")
|
||||
sys.exit(0)
|
||||
sys.exit()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
Reference in New Issue
Block a user