diff --git a/README.md b/README.md index 98db14b..9067b8d 100644 --- a/README.md +++ b/README.md @@ -13,16 +13,24 @@ Tested with Python 3.10.6 ## Usage -`usage: snaffler.py [-h] [-u username] [-p password] [-d domain] [-v] [-i] [-n] targets [targets ...] ` +`usage: snaffler.py [-h] [-u USERNAME] [-p PASSWORD] [-d DOMAIN] [-H HASH] [-v] [--go-loud] [-i] [-n] targets [targets ...]` ## Options -1. targets IPs, hostnames, CIDR ranges, or files contains targets to snaffle - -2. -h, --help show help message and exit -3. -u username, --username username username for LDAP login and SMB -4. -p password, --password password password for LDAP login and SMB -5. -d domain, --domain domain Domain to authenticate to -6. -H hash, --hash hash NT hash for authentication -7. -v, --verbose Show debugging information -8. -i, --no-discovery Disables computer and share discovery (more stealthy, maybe) -9. -n, --disable-computer-discovery Disable computer discovery, requires a single host or list of hosts to do discovery on +~~~ +options: + -h, --help show this help message and exit + -u USERNAME, --username USERNAME + domain username + -p PASSWORD, --password PASSWORD + password for domain user + -d DOMAIN, --domain DOMAIN + FQDN domain to authenticate to, if this option is not provided, SnafflePy will attempt to automatically discover the domain for you + -H HASH, --hash HASH NT hash for authentication + -v, --verbose Show more info + --go-loud Don't try to find anything interesting, literally just go through every computer and every share and print out as many files as possible. Use at your own + risk + -i, --no-share-discovery + Disables share discovery (more stealthy) + -n, --disable-computer-discovery + Disable computer discovery, requires a list of hosts to do discovery on +~~~ \ No newline at end of file diff --git a/snaffcore/classifier.py b/snaffcore/classifier.py index 9cb3d93..6e1b085 100644 --- a/snaffcore/classifier.py +++ b/snaffcore/classifier.py @@ -1,9 +1,17 @@ import re import toml -from impacket.smbconnection import SessionError, SMBConnection import os +import logging +import pprint + +from impacket.smbconnection import SessionError, SMBConnection + + +log = logging.getLogger('snafflepy.classifier') + +# TODO + -# TODO class Rules: def __init__(self) -> None: self.classifier_rules = [] @@ -13,7 +21,6 @@ class Rules: self.contents_classifiers = [] self.postmatch_classifiers = [] - def prepare_classifiers(self): share_path = "./snaffcore/DefaultRules/" @@ -21,11 +28,31 @@ class Rules: for name in files: # print(os.path.join(root,name)) with open(os.path.join(root, name), 'r') as tfile: - yield toml.load(tfile) + toml_loaded = toml.load(tfile) + for dict_rule in toml_loaded['ClassifierRules']: + if dict_rule['EnumerationScope'] == "ShareEnumeration": + self.share_classifiers.append(dict_rule) + elif dict_rule['EnumerationScope'] == "FileEnumeration": + self.file_classifiers.append(dict_rule) + elif dict_rule['EnumerationScope'] == "DirectoryEnumeration": + self.directory_classifiers.append(dict_rule) + elif dict_rule['EnumerationScope'] == "PostMatch": + self.postmatch_classifiers.append(dict_rule) + elif dict_rule['EnumerationScope'] == "ContentsEnumeration": + self.contents_classifiers.append(dict_rule) + else: + log.warning( + f"{dict_rule['RuleName']} is invalid, please check your syntax!") + # pprint.pprint(self.share_classifiers) + # pprint.pprint(self.directory_classifiers) + # pprint.pprint(self.file_classifiers) + # pprint.pprint(self.contents_classifiers) + # pprint.pprint(self.postmatch_classifiers) # TODO + def is_interest(file): # massive_wordlist = prepare_classifiers() # print(massive_wordlist) @@ -33,10 +60,9 @@ def is_interest(file): # for name in files: # with open(os.path.join(root, name), 'rb') as tfile: # print(toml.loads(tfile)) - interest_names = ["Creds.txt"] if file.get_shortname() in interest_names: return True else: - return False \ No newline at end of file + return False diff --git a/snaffcore/go_snaffle.py b/snaffcore/go_snaffle.py index 5fcc96f..c40805e 100644 --- a/snaffcore/go_snaffle.py +++ b/snaffcore/go_snaffle.py @@ -18,28 +18,28 @@ def begin_snaffle(options): # Prepare classifiers for use in naive_classify() snaff_rules = Rules() - # prepped_rules = snaff_rules.prepare_classifiers() + prepped_rules = snaff_rules.prepare_classifiers() # for dict_rules in prepped_rules: # for actual_rule in dict_rules['ClassifierRules']: # pprint.pprint(actual_rule['Triage']) - print("Beginning the snaffle...") sleep(0.2) - if(not options.domain): + if not options.domain: log.info("Domain not provided, retrieving automatically.") - s = Server(options.targets[0], get_info = ALL) + s = Server(options.targets[0], get_info=ALL) c = Connection(s) - if(not c.bind()): - log.error("Could not get domain automatically") - sys.exit(1) - else: - try: - options.domain = str(s.info.other["ldapServiceName"][0].split("@")[1]).lower() - except Exception as e: - log.error("Could not get domain automatically") - sys.exit(1) + if not c.bind(): + log.error("Could not get domain automatically") + sys.exit(1) + else: + try: + options.domain = str( + s.info.other["ldapServiceName"][0].split("@")[1]).lower() + except Exception as e: + log.error("Could not get domain automatically") + sys.exit(1) c.unbind() domain_names = [] @@ -58,7 +58,7 @@ def begin_snaffle(options): f"Found{target}, adding to targets to snaffle...") sleep(0.5) try: - # TODO: Try to fix this? - How to resolve local IP address from Hostname + # TODO: Try to fix this? - How to resolve internal IP address from Hostname # Supposedly SMBConnection should be able to take a hostname but not working as intended on the HTB enviroment I am using for testing # ip = resolve(options.domain, target) options.targets.append(target) @@ -79,8 +79,8 @@ def begin_snaffle(options): except: log.error(f"Error logging in to SMB on {options.targets[0]}") - else: - log.info("Enumerating shares for 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( @@ -89,12 +89,12 @@ def begin_snaffle(options): for share in smb_client.shares: try: files = smb_client.ls(share, "") - + for file in files: # filelist.append(file) # Ask do they want file sizes? - # log.info(f"{target} Found file in {share}: {file.get_longname()}") - naive_classify(share, file, prepped_rules) + log.info(f"{target} Found file in {share}: {file.get_longname()}") + # naive_classify(share, file, prepped_rules) # log.info(f"{target} Found file in {share}: {file}") except FileListError: log.error( @@ -103,9 +103,8 @@ def begin_snaffle(options): except Exception as e: log.error(f"Error creating SMBClient object, {e}") - - - + else: + pass def access_ldap_server(ip, username, password): log.info("Accessing LDAP Server") @@ -166,9 +165,10 @@ def list_computers(connection: Connection, domain): return None # TODO -def naive_classify(share, file, rules:Rules): + + +def naive_classify(share, file, rules: Rules): log.info(f"{share}: {file.get_longname()}") - if is_interest(file, rules): log.info(f"Found interesting file: {share}/{file}") diff --git a/snaffler.py b/snaffler.py index 41a32c7..e6307bf 100644 --- a/snaffler.py +++ b/snaffler.py @@ -19,16 +19,18 @@ def parse_arguments(): add_help=True, prog='snaffler.py', description='A "port" of Snaffler in python') parser.add_argument("targets", nargs='+', type=make_targets, help="IPs, hostnames, CIDR ranges, or files contains targets to snaffle. If you are providing more than one target, the -n option must be used.") - parser.add_argument("-u", "--username", metavar='username', + parser.add_argument("-u", "--username", type=str, help="domain username") - parser.add_argument("-p", "--password", metavar='password', + parser.add_argument("-p", "--password", type=str, help="password for domain user") - parser.add_argument("-d", "--domain", metavar='domain', - default="", help="FQDN domain to authenticate to") - parser.add_argument("-H", "--hash", metavar='hash', + parser.add_argument("-d", "--domain", + default="", help="FQDN domain to authenticate to, if this option is not provided, SnafflePy will attempt to automatically discover the domain for you") + parser.add_argument("-H", "--hash", default="", help="NT hash for authentication") parser.add_argument("-v", "--verbose", action='store_true', help="Show more info") + parser.add_argument("--go-loud", action='store_true', + help="Don't try to find anything interesting, literally just go through every computer and every share and print out as many files as possible. Use at your own risk") # parser.add_argument("-e", "--exclude", ) # TODO