Skip to content

Commit

Permalink
adding form actions XSS detect, fixing parameter extraction bugs, dep…
Browse files Browse the repository at this point in the history
…s update
  • Loading branch information
liquidsec committed Dec 18, 2024
1 parent 4f4c2d7 commit 4a72eda
Show file tree
Hide file tree
Showing 6 changed files with 338 additions and 151 deletions.
5 changes: 3 additions & 2 deletions bbot/core/helpers/regexes.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,13 @@

# For use with excavate parameters extractor
input_tag_regex = re.compile(
r"<input[^>]*?name=[\"\']?([\-\._=+\/\w]+)[\"\']?[^>]*?value=[\"\']?([%\-\._=+\/\w]*)[\"\']?[^>]*?>"
r"<input[^>]*?\sname=[\"\']?([\-\._=+\/\w]+)[\"\']?[^>]*?\svalue=[\"\']?([:%\-\._=+\/\w]*)[\"\']?[^>]*?>"
)
input_tag_regex2 = re.compile(
r"<input[^>]*?value=[\"\']?([\-%\._=+\/\w]*)[\"\']?[^>]*?name=[\"\']?([\-\._=+\/\w]+)[\"\']?[^>]*?>"
r"<input[^>]*?\svalue=[\"\']?([:\-%\._=+\/\w]*)[\"\']?[^>]*?\sname=[\"\']?([\-\._=+\/\w]+)[\"\']?[^>]*?>"
)
input_tag_novalue_regex = re.compile(r"<input(?![^>]*\bvalue=)[^>]*?name=[\"\']?([\-\._=+\/\w]*)[\"\']?[^>]*?>")
input_tag_novalue_regex = re.compile(r"<input(?![^>]*\b\svalue=)[^>]*?\sname=[\"\']?([\-\._=+\/\w]*)[\"\']?[^>]*?>")
# jquery_get_regex = re.compile(r"url:\s?[\"\'].+?\?(\w+)=")
# jquery_get_regex = re.compile(r"\$.get\([\'\"].+[\'\"].+\{(.+)\}")
# jquery_post_regex = re.compile(r"\$.post\([\'\"].+[\'\"].+\{(.+)\}")
Expand Down
10 changes: 6 additions & 4 deletions bbot/modules/internal/excavate.py
Original file line number Diff line number Diff line change
Expand Up @@ -493,11 +493,11 @@ class GetForm(ParameterExtractorRule):
form_content_regexes = {
"input_tag_regex": bbot_regexes.input_tag_regex,
"input_tag_regex2": bbot_regexes.input_tag_regex2,
"input_tag_novalue_regex": bbot_regexes.input_tag_novalue_regex,
"select_tag_regex": bbot_regexes.select_tag_regex,
"textarea_tag_regex": bbot_regexes.textarea_tag_regex,
"button_tag_regex": bbot_regexes.button_tag_regex,
"button_tag_regex2": bbot_regexes.button_tag_regex2,
"_input_tag_novalue_regex": bbot_regexes.input_tag_novalue_regex,
}
extraction_regex = bbot_regexes.get_form_regex
output_type = "GETPARAM"
Expand All @@ -515,15 +515,17 @@ def extract(self):
for form_content_regex_name, form_content_regex in self.form_content_regexes.items():
input_tags = form_content_regex.findall(form_content)
if input_tags:
if form_content_regex_name == "input_tag_novalue_regex":
form_parameters[input_tags[0]] = None
if form_content_regex_name == "_input_tag_novalue_regex":
form_parameters.setdefault(input_tags[0], None)

else:
if form_content_regex_name in ["input_tag_regex2", "button_tag_regex2"]:
input_tags = [(b, a) for a, b in input_tags]

for parameter_name, original_value in input_tags:
form_parameters[parameter_name] = original_value.strip()
# form_parameters[parameter_name] = original_value.strip()
form_parameters.setdefault(parameter_name, original_value.strip())


