Skip to content

Release 1.3.0 - #432

Merged
liquidsec merged 41 commits into
mainfrom
dev
Oct 14, 2026
Merged

liquidsec merged 41 commits into
mainfrom
dev

Conversation

@liquidsec

@liquidsec liquidsec commented Aug 4, 2026 •

Copy link
Copy Markdown
Contributor

Release 1.3.0.

New module

  • NextAuth passive module: checks NextAuth.js v4 (A256GCM) and Auth.js v5 (A256CBC-HS512) dir JWE session cookies for a known or weak NEXTAUTH_SECRET / AUTH_SECRET, via HKDF-derived keys. Adds JWE helpers and Supabase/Directus default JWT secrets (Add NextAuth/Auth.js JWE session token module #431).

Fixes

  • Fix carve misses on __COMPRESSED_VSTATE fields — the field name was in neither the carve regex nor the YARA prefilter, so pages using it were never detected. Also fixes first-match shadowing, where an empty decoy __VIEWSTATE hid a real payload, and quadratic backtracking in the compressed-viewstate and JSF carve regexes (Fix carve misses on __COMPRESSED_VSTATE fields #430).
  • Fix ASP.NET viewstate CLI argument handling, where a viewstate that cracked via --url reported nothing when the same viewstate and generator were passed positionally. check_secret_args was 3, so the 4th positional (ViewStateUserKey) was silently sliced away; and the generator pattern matched uppercase hex only, so a lowercase generator fell through to the wrong branch and left the generator at its 0000 default (fix ASP.NET viewstate CLI argument handling #436).

Resources and performance

  • Grow the password wordlist to top 250k, replacing the 100k list, and cache + dedup wordlist loads in load_resources so the larger list doesn't multiply per-event I/O or set rebuilds. Re-points eight passive modules to the new list. Sourced from SecLists xato-net top-1M; superset of the old top-100k.
  • Speed up Java SHA1PRNG key derivation by replacing twos_complement()'s bin/str/int round-trip with a 256-entry lookup table. Output is byte-identical, verified exhaustively over all 256 byte values and against 45 pre-change key vectors. updateState drops from 23s to 2.4s and the JSF 3DES sweep from 13.3s to 9.6s (Speed up Java SHA1PRNG key derivation #440).
  • Speed up the test suite by trimming brute-force wordlists to the entries each test needs, via a trim_wordlist pytest marker. The 3.10 CI job had been timing out at 40 minutes on all three attempts. Shipped wordlists and module behaviour are unchanged (Speed up tests by trimming brute-force wordlists #439).

Dependencies

Release

  • Bump __version__ to 1.3.0.

Merging this to main triggers the publish job in tests.yaml: it runs after the test matrix, compares __version__ against the latest tag (currently 1.2.1), then builds, uploads to PyPI, and pushes a 1.3.0 tag.

liquidsec and others added 9 commits July 3, 2026 22:40
load_resources reads each wordlist combination once per process and deduplicates it, so the larger list doesn't multiply per-event I/O or set rebuilds. Sourced from SecLists xato-net top-1M; superset of the old top-100k.
Cracks NextAuth v4 (A256GCM) and Auth.js v5 (A256CBC-HS512) session cookies via HKDF-derived keys against known/leaked secrets. Adds JWE helpers and Supabase/Directus default JWT secrets.
Bumps [django](https://github.com/django/django) from 5.2.15 to 5.2.16.
- [Commits](django/django@5.2.15...5.2.16)

---
updated-dependencies:
- dependency-name: django
  dependency-version: 5.2.16
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
…jango-5.2.16

build(deps): bump django from 5.2.15 to 5.2.16
- add __COMPRESSED_VSTATE to the compressed viewstate carve regex and YARA
  prefilter; it was in neither, so pages using that field name were never
  detected
- _carve_body walks re.finditer instead of taking only re.search's first
  match, so an empty decoy __VIEWSTATE no longer shadows a real payload
- reshape the aspnet_compressedviewstate and jsf_viewstate carve regexes to
  assert the field name in a lookahead: allows attributes between name= and
  value=, and fixes quadratic backtracking
…state-carve

Fix carve misses on __COMPRESSED_VSTATE fields
Add NextAuth/Auth.js JWE session token module
@codecov-commenter

codecov-commenter commented Aug 4, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.88889% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 99.29%. Comparing base (d9e43d8) to head (5dcf23b).
⚠️ Report is 42 commits behind head on main.

Files with missing lines Patch % Lines
badsecrets/modules/passive/generic_jwt.py 89.28% 6 Missing ⚠️
badsecrets/__version__.py 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #432      +/-   ##
==========================================
- Coverage   99.45%   99.29%   -0.16%     
==========================================
  Files          30       31       +1     
  Lines        3102     3259     +157     
==========================================
+ Hits         3085     3236     +151     
- Misses         17       23       +6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

dependabot Bot and others added 20 commits August 5, 2026 04:46
Bumps [cryptography](https://github.com/pyca/cryptography) from 48.0.1 to 50.0.0.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](pyca/cryptography@48.0.1...50.0.0)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-version: 50.0.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [django](https://github.com/django/django) from 5.2.15 to 5.2.16.
- [Commits](django/django@5.2.15...5.2.16)

---
updated-dependencies:
- dependency-name: django
  dependency-version: 5.2.16
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Two defects made the positional CLI path silently mishandle valid input,
so a viewstate that cracked via --url would report nothing when the same
viewstate and generator were passed by hand:

- check_secret_args was 3, so the 4th positional was sliced away by
  check_all_modules. check_secret() accepts (viewstate, generator, url,
  ViewStateUserKey), but url and userkey could never be supplied
  together -- the userkey vanished with no error. Raised to 4.
- the generator pattern matched uppercase hex only. A lowercase
  generator fell through to the ViewStateUserKey branch, leaving the
  generator at its "0000" default and checking the wrong thing, then
  reporting "No secrets found". Matching is now case-insensitive and
  resolve_args canonicalizes to uppercase so downstream path
  brute-forcing and result strings agree.

Also document the remaining asymmetry, which is a real constraint rather
than a bug: DOTNET45 derives the validation key from the page path via
the SP800-108 KDF purposes, and DOTNET40 with IsolateApps mixes in an
app-path hash, so neither can be validated without the URL. This is the
usual reason --url succeeds where passing the viewstate and generator
alone does not.
Bumps [sqlparse](https://github.com/andialbrecht/sqlparse) from 0.5.5 to 0.6.0.
- [Changelog](https://github.com/andialbrecht/sqlparse/blob/master/CHANGELOG)
- [Commits](andialbrecht/sqlparse@0.5.5...0.6.0)

---
updated-dependencies:
- dependency-name: sqlparse
  dependency-version: 0.6.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
…rse-0.6.0

build(deps): bump sqlparse from 0.5.5 to 0.6.0
…o-5.2.16

build(deps): bump django from 5.2.15 to 5.2.16
…ography-50.0.0

build(deps): bump cryptography from 48.0.1 to 50.0.0
Modules that brute-force top_250000_passwords.txt pay it on every
check_secret() call that gets past identify(). For most that is a cheap
HMAC per candidate, but the JSF viewstate 3DES path derives a SHA1PRNG
key per candidate in pure Python, costing ~13s per call locally and far
more on CI under coverage's tracer. The 3.10 job timed out at 40 minutes
on all three attempts as a result.

Add a trim_wordlist fixture and use it in the affected test files to cut
the list down to the entries each test actually needs. The shipped
wordlists and module behaviour are unchanged.
Bumps [urllib3](https://github.com/urllib3/urllib3) from 2.7.0 to 2.8.0.
- [Release notes](https://github.com/urllib3/urllib3/releases)
- [Changelog](https://github.com/urllib3/urllib3/blob/main/CHANGES.rst)
- [Commits](urllib3/urllib3@2.7.0...2.8.0)

---
updated-dependencies:
- dependency-name: urllib3
  dependency-version: 2.8.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
The autouse `_trim_wordlists` fixture was pasted into seven test files, with
only nextauth_test.py varying the call. Replace the copies with a single
autouse fixture in conftest.py that reads a registered `trim_wordlist`
marker, so opting a file in is one line:

    pytestmark = pytest.mark.trim_wordlist

and a file that has to crack a secret deep in the list passes it as a marker
arg, as nextauth_test.py does with "secret". The marker also accepts
`wordlist=` and `context=`, which pass through to the existing fixture.

The fixture resolves `trim_wordlist` lazily via getfixturevalue, so unmarked
tests never build it and keep the shared resource cache rather than paying a
cache clear on every teardown.
twos_complement() took the signed value of a byte via bin() -> string
replace -> int(str, 2) -> to_bytes -> from_bytes, and updateState()
called it twice per byte. Deriving one 24-byte key ran it 60 times, so a
full sweep of top_250000_passwords.txt made 20M calls and updateState
accounted for 23s of a 40s profile.

Replace it with a 256-entry lookup table of the same values. Output is
byte-identical -- verified exhaustively over all 256 byte values and
against 45 pre-change key vectors -- and updateState drops to 2.4s, with
the JSF 3DES sweep going from 13.3s to 9.6s. The remaining cost is
pycryptodome's DES3 key setup, not our code.

Adds tests pinning the derivation.
Speed up tests by trimming brute-force wordlists
Bumps [django](https://github.com/django/django) from 5.2.16 to 5.2.17.
- [Commits](django/django@5.2.16...5.2.17)

---
updated-dependencies:
- dependency-name: django
  dependency-version: 5.2.17
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps blasthttp from 0.9.0 to 0.10.0.

---
updated-dependencies:
- dependency-name: blasthttp
  dependency-version: 0.10.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [django](https://github.com/django/django) from 5.2.15 to 5.2.17.
- [Commits](django/django@5.2.15...5.2.17)

---
updated-dependencies:
- dependency-name: django
  dependency-version: 5.2.17
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
…lasthttp-0.10.0

build(deps): bump blasthttp from 0.9.0 to 0.10.0
…b3-2.8.0

build(deps): bump urllib3 from 2.7.0 to 2.8.0
…jango-5.2.17

build(deps): bump django from 5.2.16 to 5.2.17
Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.15.1 to 3.16.0.
- [Release notes](https://github.com/pytest-dev/pytest-mock/releases)
- [Changelog](https://github.com/pytest-dev/pytest-mock/blob/main/CHANGELOG.rst)
- [Commits](pytest-dev/pytest-mock@v3.15.1...v3.16.0)

---
updated-dependencies:
- dependency-name: pytest-mock
  dependency-version: 3.16.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
…ytest-mock-3.16.0

build(deps-dev): bump pytest-mock from 3.15.1 to 3.16.0
liquidsec and others added 12 commits October 1, 2026 16:53
…o-5.2.17

build(deps): bump django from 5.2.15 to 5.2.17
fix ASP.NET viewstate CLI argument handling
Two related defects in the JWT module, both rooted in handing pyjwt a
token other than the one that was found.

XMLDSIG: tokens that spell "alg" as a URI are signed over that header,
but swap_algorithm rewrote it to the JWS short name before verification.
That changes the signing input, so a genuine URI-algorithm token could
never be cracked, and the hashcat command we printed was guaranteed to
fail. Teach a private pyjwt instance the URI names and verify the
original bytes instead. The instance is private so the extra names
cannot affect anything else in the process that imports pyjwt.

The existing xmldsig fixture is an HS256 token with its header replaced
and the original signature kept, so it only verifies against the
rewritten form. A URI-spelled header can legitimately be a rewrite of a
token signed under the short name, so that form is still tried, after
the token as issued.

Non-canonical signatures: base64url leaves unused trailing bits, so
several spellings of a signature decode to the same bytes. pyjwt 2.14
rejects all but the canonical one (CVE-2026-102269), which made it
refuse tokens we can still crack, and took the header down with it even
though the header itself was fine. Parse the header directly, and keep
a canonically re-encoded copy for pyjwt alone. Only the signature
segment is touched, so the decoded signature and the signing input are
both unchanged.

Findings and hashcat commands now quote the product exactly as it was
found; the rewritten forms never leave the verification path.

Verified against pyjwt 2.13.0 and 2.15.0.
Bumps [pycryptodome](https://github.com/Legrandin/pycryptodome) from 3.23.0 to 3.24.0.
- [Release notes](https://github.com/Legrandin/pycryptodome/releases)
- [Changelog](https://github.com/Legrandin/pycryptodome/blob/master/Changelog.rst)
- [Commits](Legrandin/pycryptodome@v3.23.0...v3.24.0)

---
updated-dependencies:
- dependency-name: pycryptodome
  dependency-version: 3.24.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [django](https://github.com/django/django) from 5.2.17 to 5.2.18.
- [Commits](django/django@5.2.17...5.2.18)

---
updated-dependencies:
- dependency-name: django
  dependency-version: 5.2.18
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
…ut-and-noncanonical-sigs

Verify JWTs as issued instead of rewriting them
…ycryptodome-3.24.0

build(deps): bump pycryptodome from 3.23.0 to 3.24.0
…jango-5.2.18

build(deps): bump django from 5.2.17 to 5.2.18
Bumps [werkzeug](https://github.com/pallets/werkzeug) from 3.1.8 to 3.1.9.
- [Release notes](https://github.com/pallets/werkzeug/releases)
- [Changelog](https://github.com/pallets/werkzeug/blob/main/CHANGES.rst)
- [Commits](pallets/werkzeug@3.1.8...3.1.9)

---
updated-dependencies:
- dependency-name: werkzeug
  dependency-version: 3.1.9
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
…eug-3.1.9

build(deps): bump werkzeug from 3.1.8 to 3.1.9
@liquidsec
liquidsec merged commit 6603066 into main Oct 14, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants