mirror of
https://github.com/cisagov/snafflepy
synced 2026-09-24 18:22:23 +00:00
@@ -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"
|
||||
@@ -7,5 +7,5 @@ MatchLocation = "ShareName"
|
||||
WordListType = "EndsWith"
|
||||
MatchLength = 0
|
||||
WordList = ["\\\\C\\$",
|
||||
"\\\\ADMIN\\$"]
|
||||
"\\\\ADMIN\\$", "ADMIN\\$", "C\\$"]
|
||||
Triage = "Black"
|
||||
+45
-2
@@ -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")
|
||||
|
||||
+18
-9
@@ -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:
|
||||
|
||||
'''
|
||||
|
||||
+5
-1
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user