# Auth bypass via lossy charset or unescape

Auth comparisons after lossy charset conversion or custom unescape that collapse distinct values

_Area: Web authentication and account takeover. Seen in 2 disclosed reports; latest 2026-10-06. Updated 2026-11-01._

## How it works

Authentication compares a secret or identity to a stored value. That comparison is only sound if the bytes compared are a faithful, one-to-one encoding of what the user supplied. Two transformations break that: a lossy charset conversion, and a custom unescape applied after routing or matching. In both cases distinct inputs become the same bytes, so a wrong credential matches a stored one, or a rejected token is re-read as something else.

## Where it shows up

- Hashing or encoding credentials with a single-byte charset (ISO-8859-1, ASCII) where input may contain characters above the charset's range [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-2fvj-hgj9-j2gr.md).
- Custom unescape/replace functions (`::` to `/`, `%2e`, backslash folding) applied *after* route or path matching [E1](https://bugbountylibrary.com/r/blog%3Alabs-watchtowr-com-you-wont-hear-about-these-even-in-myths-atlassian-jira-confluence-and-more-pre-auth-arbitrary-file-re.md).
- Deserializer loops with a `skip`/`goto` branch that frees a rejected key but keeps parsing the same buffer [E2](https://bugbountylibrary.com/r/h1%3A159946.md).
- Any place a value is normalized, then compared, then used — order matters.

## How to review

The invariant: **the bytes used for the security comparison must be an injective (loss-free) function of the user input, and normalization must happen before, not after, the check.**

- For every `getBytes(charset)` / `encode()` feeding a hash or compare, ask: does this charset map every possible input character to a distinct byte? If not, it is lossy and unsafe for secrets [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-2fvj-hgj9-j2gr.md).
- For every unescape/replace, confirm it runs *before* route matching and authorization, not after [E1](https://bugbountylibrary.com/r/blog%3Alabs-watchtowr-com-you-wont-hear-about-these-even-in-myths-atlassian-jira-confluence-and-more-pre-auth-arbitrary-file-re.md).
- For deserializer skip branches, confirm the skip advances past the value bytes, not just the key [E2](https://bugbountylibrary.com/r/h1%3A159946.md).
- Test with multi-language and boundary characters, not just ASCII.

## How finders spotted it

- A WAF regex in an advisory matched an unusual token (`::`); grepping the codebase found the unescape replacing it with `/`, then tracing routes to the file sink confirmed traversal after matching [E1](https://bugbountylibrary.com/r/blog%3Alabs-watchtowr-com-you-wont-hear-about-these-even-in-myths-atlassian-jira-confluence-and-more-pre-auth-arbitrary-file-re.md).
- Reading the session decode loop: the `goto skip` path freed the name and advanced `p = q` but did not stop parsing, leaving value bytes to be re-parsed as a new pair [E2](https://bugbountylibrary.com/r/h1%3A159946.md).
- Reading `DigestAuthentication.apply()`: `getBytes(ISO_8859_1)` at three hash sites, and knowing Java silently maps out-of-range chars to `0x3F`, prompted a collision test [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-2fvj-hgj9-j2gr.md).

## Variants seen

- Lossy charset collapse of password bytes to a fixed filler byte [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-2fvj-hgj9-j2gr.md).
- Custom unescape turning a benign token into a traversal sequence after route matching [E1](https://bugbountylibrary.com/r/blog%3Alabs-watchtowr-com-you-wont-hear-about-these-even-in-myths-atlassian-jira-confluence-and-more-pre-auth-arbitrary-file-re.md).
- Skip-branch in a deserializer that continues parsing rejected input [E2](https://bugbountylibrary.com/r/h1%3A159946.md).

## Impact and severity

E3 is a direct authentication bypass: any user with a non-Latin-1 password can be impersonated by a colliding password [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-2fvj-hgj9-j2gr.md). E1 is pre-auth arbitrary file read that chains to Crowd SSO credential theft and admin creation, raising severity through the chain and its reach across Jira, Confluence and Bitbucket [E1](https://bugbountylibrary.com/r/blog%3Alabs-watchtowr-com-you-wont-hear-about-these-even-in-myths-atlassian-jira-confluence-and-more-pre-auth-arbitrary-file-re.md). E2 chains to object injection and use-after-free [E2](https://bugbountylibrary.com/r/h1%3A159946.md). Impact rises when the collapsed value feeds a privileged sink or a credential store.

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Alabs-watchtowr-com-you-wont-hear-about-these-even-in-myths-atlassian-jira-confluence-and-more-pre-auth-arbitrary-file-re.md
- E2: https://bugbountylibrary.com/r/h1%3A159946.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-2fvj-hgj9-j2gr.md
