[HackerNotes Ep. 171] Protobuf XSS, Cookie Case Sensitivity, and Clickjacking Tricks

Solo episode from Justin looking at some recent bugs he found about client side stuff

Hacker TL;DR

  • Protobuf-based XSS: constrain your payload to pure ASCII (0x01–0x7F) and align CRLF sequences with length-prefixed fields to survive browser DOM string mutation

  • CSPT is not a web-only primitive: any component that issues HTTP requests with user-controlled path input (desktop apps, IoT, sockets) is in scope

  • Cookie Path attributes are case-sensitive, but most back-ends treat paths case-insensitively: flip one character to drop a cookie while keeping the session

  • Leaking an "age" field leaks the full date of birth: watch the day it increments and you have the month + day

Today's Sponsor: Check out ThreatLocker Ringfencing https://www.criticalthinkingpodcast.io/tl-rf

Protobuf-Based XSS: Binary Payload Smithing

Justin recently found an XSS on a target that heavily uses Protobuf. The twist: the vulnerability only triggers when sending the raw binary protobuf over the wire, not protojson, not base64-encoded protobuf. That means the payload has to survive a top-level navigation in the browser, delivered through a form submission with Content-Type: text/plain.

Three hard problems surfaced during exploitation.

Challenge 1: DOM String Mutation Mangles Binary Bytes

When raw binary lands in the DOM, the browser applies a silent normalization pass. Any byte outside the pure ASCII range (0x010x7F) risks being rewritten, which destroys the length-prefixed structure of protobuf.

So, you should constrain the entire serialized payload to 0x010x7F. Tweak field sizes by:

  • Inflating or deflating string values

  • Shifting between protobuf wire types when possible

  • Omitting non-required fields entirely

Challenge 2: The Lone LF Problem

Protobuf encodes field number 1 with wire type 2 (length-delimited string) as 0x0A, a bare line feed. Browsers "helpfully" fix orphan LF bytes by prepending a CR, which shifts every downstream byte offset and breaks the entire payload.

The fix: Pad a junk field just before the problematic 0x0A so that it ends in 0x0D. The byte sequence now reads as a legitimate CRLF (0x0D 0x0A), the browser leaves it alone, and the protobuf parser is happy.

... 0x0D | 0x0A <len> <string bytes> ...
  ^^^^^^^^^^^^
  fake CRLF — browser skips the auto-fix

Challenge 3: The Mandatory = in Form Submissions

A text/plain form submission still requires at least one = character to split the key from the value. Stuffing a stray 0x3D into a binary protobuf stream normally corrupts it.

Pad a field so that its declared length byte lands exactly at the position where the = is needed. The length prefix is 0x3D. The protobuf parser reads it as a length; the form parser reads it as the key/value delimiter. Everyone wins.

Challenge 4: The Trailing CRLF Appended by the Browser

Browsers append a CRLF at the end of form-submitted bodies. That extra two bytes breaks protobuf framing.

The solution: Define the final string field with a declared length that is two bytes longer than the payload actually supplied. When the browser appends \r\n, those two bytes get absorbed into the final string field instead of being parsed as extraneous data.

last field declared: string[4]
payload provides:    2 bytes
browser appends:     \r\n (2 bytes)
result: valid 4-byte string, no parser errors

The end result is a working XSS delivered as a raw protobuf-encoded form submission, a beautiful chain of byte-level alignment tricks.

We do subs at $25, $10, and $5, premium subscribers get access to:

Hackalongs: live bug bounty hacking on real programs, VODs available
Live data streams, exploits, tools, scripts & un-redacted bug reports

PII Leak Mindset: Derivative Disclosure

A small disclosure can unlock a much larger one. Justin recently leaked the "age" field of a user on a program. On its own, an age is mild PII. But age changes over time, by monitoring the endpoint and watching for the day the value increments, an attacker recovers the exact date of birth.

When triaging a data-leak bug, ask: how does this value change, and what does the delta reveal? Any monotonically-increasing or threshold-crossing field (age, account tier, loyalty points, subscription days remaining) can be differentiated against time to recover finer-grained private data.

CSPT Beyond the Browser

Client-Side Path Traversal is usually framed as a web vulnerability, but it applies anywhere an HTTP client accepts attacker-controlled path input. Justin hit this on a desktop application recently.

The flow:

  1. The desktop app exposed a WebSocket interface reachable from another machine

  2. A socket message caused the app to issue an HTTP request using user-supplied path input

  3. A path traversal sequence in that input truncated the URL and routed the request to a completely different endpoint

  4. That endpoint delivered meaningful impact

Any component that constructs HTTP requests from untrusted input is a CSPT candidate like desktop apps, IoT firmware, mobile apps, internal services. The attack surface extends far past the browser.

Web hackers often hesitate to pivot into desktop, binary, or embedded targets. CSPT is a rare primitive that transfers cleanly across all of them.

Capital Letters Are Overpowered

A recurring bypass pattern from recent hunting: the case sensitivity mismatch between browsers and back-ends.

The scenario: a specific cookie must be absent for a request to trigger a vulnerability, but the rest of the session cookies must still be present (so the victim is authenticated). A credentialless iframe strips everything, which defeats the purpose.

The solution hinges on a subtle asymmetry:

  • The cookie's Path attribute is case-sensitive per RFC 6265

  • Most back-end HTTP routers treat paths case-insensitively (or normalize them)

If the cookie was set with Path=/abc, sending the request to /Abc (capital A) causes the browser to withhold the cookie while the server still routes the request to the same handler.

Set-Cookie: session=...; Path=/abc

GET /abc  → cookie sent
GET /Abc  → cookie NOT sent, but server hits same endpoint

Whenever you need to strip a single cookie surgically while preserving the rest of the session, test case variations on the path. Encoding tricks (%2F, double-encoding) work too, but plain uppercase is often enough.

Iframe Clickjacking + Control-Click Top Navigation

A chained attack from a recent engagement:

  1. CSRF-set a cookie via an iframe (constrained to iframe delivery)

  2. Render a link inside that iframe

  3. Victim control-clicks the link, opening it in a new tab as a top-level navigation

  4. The top-level navigation completes a login CSRF, leading to account takeover

Ctrl+Click (or Cmd+Click on macOS) from within an iframe is a reliable way to escalate an iframe-constrained interaction into a top-level navigation. It counts as a user gesture and bypasses pop-up blockers.

Bonus: window.open via keydown Event

The user gesture requirement for window.open isn't limited to clicks. A keydown event also satisfies the gesture requirement, which is sometimes easier to obtain than a click: think fake "press any key to continue" prompts or keyboard-navigable cookie banners.

Bonus: Hiding UI Until Ctrl Is Held

To make control-click clickjacking more socially plausible, gate the visible UI behind the control-key state:

  • Listen for keydown/keyup on the Control key

  • Only render the target button when the modifier is held

  • The victim sees a cleaner UI and naturally control-clicks as instructed

Modern AI-assisted UI work makes this kind of PoC polish trivial. No excuse for janky clickjacking demos anymore.

SVG-Enhanced Clickjacking (LRA)

Classic clickjacking suffers from a UX tell: the invisible iframe in front of the visible UI prevents any button depression animation, and attentive users notice. LRA's recent SVG research solves this by building responsive, animated UI overlays using SVG magic, the target UI stays interactive-looking while the invisible iframe collects the click.

Justin flagged this as library material worth building on. Expect more coverage in a dedicated episode.

Resources

That's it for the week, keep hacking!