Skip to content

telerik: overhaul, native RAU + DialogHandler crypto - #3333

Merged
liquidsec merged 10 commits into
devfrom
telerik-overhaul
Sep 21, 2026
Merged

liquidsec merged 10 commits into
devfrom
telerik-overhaul

Conversation

@liquidsec

Copy link
Copy Markdown
Collaborator

Summary

Rewrite of the telerik module.

  • Removed the rau_crypto shell-out; AES-CBC done natively.
  • Safe RAU detection via fake 9999.9.999 assembly version, flags exploitable RAU without writing a file. Opt-in rau_confirm_version walks candidate versions and uploads a 1-byte file on match for CRITICAL.
  • Incorporated badsecrets telerik_knownkey for known-key spray on DialogHandler (PBKDF1_MS + PBKDF2).
  • Ported the byte-oracle detection from dp_cryptomg (find_baseline, 81-combo ?dp=... probe) as a fallback when known-key spray misses.
  • DH version identification via response-size delta; RAU-confirmed version threads through to DH findings when DH's own probe misses.
  • dotnet-audit preset opts into rau_confirm_version + include_subdirs.

liquidsec added 10 commits July 21, 2026 20:48
Refactor into per-technique async methods, replace RAU_crypto.py
subprocess loop with native badsecrets-primitive payload builder.

Add CVE-2017-9248 oracle probe (dp_cryptomg quick_check port) and
DialogHandler known-key spray (PBKDF1_MS + PBKDF2 via badsecrets
keylists). Standardize FINDING name values on CVE + handler.

Config: exploit_rau (renamed from exploit_RAU_crypto), try_known_keys,
probe_dialoghandler_oracle, include_machinekeys.

Deps: drop deps_ansible + pycryptodome, add badsecrets.
Replace KDF-fingerprint shortcut for the DialogHandler oracle probe with
the actual dp_cryptomg find_baseline algorithm: iterate the 3^4 byte
combinations of [0x00, 0x6b, 0x08] as GET ?dp=<b64> payloads and read
for 'Index was outside the bounds of the array' / 'String was not
recognized as a valid Boolean'. On hit, emit CRITICAL/CONFIRMED.

Reorder DH exploit: try known-key spray first (definitive, recovers the
actual keys); only fall back to the byte-oracle baseline probe if no
key matches. Oracle probe is gated to PBKDF1_MS mode since the
CVE-2017-9248 patch moved to PBKDF2 specifically to defeat it.

DialogHandler discovery tries the default path (Telerik.Web.UI.
DialogHandler.aspx at root) sequentially first; the handler is global
and this produces a less confusing finding URL. Only fan out to the
extended candidate list if the default misses.
Detect CVE-2017-11317 exploitability without uploading a file. Send
default-keys payloads targeting a fake assembly version (9999.9.999);
if the server responds with 'Could not load file or assembly', crypto
succeeded and .NET rejected the bogus version before any file write.
Emit HIGH/HIGH 'Telerik RAU Default Keys Accepted' finding.

Try each (KDF, HMAC) combination pre-patch targets can use: PBKDF1_MS
no-HMAC (pre-2017), PBKDF1_MS with HMAC (2017.1.118 - 2018.1.117),
PBKDF2 with HMAC (2018.2+). First hit exits.

exploit_rau=True still runs the full version iteration for the
CRITICAL/CONFIRMED RCE finding; version identification and RCE
confirmation are the same operation.

Fix: version fingerprint used to send version 2014.3.1024, a real
Telerik version. On a target actually running that version, that was
an accidental file upload during 'default' scanning.
The safe fake-version probe already detects RAU exploitability by
default; this opt-in specifically identifies the exact installed
version by uploading a benign 1-byte file. Old name misrepresented
its role. Description now warns explicitly about the file write.
Safe RAU probe now sprays the full badsecrets keylist (not just the
two hardcoded defaults), split into Default vs Known findings by key
source. Version list picks up badsecrets' patched set + undotted
variants; adopts quoted Version="X" and lowercase C:\\windows\\temp\\
per badsecrets. custom_secrets wired through. rau_confirm_version
defers HIGH until version-check runs — CRITICAL alone on match,
HIGH fallback (with attempted-but-failed wording) on miss. Verbose
step messages throughout.

dotnet-audit preset uses rau_confirm_version.
Post-patch DialogHandlers validate input length before touching
crypto, so the short AAAA fingerprint probe returns 'Invalid length
for a Base-64' regardless of the actual KDF. Real known-key probes
are long enough to pass that gate, so spray PBKDF1_MS then PBKDF2
when the classifier can't tell. Catches weak-custom-key configs on
hardened targets that would otherwise fall through with no attempt.
badsecrets sees the same string and silently keeps its
initial PBKDF1_MS default; it's an input-length gate, not
a patch signal. Returning None makes the exploit path try
both KDFs (my prior change), which is strictly more
coverage than badsecrets' PBKDF1_MS-only fallback.
dp_cryptomg does no KDF classification before find_baseline —
it just tries the byte oracle and reads response strings. My
PBKDF1_MS-only gate skipped legitimate oracle-vulnerable
targets whose short KDF-fingerprint probe was rejected by an
input-length gate (kdf_mode = None). Only skip when the
classifier explicitly reported PBKDF2 (where the CVE-2017-9248
patch fixed the oracle by design).
Add DialogHandler version detection using the recovered
hash/enc keys (safe, size-diff probe, mirrors badsecrets'
DialogHandler.probe_version). Emit follows the RAU pattern:
CRITICAL/CONFIRMED with version, HIGH/HIGH fallback when
target's version isn't in the candidate list.

Thread RAU-confirmed version through to DH oracle + known-key
findings. Both handlers back onto the same Telerik.Web.UI
install, so an RAU-confirmed version fills in the DH side
when DH can't identify it directly (strong custom DH keys).

Also fix DH known-key test mock to model version detection
correctly (assembly type string is base64-encoded inside
the plaintext) and assert CRITICAL + version in the finding.
_rau_confirmed set added nothing on top of _incoming_dedup_hash: URL
events dedupe per-netloc (default) or per-normalized-URL
(include_subdirs), and either way the same base_url can't reach
_probe_rau twice.

Also: rename stale exploit_rau -> rau_confirm_version in a test message
and add missing f-string prefix on another.
@github-actions

Copy link
Copy Markdown
Contributor

📊 Performance Benchmark Report

Comparing dev (baseline) vs telerik-overhaul (current)

📈 Detailed Results (All Benchmarks)

📋 Complete results for all benchmarks - includes both significant and insignificant changes

🧪 Test Name 📏 Base 📏 Current 📈 Change 🎯 Status
Bloom Filter Dns Mutation Tracking Performance 4.32ms 4.33ms +0.2%
Bloom Filter Large Scale Dns Brute Force 17.89ms 17.84ms -0.3%
Large Closest Match Lookup 340.00ms 337.01ms -0.9%
Realistic Closest Match Workload 182.47ms 181.31ms -0.6%
Event Memory Medium Scan 1403 B/event 1402 B/event -0.1%
Event Memory Large Scan 1527 B/event 1527 B/event +0.0%
Event Validation Full Scan Startup Small Batch 401.81ms 402.12ms +0.1%
Event Validation Full Scan Startup Large Batch 510.54ms 510.61ms +0.0%
Make Event Autodetection Small 20.20ms 20.16ms -0.2%
Make Event Autodetection Large 205.13ms 203.52ms -0.8%
Make Event Explicit Types 8.40ms 8.50ms +1.2%
Excavate Single Thread Small 3.636s 3.640s +0.1%
Excavate Single Thread Large 9.291s 9.223s -0.7%
Excavate Parallel Tasks Small 3.876s 3.839s -0.9%
Excavate Parallel Tasks Large 5.862s 5.822s -0.7%
Intercept Throughput Small 885.44ms 878.92ms -0.7%
Intercept Throughput Medium 868.22ms 869.30ms +0.1%
Dns Throughput Quiet 2.547s 2.561s +0.5%
Dns Throughput Loaded 1.738s 1.708s -1.7%
Dns Throughput Inherited 2.264s 2.293s +1.3%
Is Ip Performance 2.29ms 2.39ms +4.5%
Make Ip Type Performance 202.97µs 213.23µs +5.1%
Mixed Ip Operations 2.32ms 2.41ms +4.0%
Memory Use Web Crawl 1.0 MB 1.0 MB +1.0%
Memory Use Subdomain Enum 28.3 MB 28.3 MB +0.0%
Memory Use Deep Chain 5.0 MB 5.0 MB +0.0%
Memory Use Parallel Chains 11.7 MB 10.2 MB -12.7% 🟢🟢 🚀
Scan Throughput 100 2.661s 2.569s -3.5%
Scan Throughput 1000 17.954s 17.794s -0.9%
Typical Queue Shuffle 5.50µs 5.45µs -0.9%
Priority Queue Shuffle 27.11µs 26.97µs -0.5%

🎯 Performance Summary

+ 1 improvement 🚀
  30 unchanged ✅

🔍 Significant Changes (>10%)

  • Memory Use Parallel Chains: 12.7% 🚀 less memory

🐍 Python Version 3.11.15

@codecov

codecov Bot commented Jul 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.11111% with 65 lines in your changes missing coverage. Please review.
✅ Project coverage is 90%. Comparing base (fab03be) to head (979d116).
⚠️ Report is 91 commits behind head on dev.

Files with missing lines Patch % Lines
bbot/modules/telerik.py 84% 54 Missing ⚠️
...st/test_step_2/module_tests/test_module_telerik.py 92% 11 Missing ⚠️
Additional details and impacted files
@@          Coverage Diff           @@
##             dev   #3333    +/-   ##
======================================
- Coverage     90%     90%    -0%     
======================================
  Files        450     450            
  Lines      46308   46656   +348     
======================================
+ Hits       41565   41862   +297     
- Misses      4743    4794    +51     

☔ 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.

@liquidsec liquidsec self-assigned this Sep 3, 2026

@singlerider singlerider left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First pass on the rewrite at 979d116. Dropping the RAU_crypto shell-out is a real win: it removes a git-clone build dep and a subprocess per probe. CI green across 3.10 to 3.14 and all seven distros. I verified the crypto and the RAU probe posture rather than trusting the diff, findings below.

🟢 Native RAU crypto reproduces the shell-out exactly

This was the part most likely to silently break, so I checked it against the reference the shell-out used. _rau_encrypt's c + "\x00" interleave plus chr(pad)*pad is UTF-16LE + PKCS7, and it matches base64(AES-CBC(pad(pt.encode("utf-16-le"), 16))) byte for byte with the PBKDF1_MS-derived key and IV. The default RAU keys are the documented CVE-2017-11317 constants, and the KDF derivation is delegated to badsecrets, not reimplemented. This is correct.

One nit, not blocking: c + "\x00" equals UTF-16LE only for code points below U+0100. The only plaintext through this path is the hardcoded ASCII temp path, so it is fine today, but if a non-ASCII folder ever gets fed in the ciphertext will be silently wrong rather than raising. pt.encode("utf-16-le") would be the same result for the current input and immune to that. Your call.

🟢 The file-upload path is gated correctly

rau_confirm_version defaults False, it only runs after the safe fake-version key spray (_probe_rau_known_keys) has already proven the keys with a 9999.9.999 assembly that fails to load before any write, and the WARNING in the config text names the 1-byte write to C:\\windows\\temp\\. Emitting a HIGH keys-accepted finding when confirmation is disabled, and only escalating to CRITICAL/CONFIRMED on an actual fileInfo response, is the right severity ladder. A destructive confirmation being opt-in and gated behind a non-destructive proof is exactly the posture I want to see here.

The CRITICAL RAU RCE emitting as a FINDING rather than a VULNERABILITY matches what the old module did at the removed block, so this preserves convention rather than introducing drift. Not raising it.

🟢 Oracle fallback and known-key spray are ordered sensibly

Known-key spray first (definitive, recovers the actual keys), byte-oracle only as a fallback when the spray misses, and the oracle gated on PBKDF1_MS since that is the only mode it works against. probe_dialoghandler_oracle defaulting True is reasonable for a non-destructive probe. The version list dedupes via dict.fromkeys and the undotted-variant rationale is documented.

Approving. The one crypto nit is a latent edge, not a blocker.

@liquidsec
liquidsec merged commit 07e6eba into dev Sep 21, 2026
19 checks passed
@liquidsec
liquidsec deleted the telerik-overhaul branch September 21, 2026 19:21
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.

2 participants