Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
06adbe2
Feature to add testing of default credentials. still need to include …
redsand Jul 24, 2023
71bb232
Feature: properly check and detect WWW-Authenticate protected realms.…
redsand Jul 25, 2023
181a3af
BUG: fix check to only validate WWW-Authenticate header is present
redsand Jul 25, 2023
9120852
BUG: fixing error during reporting of mixed types of bytes and string…
redsand Jul 25, 2023
895e52c
Merging from foreign master
redsand Jul 25, 2023
79cf7a2
FEATURE: add new screenshots to report for visibility, along with a t…
redsand Jul 25, 2023
d7e4415
BUG: fixing formatting of html to display additional screenshots
redsand Jul 25, 2023
97eac22
Scrubbing debugging info
redsand Jul 25, 2023
688c277
BUG: fix selenium driver leak
redsand Jul 26, 2023
b73f17f
Adding Rico IPP-Print signature for realm
redsand Jul 26, 2023
a674837
BUG: fix overwriting of authentication screenshots
redsand Jul 26, 2023
f8c240b
Adds check for empty passwords as well as asignature for WEB Remote V…
redsand Aug 2, 2023
01b4549
Feature is disabled by default. Also allows to log validated credenti…
redsand Aug 7, 2023
2e278e4
cleanup
redsand Aug 7, 2023
65bed89
Fixing false positive when performing uri authentication detection
redsand Aug 8, 2023
0bc8150
FIX: fix auth false positive
redsand Aug 8, 2023
6573cc0
SIGS: Adding signatures for additional Polycom phones.
redsand Aug 11, 2023
bf62efa
Add additional failure detection to form authentication testing.
redsand Aug 15, 2023
2d4ac92
Re-adding <blank> marker.
redsand Aug 15, 2023
0b7c585
Treating <blank> as an empty string
redsand Aug 15, 2023
4088f0e
Merge branch 'master' into FEATURE_PASSWORD_VERIFICATION
Relkci Jun 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 59 additions & 9 deletions Python/EyeWitness.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ def create_cli_parser():
a timeout'.replace(' ', ''), type=int,
help='Max retries on timeouts')

cred_validation_options = parser.add_argument_group('Credential Validation Options')
cred_validation_options.add_argument('--enable-validation',
default=None, action='store_true',
help='Test default credentials and validate the host is vulnerable.')
cred_validation_options.add_argument('--validation-output', metavar='Output log file of validated credentials',
default=None,
help='Specify a file to log valid credentials (for easy automation).')

