unicode-normalization-bypass · git:20260504.2a64374 · 2026-05-04 · sha256 43865eee55ec2ce1

unicode-normalization-bypass git:20260504.2a64374B

Immutable. This exact content is served forever at /api/v1/blob/43865eee55ec2ce1.

---
name: unicode-normalization-bypass
description: Bypass WAFs and input filters using Unicode normalization equivalents (NFKC/NFKD). Use when WAF blocks payloads but application normalizes Unicode before processing.
---

# Unicode Normalization Bypass

## Pattern
- WAF blocking standard payloads but backend normalizes Unicode post-filter
- Application accepts internationalized/multi-script input
- Fullwidth, mathematical, or decorative Unicode characters accepted
- Length-based restrictions with case mapping (Turkish locale edge cases)

## Probe
**Substitutions** (WAF sees Unicode, app normalizes to ASCII):
- `<` → `\uff1c` (fullwidth `<`) | `>` → `\uff1e` (fullwidth `>`)
- `'` → `\uff07` (fullwidth) or `\u02bc` (modifier letter)
- `/` → `\u2215` (division slash) or `\u2044` (fraction slash)
- `"` → `\uff02` (fullwidth) | `.` → `\uff0e` (fullwidth)
- `0-9` → `\uff10-\uff19` (fullwidth digits)
- Script variants: `\U0001d4e2` (mathematical bold `s`), circled letters

**Detection test**: submit `\U0001d543\u2147\U0001d664\U0001d4c3\u2148\U0001d530\U0001d525\U0001d4b6\U0001d65f` — if response reflects "Leonishan", normalization is active.

**NFKC normalization** is most common (Kubernetes, Python `.casefold()`, Java `Normalizer`).

**Unicode surrogates → wildcard injection** (Solr/Elasticsearch):
- Lone surrogates like `\udc2a` are invalid UTF-8 and cannot be displayed
- Systems convert them to U+FFFD replacement character (`�`)
- Some backends further simplify `�` to ASCII `?`
- `?` is a single-char wildcard in Solr, Elasticsearch, and SQL LIKE
- Result: inject surrogate → bypass character filter → wildcard query execution → data leak
```
Input:     admin\udc2a      (filter allows it — not a blocked char)
Backend:   admin�           (replacement char)
Simplified: admin?          (wildcard — matches admin1, admin2, adminX, ...)
```
Test on search/filter endpoints backed by Solr/Elasticsearch: submit `\udc2a` and check if results broaden.

## Indicators
- Response reflects normalized (ASCII) version of Unicode input
- Payload bypasses WAF but executes on backend (XSS, SSTI, SQLi, path traversal)
- Character count changes after normalization (compatibility decomposition)

## Double Encoding + Unicode
Stack URL encoding with Unicode for multi-layer bypass:
```
# Filter decodes URL once, then checks for <script> — but Unicode survives
%ef%bc%9c%ef%bd%93%ef%bd%83%ef%bd%92%ef%bd%89%ef%bd%90%ef%bd%94%ef%bc%9e
# Double-encode the Unicode bytes — filter decodes to UTF-8 bytes, backend decodes to fullwidth chars
%25ef%25bc%259c  →  decode once: %ef%bc%9c  →  decode again: < (fullwidth <)
# Triple for two-decode-layer stacks
%2525ef%2525bc%25259c
```
Test when WAF decodes URL encoding before Unicode normalization — each layer peels one encoding level.

## Chain With
- ssti-error-based-detection (bypass WAF to reach template engine)
- apache-confusion-attacks (combine encodings with path confusion)
- orm-filter-data-leak (surrogate wildcard injection on search/filter endpoints)

## Reference
- https://i.blackhat.com/BH-USA-25/Presentations/USA-25-Barnett-Lost-In-Translation-Exploiting-Unicode-compressed.pdf
- https://lab.ctbb.show/research/unicode-surrogates-conversion (surrogates → wildcard)