snafflepy now looks through directories and subdirectories too! (only in go-loud mode)

This commit is contained in:
robert-todora
2023-07-26 15:27:35 -04:00
parent 827dd4b6af
commit d58261151b
4 changed files with 73 additions and 61 deletions
+1 -1
View File
@@ -59,4 +59,4 @@ class RemoteFile():
def __str__(self):
return f'{self.target}\\{self.share}\\{self.name}'
return f'\\\\{self.target}\\{self.share}\\{self.name}'
+20 -57
View File
@@ -1,15 +1,10 @@
import sys
# import socket
# import urllib.parse
# import dns.resolver
from ldap3 import ALL_ATTRIBUTES, Server, Connection, DSA, ALL, SUBTREE
from time import sleep
from .smb import *
from .utilities import *
from .file import *
from .classifier import *
# import pprint
log = logging.getLogger('snafflepy')
@@ -22,23 +17,11 @@ def begin_snaffle(options):
# Automatically get domain from target if not provided
if not options.domain:
log.info("Domain not provided, retrieving automatically.")
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)
c.unbind()
options.domain = get_domain(options.targets[0])
if options.domain == "":
sys.exit(2)
domain_names = []
# TODO: Talk to AD via LDAP to get list of computers with file shares
if options.disable_computer_discovery:
log.info(
"Computer discovery is turned off. Snaffling will only occur on the host(s) specified.")
@@ -47,27 +30,15 @@ def begin_snaffle(options):
login = access_ldap_server(
options.targets[0], options.username, options.password)
domain_names = list_computers(login, options.domain)
# list_computers() returns list so need to individually add entry
for target in domain_names:
log.debug(
f"Found{target}, adding to targets to snaffle...")
sleep(0.5)
try:
# 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)
except Exception as e:
log.debug(f"Exception: {e}")
log.warning(f"Unable to add{target} to targets to snaffle")
continue
# log.debug(f"Targets that will be snaffled: {options.targets}")
# Login via SMB
# log.info("Preparing classifiers...")
# prepare_classifiers()
try:
smb_client = SMBClient(
options.targets[0], options.username, options.password, options.domain, options.hash)
@@ -81,15 +52,12 @@ def begin_snaffle(options):
smb_client = SMBClient(
target, options.username, options.password, options.domain, options.hash)
if not smb_client.login():
log.error(f" Unable to login to{target}")
log.error(f"Unable to login to{target}")
continue
for share in smb_client.shares:
try:
if not options.go_loud:
classify_share(share, snaff_rules)
# else:
# log.info(f"Found share: {share}")
is_interest_share(share, snaff_rules)
files = smb_client.ls(share, "")
for file in files:
@@ -98,27 +66,30 @@ def begin_snaffle(options):
file = RemoteFile(name, share, target, size)
if options.go_loud:
# Dont care about empty files
if size == 0:
continue
try:
file_text = termcolor.colored("[File]", 'green')
file.get(smb_client)
log.info(f"{target}: {share}\\{name}")
except FileRetrievalError:
log.debug(f"Unable to download ({target}\\\\{share}\\{name})")
print(file_text, f"\\\\{target}\\{share}\\{name}")
except FileRetrievalError as e:
dir_path = file.name
# Check if its a directory, and try to list files/more directories here
smb_client.handle_download_error(share, dir_path, e)
if str(e).find("ACCESS_DENIED"):
log.debug(f"Access Denied \\\\{target}\\{share}\\{file}")
continue
else:
if size >= options.max_file_snaffle:
pass
continue
else:
try:
classify_file(file, snaff_rules, smb_client)
is_interest_file(file, snaff_rules, smb_client)
except FileRetrievalError as e:
log.debug(f"{e}")
if str(e).find("ACCESS_DENIED"):
log.debug(f"Access Denied, cannot download \\\\{target}\\{share}\\ {file}")
continue
except FileListError:
log.error(
"Access Denied, cannot list files in %s" % share)
except FileListError as e:
log.error(f"{share}, {e}")
continue
except Exception as e:
@@ -162,7 +133,6 @@ def access_ldap_server(ip, username, password):
def list_computers(connection: Connection, domain):
dn = get_domain_dn(domain)
# filter = "(objectCategory=computer)"
if connection is None:
log.critical("Connection is not established")
sys.exit(2)
@@ -182,12 +152,5 @@ def list_computers(connection: Connection, domain):
log.critical(f"Unable to list computers: {e}")
return None
# TODO
def classify_file(file: RemoteFile, rules: Rules, smb_client: SMBClient):
is_interest_file(file, rules, smb_client)
def classify_share(share, rules: Rules):
is_interest_share(share, rules)
+27 -1
View File
@@ -4,6 +4,7 @@ import logging
import termcolor
from .errors import *
from .file import *
from impacket.nmb import NetBIOSError, NetBIOSTimeout
from impacket.smbconnection import SessionError, SMBConnection
@@ -45,7 +46,7 @@ class SMBClient:
remarkname = resp[i]['shi1_remark'][:-1]
# log.info(f'Found share {sharename} on {self.server}, remark {remarkname}')
share_text = termcolor.colored("[Share]", 'yellow')
share_text = termcolor.colored("[Share]", 'light_yellow')
print(share_text, termcolor.colored(f"{{Green}} \\\\{self.server}\\{sharename} ({remarkname})", 'green', 'on_white'))
# log.info(f'{self.server}: Share: {sharename}')
@@ -164,3 +165,28 @@ class SMBClient:
log.debug(
f'Rebuilding connection to {self.server} after error: {error}')
self.login(refresh=True)
# Handle download errors and recurse into directories
def handle_download_error(self, share, dir_path, err):
while str(err).find("STATUS_FILE_IS_A_DIRECTORY") != -1:
try:
subfiles = self.ls(share, str(dir_path))
for subfile in subfiles:
sub_size = subfile.get_filesize()
sub_name = str(dir_path + "\\" + subfile.get_longname())
subfile = RemoteFile(sub_name, share, self.server, sub_size)
subfile.get(self)
file_text = termcolor.colored("[File]", 'green')
print(file_text, f"\\\\{self.server}\\{share}\\{sub_name}")
self.handle_download_error(share, sub_name, err)
except:
break
finally:
break
+25 -2
View File
@@ -5,10 +5,12 @@ import random
import logging
import ipaddress
from pathlib import Path
from ldap3 import ALL_ATTRIBUTES, Server, Connection, DSA, ALL, SUBTREE
# RT: Stolen from manspider - https://github.com/blacklanternsecurity/MANSPIDER
log = logging.getLogger('snafflerpy.util')
log = logging.getLogger('snafflepy.util')
def str_to_list(s):
@@ -148,4 +150,25 @@ def get_domain_dn(domain):
for i in domain_parts:
base_dn += 'DC=%s,' % i
base_dn = base_dn[:-1]
return base_dn
return base_dn
def get_domain(target):
log.debug("Domain not provided, retrieving automatically.")
s = Server(target, get_info=ALL)
c = Connection(s)
if not c.bind():
log.error("Could not get domain automatically")
return ""
else:
try:
domain = str(s.info.other["ldapServiceName"][0].split("@")[1]).lower()
except Exception as e:
log.error("Could not get domain automatically")
domain = ""
c.unbind()
log.debug(f"Domain:{domain}")
return domain