report_options = parser.add_argument_group('Report Output Options')
report_options.add_argument('-d', metavar='Directory Name',
default=None,
Expand Down Expand Up @@ -256,6 +264,8 @@ def exitsig(*args):
if cli_parsed.web:
create_driver = selenium_module.create_driver
capture_host = selenium_module.capture_host
auth_host = selenium_module.auth_host
test_realm = selenium_module.test_realm
if not cli_parsed.show_selenium:
display = Display(visible=0, size=(1920, 1080))
display.start()
Expand All @@ -269,8 +279,20 @@ def exitsig(*args):
web_index_head = create_web_index_head(cli_parsed.date, cli_parsed.time)

driver = create_driver(cli_parsed)
result, driver = capture_host(cli_parsed, http_object, driver)
result = default_creds_category(result)
result, is_protected = test_realm(cli_parsed, http_object, driver)
if not result:
result, driver = capture_host(cli_parsed, http_object, driver)
result = default_creds_category(result)
if cli_parsed.enable_validation:
auth_host(cli_parsed, result, driver, is_protected)
else:
print("[!] HTTP Authentication Realm Detected")
result, driver = capture_host(cli_parsed, result, driver)
if result.default_creds:
result = default_creds_category(result)
if cli_parsed.enable_validation:
auth_host(cli_parsed, result, driver, is_protected)

if cli_parsed.resolve:
result.resolved = resolve_host(result.remote_system)
driver.quit()
Expand All @@ -291,6 +313,8 @@ def worker_thread(cli_parsed, targets, lock, counter, user_agent=None):
if cli_parsed.web:
create_driver = selenium_module.create_driver
capture_host = selenium_module.capture_host
auth_host = selenium_module.auth_host
test_realm = selenium_module.test_realm

with lock:
driver = create_driver(cli_parsed, user_agent)
Expand Down Expand Up @@ -320,16 +344,42 @@ def worker_thread(cli_parsed, targets, lock, counter, user_agent=None):

http_object.resolved = resolve_host(http_object.remote_system)
if user_agent is None:
http_object, driver = capture_host(
cli_parsed, http_object, driver)
if http_object.category is None and http_object.error_state is None:

# head call and detect WWW-Authenticate realm info
# if found, attempt manual auth

http_object2, is_protected = test_realm(cli_parsed, http_object, driver)
if not http_object2:
http_object, driver = capture_host(cli_parsed, http_object, driver)
http_object = default_creds_category(http_object)
manager.update_http_object(http_object)
if cli_parsed.enable_validation:
auth_host(cli_parsed, http_object, driver, is_protected)
manager.update_http_object(http_object)
else:
print("[!] HTTP Authentication Realm Detected")
http_object2, driver = capture_host(cli_parsed, http_object2, driver)
if http_object2.default_creds:
http_object2 = default_creds_category(http_object2)
if cli_parsed.enable_validation:
auth_host(cli_parsed, http_object2, driver, is_protected)
manager.update_http_object(http_object2)

else:
ua_object, driver = capture_host(
cli_parsed, http_object, driver)
if http_object.category is None and http_object.error_state is None:

ua_object, is_protected = test_realm(cli_parsed, http_object, driver)
if not ua_object:
ua_object, driver = capture_host(cli_parsed, http_object, driver)
ua_object = default_creds_category(ua_object)
if cli_parsed.enable_validation:
auth_host(cli_parsed, ua_object, driver, is_protected)
else:
print("[!] HTTP Authentication Realm Detected")
ua_object, driver = capture_host(cli_parsed, ua_object, driver)
if ua_object.default_creds:
ua_object = default_creds_category(ua_object)
if cli_parsed.enable_validation:
auth_host(cli_parsed, ua_object, driver, is_protected)

manager.update_ua_object(ua_object)

counter[0].value += 1
Expand Down
23 changes: 20 additions & 3 deletions Python/modules/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,7 @@ def do_jitter(cli_parsed):
sleep_value = sleep_value * .01
sleep_value = 1 - sleep_value
sleep_value = sleep_value * cli_parsed.jitter
print("[*] Sleeping for " + str(sleep_value) + " seconds..")
print("[*] Sleeping for " + "{:.2f}".format(sleep_value) + " seconds..")
try:
time.sleep(sleep_value)
except KeyboardInterrupt:
Expand Down Expand Up @@ -726,9 +726,24 @@ def default_creds_category(http_object):
for sig in signatures:
# Find the signature(s), split them into their own list if needed
# Assign default creds to its own variable
# !! added support for description field and cred field, so that creds can be
# !! automatically checked
sig = sig.rstrip("\n")
sig_cred = sig.split('|')
if len(sig_cred) > 3:
# character '|' is contained in the page_sig, rejoin and work backwards
tmp_sig = sig_cred[0:len(sig_cred)-2]
sig = "|".join(tmp_sig)
sig_cred2 = [ sig, sig_cred[len(sig_cred) - 2], sig_cred[len(sig_cred) - 1] ]
sig_cred = sig_cred2

page_sig = sig_cred[0].split(";")
cred_info = sig_cred[1].strip()
desc = sig_cred[1].strip()
try:
cred_info = sig_cred[2]
except Exception as e:
# default to description, assume description is missing
cred_info = desc

# Set our variable to 1 if the signature was not identified. If it is
# identified, it will be added later on. Find total number of
Expand All @@ -739,10 +754,12 @@ def default_creds_category(http_object):
# web page needed to make a signature Delimete the "signature"
# by ";" before the "|", and then have the creds after the "|"
if all([x.lower() in http_object.source_code.decode().lower() for x in page_sig]):
http_object._description = desc

if http_object.default_creds is None:
http_object.default_creds = cred_info
else:
http_object.default_creds += '\n' + cred_info
http_object.default_creds += ';' + cred_info

for cat in categories:
# Find the signature(s), split them into their own list if needed
Expand Down
166 changes: 148 additions & 18 deletions Python/modules/objects.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import html
import os
import re
import traceback

from modules.helpers import strip_nonalphanum

Expand Down Expand Up @@ -30,6 +31,10 @@ def __init__(self):
self._ua_left = None
self._resolved = None

# parsed authentication, tuple of username and password, if username is empty it will be None
self._description = ""
self._parsed_creds = [ ]

def set_paths(self, outdir, suffix=None):
file_name = self.remote_system.replace('://', '.')
for char in [':', '/', '?', '=', '%', '+']:
Expand Down Expand Up @@ -179,6 +184,44 @@ def default_creds(self):

@default_creds.setter
def default_creds(self, default_creds):
if not default_creds: return

# attempt to parse
# filter out those without '/' or start with '(' # save as comment only
if not '/' in default_creds or default_creds[0] == '(':
self._parsed_creds = [ (None, None, default_creds, None, '') ] # no user or pass, only comment
self._default_creds = default_creds
return

try:
creds = default_creds.split(';')
# parse out those with comments if present
# else split /
for c in creds:
user = passwd = comment = None
if ' (' in c:
x = c.split(' (')
comment = x[1][:-1].strip() # assuming ending ) and removing it
y = x[0].split('/')
user = y[0].strip()
try:
passwd = y[1].strip()
except:
# Only 1 value found
passwd = None
else:
y = c.split('/')
user = y[0].strip()
try:
passwd = y[1].strip()
except:
# Only 1 value found
passwd = None
self._parsed_creds = self._parsed_creds + [ (user,passwd,comment,None, '') ]
except Exception as e:
print("[!] Failed to parse credentials: ", e)
print(" ", default_creds)
print(traceback.format_exc())
self._default_creds = default_creds

@property
Expand All @@ -203,12 +246,12 @@ def create_table_html(self):
html = u""
if self._remote_login is not None:
html += ("""<tr>
<td><div style=\"display: inline-block; width: 300px; word-wrap: break-word\">
<td valign="top"><div style=\"display: inline-block; width: 300px; word-wrap: break-word\">
<a href=\"{address}\" target=\"_blank\">{address}</a><br>
""").format(address=self._remote_login)
else:
html += ("""<tr>
<td><div style=\"display: inline-block; width: 300px; word-wrap: break-word\">
<td valign="top"><div style=\"display: inline-block; width: 300px; word-wrap: break-word\">
<a href=\"{address}\" target=\"_blank\">{address}</a><br>
""").format(address=self.remote_system)

Expand All @@ -227,13 +270,51 @@ def create_table_html(self):
self.remote_system)

if self.default_creds is not None:
try:
html += "<br><b>Default credentials:</b> {0}<br>".format(
self.sanitize(self.default_creds))
except UnicodeEncodeError:
html += u"<br><b>Default credentials:</b> {0}<br>".format(
self.sanitize(self.default_creds))

if type(self.default_creds) is list:
try:
html += "<br><b>Default credentials:</b> {0} ({1})<br>".format(
self.sanitize(self._description), self.sanitize(", ".join(self.default_creds)))
except UnicodeEncodeError:
try:
html += u"<br><b>Default credentials:</b> {0} ({1})<br>".format(
self.sanitize(self._description), self.sanitize(self.default_creds))
except:
print('[!] Failed to format default credentials: ')
# print(json.dumps(self.default_creds))
else:
try:
html += "<br><b>Default credentials:</b> {0} ({1})<br>".format(
self.sanitize(self._description), self.sanitize(self.default_creds))
except UnicodeEncodeError:
try:
html += u"<br><b>Default credentials:</b> {0} ({1})<br>".format(
self.sanitize(self._description), self.sanitize(self.default_creds))
except:
print('[!] Failed to format default credentials: ')
# print(json.dumps(self.default_creds))

if self._parsed_creds is not None and type(self._parsed_creds) is list and len(self._parsed_creds) > 0:
html += "<br /><table width=\"100%\" style=\"padding-left: 5px; padding-right: 5px;\"><thead><td>User</td><td>Password</td><td>Status</td></thead>"
for cred in self._parsed_creds:
html += "<tr>"
if cred[0] and cred[1]:
html += "<td>{0}</td><td>{1}</td>".format(cred[0], cred[1])
elif cred[0]:
html += "<td>{0}</td><td></td>".format(cred[0])
elif cred[1]:
html += "<td></td><td>{0}</td>".format(cred[0])
if cred[3] == True:
if len(cred[4]) > 0:
a_scr_path = os.path.relpath(cred[4], self.root_path)
html += '<td bgcolor="green"><a href=\"{0}\" target=\"_blank\">Success</a></td>'.format(a_scr_path)
else:
html += '<td bgcolor="green">Success</td>'
else:
html += '<td bgcolor="red">Failed</td>'
html+= "</tr>"
html += "</table><br />"


