man-spider 1.1.2__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- man_spider/lib/errors.py +8 -4
- man_spider/lib/file.py +13 -16
- man_spider/lib/logger.py +27 -32
- man_spider/lib/parser/__init__.py +1 -1
- man_spider/lib/parser/parser.py +102 -55
- man_spider/lib/processpool.py +24 -31
- man_spider/lib/smb.py +69 -62
- man_spider/lib/spider.py +66 -66
- man_spider/lib/spiderling.py +182 -136
- man_spider/lib/util.py +95 -29
- man_spider/manspider.py +168 -54
- {man_spider-1.1.2.dist-info → man_spider-2.0.0.dist-info}/METADATA +100 -42
- man_spider-2.0.0.dist-info/RECORD +18 -0
- {man_spider-1.1.2.dist-info → man_spider-2.0.0.dist-info}/WHEEL +1 -1
- man_spider-2.0.0.dist-info/entry_points.txt +2 -0
- man_spider-1.1.2.dist-info/RECORD +0 -18
- man_spider-1.1.2.dist-info/entry_points.txt +0 -3
- {man_spider-1.1.2.dist-info → man_spider-2.0.0.dist-info/licenses}/LICENSE +0 -0
man_spider/lib/smb.py
CHANGED
|
@@ -7,17 +7,20 @@ from impacket.smbconnection import SessionError, SMBConnection
|
|
|
7
7
|
from man_spider.lib.errors import *
|
|
8
8
|
|
|
9
9
|
# set up logging
|
|
10
|
-
log = logging.getLogger(
|
|
10
|
+
log = logging.getLogger("manspider.smb")
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class SMBClient:
|
|
14
|
-
|
|
14
|
+
"""
|
|
15
15
|
Wrapper around impacket's SMBConnection() object
|
|
16
|
-
|
|
16
|
+
"""
|
|
17
17
|
|
|
18
|
-
def __init__(
|
|
18
|
+
def __init__(
|
|
19
|
+
self, server, username, password, domain, nthash, use_kerberos=False, aes_key="", dc_ip=None, port=445
|
|
20
|
+
):
|
|
19
21
|
|
|
20
22
|
self.server = server
|
|
23
|
+
self.port = port
|
|
21
24
|
|
|
22
25
|
self.conn = None
|
|
23
26
|
|
|
@@ -31,22 +34,33 @@ class SMBClient:
|
|
|
31
34
|
self.hostname = None
|
|
32
35
|
self.dns_domain = None
|
|
33
36
|
if self.nthash:
|
|
34
|
-
self.lmhash =
|
|
37
|
+
self.lmhash = "aad3b435b51404eeaad3b435b51404ee"
|
|
35
38
|
else:
|
|
36
|
-
self.lmhash =
|
|
39
|
+
self.lmhash = ""
|
|
37
40
|
self._shares = None
|
|
38
41
|
|
|
39
|
-
|
|
40
42
|
def list_shares(self):
|
|
41
|
-
|
|
43
|
+
"""
|
|
42
44
|
List shares on the SMB server
|
|
43
|
-
|
|
45
|
+
"""
|
|
44
46
|
resp = self.conn.listShares()
|
|
47
|
+
log.debug(f"{self.server}: Response length: {len(resp)}")
|
|
45
48
|
for i in range(len(resp)):
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
try:
|
|
50
|
+
sharename = resp[i]["shi1_netname"].rstrip("\x00")
|
|
51
|
+
try:
|
|
52
|
+
share_type = resp[i]["shi1_type"]
|
|
53
|
+
except (KeyError, AttributeError):
|
|
54
|
+
share_type = "unknown"
|
|
55
|
+
try:
|
|
56
|
+
share_comment = resp[i]["shi1_remark"].rstrip("\x00")
|
|
57
|
+
except (KeyError, AttributeError):
|
|
58
|
+
share_comment = ""
|
|
59
|
+
log.debug(f"{self.server}: Share {i}: name='{sharename}', type={share_type}, comment='{share_comment}'")
|
|
60
|
+
yield sharename
|
|
61
|
+
except Exception as e:
|
|
62
|
+
log.debug(f"{self.server}: Error processing share {i}: {e}")
|
|
63
|
+
continue
|
|
50
64
|
|
|
51
65
|
@property
|
|
52
66
|
def shares(self):
|
|
@@ -55,27 +69,26 @@ class SMBClient:
|
|
|
55
69
|
self._shares = list(self.list_shares())
|
|
56
70
|
except Exception as e:
|
|
57
71
|
e = self.handle_impacket_error(e)
|
|
58
|
-
log.debug(f
|
|
72
|
+
log.debug(f"{self.server}: Error listing shares: {e}, retrying...")
|
|
59
73
|
self.rebuild(e)
|
|
60
74
|
try:
|
|
61
75
|
self._shares = list(self.list_shares())
|
|
62
76
|
except Exception as e:
|
|
63
77
|
e = self.handle_impacket_error(e)
|
|
64
|
-
log.warning(f
|
|
78
|
+
log.warning(f"{self.server}: Error listing shares: {e}")
|
|
65
79
|
self.rebuild(e)
|
|
66
80
|
return self._shares or []
|
|
67
81
|
|
|
68
|
-
|
|
69
82
|
def get_hostname(self):
|
|
70
|
-
|
|
83
|
+
"""
|
|
71
84
|
Get the hostname from the SMB connection
|
|
72
|
-
|
|
85
|
+
"""
|
|
73
86
|
try:
|
|
74
87
|
conn = SMBConnection(
|
|
75
88
|
self.server,
|
|
76
89
|
self.server,
|
|
77
90
|
None,
|
|
78
|
-
|
|
91
|
+
self.port,
|
|
79
92
|
timeout=10,
|
|
80
93
|
)
|
|
81
94
|
with suppress(Exception):
|
|
@@ -86,36 +99,36 @@ class SMBClient:
|
|
|
86
99
|
# Get the server name from SMB
|
|
87
100
|
self.hostname = str(conn.getServerName()).strip().replace("\x00", "").lower()
|
|
88
101
|
if self.hostname:
|
|
89
|
-
log.debug(f
|
|
102
|
+
log.debug(f"{self.server}: Got hostname: {self.hostname}")
|
|
90
103
|
else:
|
|
91
|
-
log.debug(f
|
|
104
|
+
log.debug(f"{self.server}: No hostname found")
|
|
92
105
|
except Exception as e:
|
|
93
|
-
log.debug(f
|
|
106
|
+
log.debug(f"{self.server}: Error getting hostname from SMB: {e}")
|
|
94
107
|
self.hostname = ""
|
|
95
108
|
|
|
96
109
|
if self.dns_domain is None:
|
|
97
110
|
try:
|
|
98
111
|
self.dns_domain = str(conn.getServerDNSDomainName()).strip().replace("\x00", "").lower()
|
|
99
112
|
if self.dns_domain:
|
|
100
|
-
log.debug(f
|
|
113
|
+
log.debug(f"{self.server}: Got DNS domain: {self.dns_domain}")
|
|
101
114
|
else:
|
|
102
|
-
log.debug(f
|
|
115
|
+
log.debug(f"{self.server}: No DNS domain found")
|
|
103
116
|
except Exception as e:
|
|
104
|
-
log.debug(f
|
|
105
|
-
self.dns_domain =
|
|
117
|
+
log.debug(f"{self.server}: Error getting DNS domain: {e}")
|
|
118
|
+
self.dns_domain = self.domain if self.domain else ""
|
|
106
119
|
|
|
107
120
|
except Exception as e:
|
|
108
|
-
log.debug(f
|
|
121
|
+
log.debug(f"{self.server}: Error getting hostname: {e}")
|
|
109
122
|
|
|
110
123
|
return self.hostname, self.domain
|
|
111
124
|
|
|
112
125
|
def login(self, refresh=False, first_try=True):
|
|
113
|
-
|
|
126
|
+
"""
|
|
114
127
|
Create a new SMBConnection object (if there isn't one already or if refresh is True)
|
|
115
128
|
Attempt to log in, and switch to null session if logon fails
|
|
116
129
|
Return True if logon succeeded
|
|
117
130
|
Return False if logon failed
|
|
118
|
-
|
|
131
|
+
"""
|
|
119
132
|
|
|
120
133
|
target_server = self.server
|
|
121
134
|
if self.use_kerberos:
|
|
@@ -127,20 +140,19 @@ class SMBClient:
|
|
|
127
140
|
|
|
128
141
|
if self.conn is None or refresh:
|
|
129
142
|
try:
|
|
130
|
-
self.conn = SMBConnection(target_server, target_server, sess_port=
|
|
143
|
+
self.conn = SMBConnection(target_server, target_server, sess_port=self.port, timeout=20)
|
|
131
144
|
except Exception as e:
|
|
132
145
|
log.debug(impacket_error(e))
|
|
133
146
|
return None
|
|
134
147
|
|
|
135
148
|
try:
|
|
136
|
-
|
|
137
|
-
if self.username in [None, '', 'Guest'] and first_try:
|
|
149
|
+
if self.username in [None, "", "Guest"] and first_try:
|
|
138
150
|
# skip to guest / null session
|
|
139
151
|
assert False
|
|
140
152
|
|
|
141
153
|
user_str = self.username
|
|
142
154
|
if self.domain:
|
|
143
|
-
user_str = f
|
|
155
|
+
user_str = f"{self.domain}\\{self.username}"
|
|
144
156
|
log.debug(f'{target_server} ({self.server}): Authenticating as "{user_str}"')
|
|
145
157
|
|
|
146
158
|
if self.use_kerberos:
|
|
@@ -157,7 +169,7 @@ class SMBClient:
|
|
|
157
169
|
elif self.nthash and not self.password:
|
|
158
170
|
self.conn.login(
|
|
159
171
|
self.username,
|
|
160
|
-
|
|
172
|
+
"",
|
|
161
173
|
lmhash=self.lmhash,
|
|
162
174
|
nthash=self.nthash,
|
|
163
175
|
domain=self.domain,
|
|
@@ -174,28 +186,26 @@ class SMBClient:
|
|
|
174
186
|
return True
|
|
175
187
|
|
|
176
188
|
except Exception as e:
|
|
177
|
-
|
|
178
189
|
if type(e) != AssertionError:
|
|
179
190
|
e = self.handle_impacket_error(e, display=True)
|
|
180
191
|
|
|
181
192
|
# try guest account, then null session if logon failed
|
|
182
193
|
if first_try:
|
|
183
|
-
|
|
184
|
-
bad_statuses = ['LOGON_FAIL', 'PASSWORD_EXPIRED', 'LOCKED_OUT', 'SESSION_DELETED']
|
|
194
|
+
bad_statuses = ["LOGON_FAIL", "PASSWORD_EXPIRED", "LOCKED_OUT", "SESSION_DELETED"]
|
|
185
195
|
if any([s in str(e) for s in bad_statuses]):
|
|
186
196
|
for s in bad_statuses:
|
|
187
197
|
if s in str(e):
|
|
188
|
-
log.warning(f
|
|
198
|
+
log.warning(f"{self.server}: {s}: {self.username}")
|
|
189
199
|
|
|
190
|
-
log.debug(f
|
|
191
|
-
self.username =
|
|
192
|
-
self.password =
|
|
193
|
-
self.domain =
|
|
194
|
-
self.nthash =
|
|
200
|
+
log.debug(f"{self.server}: Trying guest session")
|
|
201
|
+
self.username = "Guest"
|
|
202
|
+
self.password = ""
|
|
203
|
+
self.domain = ""
|
|
204
|
+
self.nthash = ""
|
|
195
205
|
guest_success = self.login(refresh=True, first_try=False)
|
|
196
206
|
if not guest_success:
|
|
197
|
-
log.debug(f
|
|
198
|
-
self.username =
|
|
207
|
+
log.debug(f"{self.server}: Switching to null session")
|
|
208
|
+
self.username = ""
|
|
199
209
|
self.login(refresh=True, first_try=False)
|
|
200
210
|
|
|
201
211
|
return False
|
|
@@ -203,54 +213,51 @@ class SMBClient:
|
|
|
203
213
|
else:
|
|
204
214
|
return True
|
|
205
215
|
|
|
206
|
-
|
|
207
216
|
def ls(self, share, path):
|
|
208
|
-
|
|
217
|
+
"""
|
|
209
218
|
List files in share/path
|
|
210
219
|
Raise FileListError if there's a problem
|
|
211
220
|
@byt3bl33d3r it's really not that bad
|
|
212
|
-
|
|
221
|
+
"""
|
|
213
222
|
|
|
214
|
-
nt_path = ntpath.normpath(f
|
|
223
|
+
nt_path = ntpath.normpath(f"{path}\\*")
|
|
215
224
|
|
|
216
225
|
# for every file/dir in "path"
|
|
217
226
|
try:
|
|
218
227
|
for f in self.conn.listPath(share, nt_path):
|
|
219
228
|
# exclude current and parent directory
|
|
220
|
-
if f.get_longname() not in [
|
|
229
|
+
if f.get_longname() not in ["", ".", ".."]:
|
|
221
230
|
yield f
|
|
222
231
|
except Exception as e:
|
|
223
232
|
e = self.handle_impacket_error(e)
|
|
224
233
|
raise FileListError(f'{e.args}: Error listing files at "{share}{nt_path}"')
|
|
225
234
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
'''
|
|
235
|
+
def rebuild(self, error=""):
|
|
236
|
+
"""
|
|
229
237
|
Rebuild our SMBConnection() if it gets borked
|
|
230
|
-
|
|
231
|
-
log.debug(f
|
|
238
|
+
"""
|
|
239
|
+
log.debug(f"Rebuilding connection to {self.server} after error: {error}")
|
|
232
240
|
self.login(refresh=True)
|
|
233
241
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
'''
|
|
242
|
+
def handle_impacket_error(self, e, share="", filename="", display=False):
|
|
243
|
+
"""
|
|
237
244
|
Handle arbitrary Impacket errors
|
|
238
245
|
this is needed because the library doesn't implement proper inheritance for its exceptions
|
|
239
|
-
|
|
240
|
-
resource_str =
|
|
246
|
+
"""
|
|
247
|
+
resource_str = "/".join([self.server, share, filename]).rstrip("/")
|
|
241
248
|
|
|
242
249
|
if type(e) == KeyboardInterrupt:
|
|
243
250
|
raise
|
|
244
251
|
elif type(e) in (NetBIOSError, NetBIOSTimeout, BrokenPipeError, SessionError, CSessionError):
|
|
245
252
|
# the connection may need to be rebuilt
|
|
246
253
|
if type(e) in (SessionError, CSessionError):
|
|
247
|
-
if any([x in str(e) for x in (
|
|
254
|
+
if any([x in str(e) for x in ("PASSWORD_EXPIRED",)]):
|
|
248
255
|
self.rebuild(e)
|
|
249
256
|
else:
|
|
250
257
|
self.rebuild(e)
|
|
251
258
|
if type(e) in native_impacket_errors:
|
|
252
259
|
e = impacket_error(e)
|
|
253
260
|
if display:
|
|
254
|
-
log.debug(f
|
|
261
|
+
log.debug(f"{resource_str}: {str(e)[:150]}")
|
|
255
262
|
|
|
256
263
|
return e
|
man_spider/lib/spider.py
CHANGED
|
@@ -8,45 +8,45 @@ from man_spider.lib.spiderling import *
|
|
|
8
8
|
from man_spider.lib.parser import FileParser
|
|
9
9
|
|
|
10
10
|
# set up logging
|
|
11
|
-
log = logging.getLogger(
|
|
11
|
+
log = logging.getLogger("manspider")
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class MANSPIDER:
|
|
15
|
-
|
|
16
15
|
def __init__(self, options):
|
|
17
16
|
|
|
18
|
-
self.targets
|
|
19
|
-
self.threads
|
|
20
|
-
self.maxdepth
|
|
21
|
-
self.quiet
|
|
17
|
+
self.targets = options.targets
|
|
18
|
+
self.threads = options.threads
|
|
19
|
+
self.maxdepth = options.maxdepth
|
|
20
|
+
self.quiet = options.quiet
|
|
22
21
|
|
|
23
|
-
self.username
|
|
24
|
-
self.password
|
|
25
|
-
self.domain
|
|
26
|
-
self.nthash
|
|
27
|
-
self.use_kerberos
|
|
28
|
-
self.aes_key
|
|
29
|
-
self.dc_ip
|
|
30
|
-
self.max_failed_logons
|
|
31
|
-
self.max_filesize
|
|
22
|
+
self.username = options.username
|
|
23
|
+
self.password = options.password
|
|
24
|
+
self.domain = options.domain
|
|
25
|
+
self.nthash = options.hash
|
|
26
|
+
self.use_kerberos = options.kerberos
|
|
27
|
+
self.aes_key = options.aes_key
|
|
28
|
+
self.dc_ip = options.dc_ip
|
|
29
|
+
self.max_failed_logons = options.max_failed_logons
|
|
30
|
+
self.max_filesize = options.max_filesize
|
|
32
31
|
|
|
33
|
-
self.share_whitelist
|
|
34
|
-
self.share_blacklist
|
|
32
|
+
self.share_whitelist = options.sharenames
|
|
33
|
+
self.share_blacklist = options.exclude_sharenames
|
|
35
34
|
|
|
36
|
-
self.dir_whitelist
|
|
37
|
-
self.dir_blacklist
|
|
35
|
+
self.dir_whitelist = options.dirnames
|
|
36
|
+
self.dir_blacklist = options.exclude_dirnames
|
|
38
37
|
|
|
39
|
-
self.no_download
|
|
38
|
+
self.no_download = options.no_download
|
|
40
39
|
|
|
41
40
|
# applies "or" logic instead of "and"
|
|
42
41
|
# e.g. file is downloaded if filename OR extension OR content match
|
|
43
|
-
self.or_logic
|
|
42
|
+
self.or_logic = options.or_logic
|
|
43
|
+
|
|
44
|
+
self.extension_blacklist = options.exclude_extensions
|
|
45
|
+
self.file_extensions = options.extensions
|
|
44
46
|
|
|
45
|
-
self.extension_blacklist= options.exclude_extensions
|
|
46
|
-
self.file_extensions = options.extensions
|
|
47
47
|
if self.file_extensions:
|
|
48
48
|
extensions_str = '"' + '", "'.join(list(self.file_extensions)) + '"'
|
|
49
|
-
log.info(f
|
|
49
|
+
log.info(f"Searching by file extension: {extensions_str}")
|
|
50
50
|
|
|
51
51
|
self.init_filename_filters(options.filenames)
|
|
52
52
|
self.parser = FileParser(options.content, quiet=self.quiet)
|
|
@@ -61,20 +61,27 @@ class MANSPIDER:
|
|
|
61
61
|
self.smb_client_cache = dict()
|
|
62
62
|
|
|
63
63
|
# directory to store documents when searching contents
|
|
64
|
-
self.tmp_dir = Path(
|
|
64
|
+
self.tmp_dir = Path("/tmp/.manspider")
|
|
65
65
|
self.tmp_dir.mkdir(exist_ok=True)
|
|
66
66
|
|
|
67
67
|
# directory to store matching documents
|
|
68
|
-
self.loot_dir = Path.home() /
|
|
68
|
+
self.loot_dir = Path.home() / ".manspider" / "loot"
|
|
69
|
+
|
|
70
|
+
if options.loot_dir:
|
|
71
|
+
self.loot_dir = Path(options.loot_dir)
|
|
69
72
|
|
|
70
|
-
if(options.loot_dir):
|
|
71
|
-
self.loot_dir=Path(options.loot_dir)
|
|
72
|
-
|
|
73
73
|
self.loot_dir.mkdir(parents=True, exist_ok=True)
|
|
74
74
|
|
|
75
75
|
if not options.no_download:
|
|
76
|
-
log.info(f
|
|
76
|
+
log.info(f"Matching files will be downloaded to {self.loot_dir}")
|
|
77
77
|
|
|
78
|
+
self.modified_after = options.modified_after
|
|
79
|
+
self.modified_before = options.modified_before
|
|
80
|
+
|
|
81
|
+
if self.modified_after:
|
|
82
|
+
log.info(f"Filtering files modified after: {self.modified_after.strftime('%Y-%m-%d')}")
|
|
83
|
+
if self.modified_before:
|
|
84
|
+
log.info(f"Filtering files modified before: {self.modified_before.strftime('%Y-%m-%d')}")
|
|
78
85
|
|
|
79
86
|
def start(self):
|
|
80
87
|
|
|
@@ -99,7 +106,7 @@ class MANSPIDER:
|
|
|
99
106
|
continue
|
|
100
107
|
|
|
101
108
|
# save on CPU
|
|
102
|
-
sleep(.1)
|
|
109
|
+
sleep(0.1)
|
|
103
110
|
|
|
104
111
|
while 1:
|
|
105
112
|
self.check_spiderling_queue()
|
|
@@ -110,23 +117,19 @@ class MANSPIDER:
|
|
|
110
117
|
# make sure the queue is empty
|
|
111
118
|
self.check_spiderling_queue()
|
|
112
119
|
|
|
113
|
-
|
|
114
|
-
|
|
115
120
|
def init_file_extensions(self, file_extensions):
|
|
116
|
-
|
|
121
|
+
"""
|
|
117
122
|
Get ready to search by file extension
|
|
118
|
-
|
|
123
|
+
"""
|
|
119
124
|
|
|
120
125
|
self.file_extensions = FileExtensions()
|
|
121
126
|
if file_extensions:
|
|
122
127
|
self.file_extensions.update(file_extensions)
|
|
123
|
-
|
|
124
|
-
|
|
125
128
|
|
|
126
129
|
def init_filename_filters(self, filename_filters):
|
|
127
|
-
|
|
130
|
+
"""
|
|
128
131
|
Get ready to search by filename
|
|
129
|
-
|
|
132
|
+
"""
|
|
130
133
|
|
|
131
134
|
# strings to look for in filenames
|
|
132
135
|
# if empty, all filenames are matched
|
|
@@ -134,23 +137,22 @@ class MANSPIDER:
|
|
|
134
137
|
for f in filename_filters:
|
|
135
138
|
regex_str = str(f)
|
|
136
139
|
try:
|
|
137
|
-
if not any([f.startswith(x) for x in [
|
|
138
|
-
regex_str = rf
|
|
139
|
-
if not any([f.endswith(x) for x in [
|
|
140
|
-
regex_str = rf
|
|
140
|
+
if not any([f.startswith(x) for x in ["^", ".*"]]):
|
|
141
|
+
regex_str = rf".*{regex_str}"
|
|
142
|
+
if not any([f.endswith(x) for x in ["$", ".*"]]):
|
|
143
|
+
regex_str = rf"{regex_str}.*"
|
|
141
144
|
self.filename_filters.append(re.compile(regex_str, re.I))
|
|
142
145
|
except re.error as e:
|
|
143
146
|
log.error(f'Unsupported filename regex "{f}": {e}')
|
|
144
147
|
sleep(1)
|
|
145
148
|
if self.filename_filters:
|
|
146
149
|
filename_filter_str = '"' + '", "'.join([f.pattern for f in self.filename_filters]) + '"'
|
|
147
|
-
log.info(f
|
|
148
|
-
|
|
150
|
+
log.info(f"Searching by filename: {filename_filter_str}")
|
|
149
151
|
|
|
150
152
|
def check_spiderling_queue(self):
|
|
151
|
-
|
|
153
|
+
"""
|
|
152
154
|
Empty the spiderling queue
|
|
153
|
-
|
|
155
|
+
"""
|
|
154
156
|
|
|
155
157
|
while 1:
|
|
156
158
|
try:
|
|
@@ -160,55 +162,53 @@ class MANSPIDER:
|
|
|
160
162
|
except queue.Empty:
|
|
161
163
|
break
|
|
162
164
|
|
|
163
|
-
|
|
164
165
|
def process_message(self, message):
|
|
165
|
-
|
|
166
|
+
"""
|
|
166
167
|
Process messages from spiderlings
|
|
167
168
|
Log messages, errors, files, etc.
|
|
168
|
-
|
|
169
|
-
if message.type ==
|
|
169
|
+
"""
|
|
170
|
+
if message.type == "a":
|
|
170
171
|
if message.content == False:
|
|
171
172
|
self.failed_logons += 1
|
|
172
173
|
if self.lockout_threshold():
|
|
173
|
-
log.error(f
|
|
174
|
-
log.error(
|
|
175
|
-
#for spiderling in self.spiderling_pool:
|
|
174
|
+
log.error(f"REACHED MAXIMUM FAILED LOGONS OF {self.max_failed_logons:,}")
|
|
175
|
+
log.error("KILLING EXISTING SPIDERLINGS AND CONTINUING WITH GUEST/NULL SESSIONS")
|
|
176
|
+
# for spiderling in self.spiderling_pool:
|
|
176
177
|
# spiderling.kill()
|
|
177
|
-
self.username =
|
|
178
|
-
self.password =
|
|
179
|
-
self.nthash =
|
|
180
|
-
self.domain =
|
|
181
|
-
|
|
178
|
+
self.username = ""
|
|
179
|
+
self.password = ""
|
|
180
|
+
self.nthash = ""
|
|
181
|
+
self.domain = ""
|
|
182
182
|
|
|
183
183
|
def lockout_threshold(self):
|
|
184
|
-
|
|
184
|
+
"""
|
|
185
185
|
Return True if we've reached max failed logons
|
|
186
|
-
|
|
186
|
+
"""
|
|
187
187
|
|
|
188
188
|
if self.max_failed_logons is not None:
|
|
189
189
|
if self.failed_logons >= self.max_failed_logons and self.domain:
|
|
190
190
|
return True
|
|
191
191
|
return False
|
|
192
192
|
|
|
193
|
-
|
|
194
193
|
def get_smb_client(self, target):
|
|
195
|
-
|
|
194
|
+
"""
|
|
196
195
|
Check if we already have an smb_client cached
|
|
197
196
|
If not, then create it
|
|
198
|
-
|
|
197
|
+
"""
|
|
199
198
|
|
|
200
199
|
smb_client = self.smb_client_cache.get(target, None)
|
|
201
200
|
|
|
202
201
|
if smb_client is None:
|
|
203
202
|
smb_client = SMBClient(
|
|
204
|
-
target,
|
|
203
|
+
target.host,
|
|
205
204
|
self.username,
|
|
206
205
|
self.password,
|
|
207
206
|
self.domain,
|
|
208
207
|
self.nthash,
|
|
209
208
|
self.use_kerberos,
|
|
210
209
|
self.aes_key,
|
|
211
|
-
self.dc_ip
|
|
210
|
+
self.dc_ip,
|
|
211
|
+
port=target.port,
|
|
212
212
|
)
|
|
213
213
|
logon_result = smb_client.login()
|
|
214
214
|
if logon_result == False:
|