for parameter_name, original_value in form_parameters.items():
yield (
Expand Down
18 changes: 9 additions & 9 deletions bbot/modules/lightfuzz_submodules/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,6 @@ def additional_params_process(self, additional_params, additional_params_populat
new_additional_params[k] = v
return new_additional_params

async def send_probe(self, probe):
probe = self.outgoing_probe_value(probe)
getparams = {self.event.data["name"]: probe}
url = self.lightfuzz.helpers.add_get_params(self.event.data["url"], getparams, encode=False).geturl()
self.lightfuzz.debug(f"lightfuzz sending probe with URL: {url}")
r = await self.lightfuzz.helpers.request(method="GET", url=url, allow_redirects=False, retries=2, timeout=10)
if r:
return r.text

def compare_baseline(
self, event_type, probe, cookies, additional_params_populate_empty=False, speculative_mode="GETPARAM"
):
Expand Down Expand Up @@ -131,6 +122,7 @@ async def compare_probe(
compare_result = await http_compare.compare(
self.event.data["url"], method="POST", data=data, cookies=cookies
)

elif event_type == "BODYJSON":
data = {self.event.data["name"]: f"{probe}"}
if additional_params:
Expand All @@ -150,6 +142,11 @@ async def standard_probe(
speculative_mode="GETPARAM",
allow_redirects=False,
):





probe = self.outgoing_probe_value(probe)

if event_type == "SPECULATIVE":
Expand Down Expand Up @@ -177,6 +174,8 @@ async def standard_probe(

if event_type == "POSTPARAM":



method = "POST"
data = {self.event.data["name"]: probe}
if self.event.data["additional_params"] is not None:
Expand All @@ -185,6 +184,7 @@ async def standard_probe(
self.event.data["additional_params"], additional_params_populate_empty
)
)

elif event_type == "BODYJSON":
method = "POST"
json_data = {self.event.data["name"]: probe}
Expand Down
34 changes: 21 additions & 13 deletions bbot/modules/lightfuzz_submodules/xss.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


class XSSLightfuzz(BaseLightfuzz):
def determine_context(self, html, random_string):
def determine_context(self, cookies, html, random_string):
between_tags = False
in_tag_attribute = False
in_javascript = False
Expand All @@ -29,20 +29,22 @@ def determine_context(self, html, random_string):

return between_tags, in_tag_attribute, in_javascript

async def check_probe(self, probe, match, context):
probe_result = await self.send_probe(probe)
if probe_result and match in probe_result:
async def check_probe(self, cookies, probe, match, context):
probe_result = await self.standard_probe(self.event.data["type"], cookies, probe)
if probe_result and match in probe_result.text:
self.results.append(
{
"type": "FINDING",
"description": f"Possible Reflected XSS. Parameter: [{self.event.data['name']}] Context: [{context}]",
"description": f"Possible Reflected XSS. Parameter: [{self.event.data['name']}] Context: [{context}] Parameter Type: [{self.event.data['type']}]",
}
)
return True
return False

async def fuzz(self):

lightfuzz_event = self.event.parent
cookies = self.event.data.get("assigned_cookies", {})

# If this came from paramminer_getparams and didn't have a http_reflection tag, we don't need to check again
if (
Expand All @@ -57,38 +59,44 @@ async def fuzz(self):

reflection = None
random_string = self.lightfuzz.helpers.rand_string(8)
reflection_probe_result = await self.send_probe(random_string)
if reflection_probe_result and random_string in reflection_probe_result:

reflection_probe_result = await self.standard_probe(self.event.data["type"], cookies, random_string)
self.lightfuzz.hugeinfo(reflection_probe_result.text)
if reflection_probe_result and random_string in reflection_probe_result.text:
reflection = True

if not reflection or reflection is False:
return

between_tags, in_tag_attribute, in_javascript = self.determine_context(reflection_probe_result, random_string)

between_tags, in_tag_attribute, in_javascript = self.determine_context(cookies, reflection_probe_result.text, random_string)
self.lightfuzz.debug(
f"determine_context returned: between_tags [{between_tags}], in_tag_attribute [{in_tag_attribute}], in_javascript [{in_javascript}]"
)
tags = ["z", "svg", "img"]
if between_tags:
for tag in tags:
between_tags_probe = f"<{tag}>{random_string}</{tag}>"
result = await self.check_probe(between_tags_probe, between_tags_probe, f"Between Tags ({tag} tag)")
result = await self.check_probe(cookies, between_tags_probe, between_tags_probe, f"Between Tags ({tag} tag)")
if result is True:
break

if in_tag_attribute:
in_tag_attribute_probe = f'{random_string}"'
in_tag_attribute_match = f'"{random_string}""'
await self.check_probe(in_tag_attribute_probe, in_tag_attribute_match, "Tag Attribute")
await self.check_probe(cookies, in_tag_attribute_probe, in_tag_attribute_match, "Tag Attribute")


in_tag_attribute_probe = f'javascript:{random_string}'
in_tag_attribute_match = f'action="javascript:{random_string}'
await self.check_probe(cookies, in_tag_attribute_probe, in_tag_attribute_match, "Form Action Injection")

if in_javascript:
in_javascript_probe = rf"</script><script>{random_string}</script>"
result = await self.check_probe(in_javascript_probe, in_javascript_probe, "In Javascript")
result = await self.check_probe(cookies, in_javascript_probe, in_javascript_probe, "In Javascript")
if result is False:
in_javasscript_escape_probe = rf"a\';zzzzz({random_string})\\"
in_javasscript_escape_match = rf"a\\';zzzzz({random_string})\\"
await self.check_probe(
await self.check_probe(cookies,
in_javasscript_escape_probe,
in_javasscript_escape_match,
"In Javascript (escaping the escape character)",
Expand Down
47 changes: 47 additions & 0 deletions bbot/test/test_step_2/module_tests/test_module_lightfuzz.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,53 @@ def check(self, module_test, events):
assert web_parameter_emitted, "WEB_PARAMETER was not emitted"
assert xss_finding_emitted, "Between Tags XSS FINDING not emitted"

# Form Action Injection Detection
class Test_Lightfuzz_xss_formaction(Test_Lightfuzz_xss):

def request_handler(self, request):
form_data = request.form
value = form_data.get("func", None)

parameter_block = """
<section class=search>
<form action="/" method=POST>
<input type=text placeholder='Search the blog...' name=search>
<input type=text name=func value="/">
<button type=submit class=button>Search</button>
</form>
</section>
"""

if value:
xss_block = f"""
<section class=search>
<form action="{value}" method=POST>
<input type=text placeholder='Search the blog...' name=search>
<input type=text name=func value="{value}">
<button type=submit class=button>Search</button>
</form>
</section>
"""


return Response(xss_block, status=200)

return Response(parameter_block, status=200)

def check(self, module_test, events):
web_parameter_emitted = False
xss_finding_emitted = False
for e in events:
if e.type == "WEB_PARAMETER":
if "HTTP Extracted Parameter [search]" in e.data["description"]:
web_parameter_emitted = True

if e.type == "FINDING":
if "Possible Reflected XSS. Parameter: [func] Context: [Form Action Injection] Parameter Type: [POSTPARAM]" in e.data["description"]:
xss_finding_emitted = True

assert web_parameter_emitted, "WEB_PARAMETER was not emitted"
assert xss_finding_emitted, "Form Action XSS FINDING not emitted"

# Base64 Envelope XSS Detection
class Test_Lightfuzz_envelope_base64(Test_Lightfuzz_xss):
Expand Down
Loading

0 comments on commit 4a72eda

Please sign in to comment.