Skip to content

Commit

Permalink
Merge pull request #97 from blacklanternsecurity/dev
Browse files Browse the repository at this point in the history
adding vstate module + severity colors in cli
  • Loading branch information
liquidsec authored Aug 3, 2023
2 parents 15af124 + 600b582 commit 6d56304
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 10 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Inspired by [Blacklist3r](https://github.com/NotSoSecure/Blacklist3r), with a de
| Express_SignedCookies_ES | Checks express.js express-session middleware for signed cookies and session cookies for known 'session secret' |
| Express_SignedCookies_CS | Checks express.js cookie-session middleware for signed cookies and session cookies for known secret |
| Laravel_SignedCookies | Checks 'laravel_session' cookies for known laravel 'APP_KEY' |
| ASPNET_Vstate | Checks for a once popular custom compressed Viewstate [code snippet](https://www.graa.nl/articles/2010.html) vulnerable to RCE|

## Installation

Expand Down Expand Up @@ -265,6 +266,7 @@ Symfony_SignedURL = modules_loaded["symfony_signedurl"]
Express_SignedCookies_ES = modules_loaded["express_signedcookies_es"]
Express_SignedCookies_CS = modules_loaded["express_signedcookies_cs"]
Laravel_SignedCookies = modules_loaded["laravel_signedcookies"]
ASPNET_Vstate = modules_loaded["aspnet_vstate"]
x = ASPNET_Viewstate()
print(f"###{str(x.__class__.__name__)}###")
Expand Down Expand Up @@ -374,6 +376,15 @@ if r:
else:
print("KEY NOT FOUND :(")
x = ASPNET_Vstate()
print(f"###{str(x.__class__.__name__)}###")
r = x.check_secret("H4sIAAAAAAAEAPvPyJ/Cz8ppZGpgaWpgZmmYAgAAmCJNEQAAAA==")
if r:
print(r)
else:
print("KEY NOT FOUND :(")
```
#### Carve
Expand Down
11 changes: 11 additions & 0 deletions badsecrets/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import re
import os
import gzip
import base64
import hashlib
import binascii
import requests
import badsecrets.errors
from abc import abstractmethod
Expand Down Expand Up @@ -40,6 +43,14 @@ def __init__(self, custom_resource=None, **kwargs):
def check_secret(self, secret):
raise NotImplementedError

@staticmethod
def attempt_decompress(value):
try:
uncompressed = gzip.decompress(base64.b64decode(value))
except (gzip.BadGzipFile, binascii.Error, ValueError):
return False
return uncompressed

@classmethod
def get_description(self):
return self.description
Expand Down
9 changes: 8 additions & 1 deletion badsecrets/examples/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,14 @@ class ReportSecret(BaseReport):
def report(self):
self.print_report(print_status("Known Secret Found!\n", color=Fore.GREEN, passthru=True))
print_status(f"Secret: {self.x['secret']}", color=Fore.GREEN)
print(f"Severity: {self.x['description']['severity']}")
severity = self.x["description"]["severity"]
if severity in ["CRITICAL", "HIGH"]:
severity_color = Fore.RED
elif severity in ["LOW", "MEDIUM"]:
severity_color = Fore.YELLOW
elif severity == "INFO":
severity_color = Fore.BLUE
print_status(f"Severity: {self.x['description']['severity']}", color=severity_color)
print(f"Details: {self.x['details']}")


Expand Down
24 changes: 24 additions & 0 deletions badsecrets/modules/aspnet_vstate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import re

from badsecrets.base import BadsecretsBase
from badsecrets.modules.aspnet_viewstate import ASPNET_Viewstate

# Reference: https://www.graa.nl/articles/2010.html


class ASPNET_vstate(BadsecretsBase):
identify_regex = re.compile(r"^H4sI.+$")
description = {"product": "ASP.NET Compressed Vstate", "secret": "unprotected", "severity": "CRITICAL"}

def carve_regex(self):
return re.compile(r"<input.+__VSTATE\"\svalue=\"(H4sI.+)\"")

def check_secret(self, compressed_vstate):
if not self.identify(compressed_vstate):
return None

uncompressed = self.attempt_decompress(compressed_vstate)
if uncompressed and ASPNET_Viewstate.valid_preamble(uncompressed):
r = {"source": compressed_vstate, "info": "ASP.NET Vstate (Unprotected, Compressed)"}
return {"secret": "UNPROTECTED (compressed)", "details": r}
return None
9 changes: 0 additions & 9 deletions badsecrets/modules/jsf_viewstate.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import re
import gzip
import hmac
import base64
import hashlib
Expand All @@ -25,14 +24,6 @@ class Jsf_viewstate(BadsecretsBase):

hashcat_hashalg_table = {"MD5": "50", "SHA1": "150", "SHA256": "1450", "SHA384": "10800", "SHA512": "1750"}

@staticmethod
def attempt_decompress(value):
try:
uncompressed = gzip.decompress(base64.b64decode(value))
except (gzip.BadGzipFile, binascii.Error, ValueError):
return False
return uncompressed

def carve_regex(self):
return re.compile(r"<input.+?name=\"javax\.faces\.ViewState\".+?value=\"([^\"]*)\"")

Expand Down
10 changes: 10 additions & 0 deletions tests/aspnet_vstate_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from badsecrets import modules_loaded

ASPNETVstate = modules_loaded["aspnet_vstate"]


def test_aspnet_vstate():
x = ASPNETVstate()
found_key = x.check_secret("H4sIAAAAAAAEAPvPyJ/Cz8ppZGpgaWpgZmmYAgAAmCJNEQAAAA==")
assert found_key
assert found_key["secret"] == "UNPROTECTED (compressed)"
84 changes: 84 additions & 0 deletions tests/examples_cli_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import requests_mock
from mock import patch

from badsecrets.modules.generic_jwt import Generic_JWT

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(f"{os.path.dirname(SCRIPT_DIR)}/examples")
from badsecrets.examples import cli
Expand Down Expand Up @@ -645,6 +647,54 @@ def test_example_cli_dotnet45_url(monkeypatch, capsys):
assert ("Details: Mode [DOTNET45]") in captured.out


def test_example_cli_aspnetvstate_url(monkeypatch, capsys):
base_vulnerable_page_aspnet_vstate = """
<!doctype html>
<!--[if IE 9]> <html class="no-js lt-ie10" lang="en" xmlns:fb="http://ogp.me/ns/fb#"> <![endif]-->
<!--[if gt IE 9]><!--> <html class="no-js" lang="en" xmlns:fb="http://ogp.me/ns/fb#"> <!--<![endif]-->
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
</head>
<body id="_body">
<a id="page_top" name="page_top"></a>
<form method="post" action="./default.aspx" id="form1">
<div class="aspNetHidden">
<input type="hidden" name="__VSTATE" id="__VSTATE" value="H4sIAAAAAAAEAPvPyJ/Cz8ppZGpgaWpgZmmYAgAAmCJNEQAAAA==" />
<input type="hidden" name="__VSTATELENGTH" id="__VSTATELENGTH" value="52" />
<input type="hidden" name="__VSTATEHOST" id="__VSTATEHOST" value="02" />
<input type="hidden" name="__VSTATETIMESTAMP" id="__VSTATETIMESTAMP" value="7/29/2016 11:19:46 AM" />
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="" />
</div>
<p>content</p>
</html>
"""
with requests_mock.Mocker() as m:
m.get(
f"http://172.16.25.128/form.aspx",
status_code=200,
text=base_vulnerable_page_aspnet_vstate,
)

monkeypatch.setattr(
"sys.argv",
[
"python",
"--url",
"http://172.16.25.128/form.aspx",
],
)
cli.main()
captured = capsys.readouterr()
assert ("Known Secret Found!") in captured.out
assert ("Product: H4sIAAAAAAAEAPvPyJ/Cz8ppZGpgaWpgZmmYAgAAmCJNEQAAAA==") in captured.out
assert ("ASP.NET Compressed Vstate") in captured.out


def test_example_cli_dotnet45_manual(monkeypatch, capsys):
monkeypatch.setattr(
"sys.argv",
Expand Down Expand Up @@ -689,3 +739,37 @@ def test_example_cli_dotnetbadgenerator(monkeypatch, capsys):
)
cli.main()
assert not exit_mock.called


def test_examples_cli_colors_medlow(monkeypatch, capsys):
monkeypatch.setattr(
"sys.argv",
[
"python",
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
],
)
monkeypatch.setattr(
Generic_JWT, "description", {"product": "JSON Web Token (JWT)", "secret": "HMAC/RSA Key", "severity": "MEDIUM"}
)
cli.main()
captured = capsys.readouterr()
assert "your-256-bit-secret" in captured.out
print(captured.out)


def test_examples_cli_colors_info(monkeypatch, capsys):
monkeypatch.setattr(
"sys.argv",
[
"python",
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
],
)
monkeypatch.setattr(
Generic_JWT, "description", {"product": "JSON Web Token (JWT)", "secret": "HMAC/RSA Key", "severity": "INFO"}
)
cli.main()
captured = capsys.readouterr()
assert "your-256-bit-secret" in captured.out
print(captured.out)

0 comments on commit 6d56304

Please sign in to comment.