if self.error_state is None:
try:
html += "\n<br><b> Page Title: </b>{0}\n".format(
Expand Down Expand Up @@ -286,6 +367,13 @@ def create_table_html(self):
target=\"_blank\"><img style=\"max-height:400px;height: expression(this.height > 400 ? 400: true);\"
src=\"{1}\"></a></div></td></tr>""").format(
src_path, scr_path)
if self._parsed_creds is not None and type(self._parsed_creds) is list and len(self._parsed_creds) > 0:
for cred in self._parsed_creds:
if len(cred[4]) > 0:
a_scr_path = os.path.relpath(cred[4], self.root_path)
html += "<br /><a href=\"{0}\" target=\"_blank\"><img src=\"{0}\" height=\"400\" /></a><br />".format(a_scr_path)

html += ("""</td></tr>""")

if len(self._uadata) > 0:
divid = strip_nonalphanum(self.remote_system)
Expand Down Expand Up @@ -389,7 +477,7 @@ def create_table_html(self, divid):
src_path = os.path.relpath(self.source_path, self.root_path)
html = u""
html += ("""<tr class="hide {0}">
<td><div style=\"display: inline-block; width: 300px; word-wrap: break-word\">
<td valign="top"><div style=\"display: inline-block; width: 300px; word-wrap: break-word\">
<a href=\"{1}\" target=\"_blank\">{1}</a><br>
""").format(divid, self.remote_system)

Expand All @@ -407,12 +495,49 @@ def create_table_html(self, divid):
self.remote_system)

if self.default_creds is not None:
try:
html += "<br><b>Default credentials:</b> {0}<br>".format(
self.sanitize(self.default_creds))
except UnicodeEncodeError:
html += u"<br><b>Default credentials:</b> {0}<br>".format(
self.sanitize(self.default_creds))
if type(self.default_creds) is list:
try:
html += "<br><b>Default credentials:</b> {0} ({1})<br>".format(
self.sanitize(self._description), self.sanitize(", ".join(self.default_creds)))
except UnicodeEncodeError:
try:
html += u"<br><b>Default credentials:</b> {0} ({1})<br>".format(
self.sanitize(self._description), self.sanitize(self.default_creds))
except:
print('[!] Failed to format default credentials: ')
# print(json.dumps(self.default_creds))
else:
try:
html += "<br><b>Default credentials:</b> {0} ({1})<br>".format(
self.sanitize(self._description), self.sanitize(self.default_creds))
except UnicodeEncodeError:
try:
html += u"<br><b>Default credentials:</b> {0} ({1})<br>".format(
self.sanitize(self._description), self.sanitize(self.default_creds))
except:
print('[!] Failed to format default credentials: ')
# print(json.dumps(self.default_creds))

if self._parsed_creds is not None and type(self._parsed_creds) is list and len(self._parsed_creds) > 0:
html += "<br /><table width=\"100%\" style=\"padding-left: 5px; padding-right: 5px;\"><thead><td>User</td><td>Password</td><td>Status</td></thead>"
for cred in self._parsed_creds:
html += "<tr>"
if cred[0] and cred[1]:
html += "<td>{0}</td><td>{1}</td>".format(cred[0], cred[1])
elif cred[0]:
html += "<td>{0}</td><td></td>".format(cred[0])
elif cred[1]:
html += "<td></td><td>{0}</td>".format(cred[0])
if cred[3] == True:
if len(cred[4]) > 0:
a_scr_path = os.path.relpath(cred[4], self.root_path)
html += '<td bgcolor="green"><a href=\"{0}\" target=\"_blank\">Success</a></td>'.format(a_scr_path)
else:
html += '<td bgcolor="green">Success</td>'
else:
html += '<td bgcolor="red">Failed</td>'
html+= "</tr>"
html += "</table><br />"

try:
html += "\n<br><b> Page Title: </b>{0}\n".format(
Expand Down Expand Up @@ -446,7 +571,12 @@ def create_table_html(self, divid):
target=\"_blank\">Source Code</a></div></td>
<td><div id=\"screenshot\"><a href=\"{1}\"
target=\"_blank\"><img style=\"max-height:400px;height: expression(this.height > 400 ? 400: true);\"
src=\"{1}\"></a></div></td></tr>""").format(
src_path, scr_path)
src=\"{1}\"></a></div></td></tr>""").format(src_path, scr_path)
if self._parsed_creds is not None and type(self._parsed_creds) is list and len(self._parsed_creds) > 0:
for cred in self._parsed_creds:
if len(cred[4]) > 0:
a_scr_path = os.path.relpath(cred[4], self.root_path)
html += "<br /><a href=\"{0}\" target=\"_blank\"><img src=\"{0}\" height=\"400\" /></a><br />".format(a_scr_path)
html += ("""</td></tr>""")
return html

Loading