Skip to content

Fix Kerberoast BOF crash on wildcard filters and output truncation#6

Open
chryzsh wants to merge 2 commits intooutflanknl:mainfrom
chryzsh:fix/kerberoast-crash-and-truncation
Open

Fix Kerberoast BOF crash on wildcard filters and output truncation#6
chryzsh wants to merge 2 commits intooutflanknl:mainfrom
chryzsh:fix/kerberoast-crash-and-truncation

Conversation

@chryzsh
Copy link

@chryzsh chryzsh commented Feb 26, 2026

Disclaimer

This PR was authored with assistance from an LLM (Claude) and has only been reviewed by that LLM. I discussed this with Dima in the Outflank Slack and we agreed to push it for human review regardless. Please review carefully before merging.

Summary

The Kerberoast BOF crashes the beacon when using wildcard filters that match multiple accounts (e.g. kerberoast roast svc_* against ~28 results). Even when it didn't crash, output was silently truncated for large result sets.

Root Cause

Crash — stale loop state (primary bug): The bResult and bRoast flags were only initialized once before the row iteration loop. When iterating LDAP results, if a user with SPNs was followed by a user without SPNs, bRoast remained TRUE from the previous iteration. This caused the code to attempt Kerberoasting a user whose SPN data was never populated, leading to a NULL pointer dereference and beacon crash.

Crash — NULL stream dereference: BeaconOutputStreamW() could be called after the stream was already released, dereferencing a dangling/invalid pointer. A guard was added to bail out if lpStream is NULL or an invalid sentinel value.

Output truncation: BeaconPrintf has an internal format buffer size limit. When the accumulated IStream content exceeded this limit (common with many tickets), output was silently truncated. This was not immediately obvious because the BOF appeared to succeed.

Changes

  1. Reset bResult/bRoast at the top of each row iteration (Kerberoast.c:304-305) — prevents stale state from a previous row from triggering Kerberoast logic on a user with no SPN data.

  2. Add NULL/sentinel guard in BeaconOutputStreamW() (Kerberoast.c:54-56) — prevents dereference of released or invalid stream pointers.

  3. Chunk the stream flush through BeaconPrintf in 4096-char blocks (Kerberoast.c:66-80) — works around the internal format buffer limit that caused truncation, while keeping BeaconPrintf (which handles wide-char %ls correctly, unlike BeaconOutput which chokes on embedded null bytes in UTF-16).

  4. Fix allocation size: cbSize + sizeof(WCHAR) instead of cbSize + 1 (Kerberoast.c:60) — ensures proper space for a wide-char null terminator rather than a single byte.

  5. Fix bitwise OR | to logical OR || (Kerberoast.c:397) — in the accountExpires attribute comparison. The bitwise OR worked by accident but was a latent bug.

Test Plan

  • Ran kerberoast roast svc_* against a domain with ~28 matching service accounts — previously crashed the beacon, now returns all tickets successfully with no truncation
  • Verify kerberoast list still works correctly
  • Verify kerberoast roast (single target) still works
  • Verify output correctness with AES exclusion filter (/excludeAES)

🤖 Generated with Claude Code

chryzsh and others added 2 commits February 25, 2026 16:38
- Reset bResult/bRoast flags each loop iteration to prevent stale state
  from causing NULL pointer dereference when a user without SPNs follows
  one with SPNs (root cause of beacon crash on svc_* filters)
- Add NULL guard in BeaconOutputStreamW to prevent dereferencing released
  stream pointer
- Replace BeaconPrintf with BeaconOutput for stream flush to avoid
  internal format buffer size limits that caused output truncation
- Fix bitwise OR to logical OR in LargeInteger attribute comparison

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
BeaconOutput sends raw bytes which breaks with UTF-16 wide-char data
(embedded null bytes kill output). Instead, keep BeaconPrintf but
flush the stream in 4096-char chunks to stay within its internal
format buffer limits. Also bump allocation to cbSize + sizeof(WCHAR)
to safely null-terminate the last chunk boundary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant