Описание
OliveTin: Unauthenticated DoS via OAuth2 State Memory Exhaustion (Unbounded Map Growth)
Summary
OliveTin's OAuth2 login handler stores per-login state in an in-memory map (registeredStates) that grows unboundedly. States are added on every /oauth/login request but are never deleted or expired. An unauthenticated attacker can send millions of requests to /oauth/login to fill the map with state entries, exhausting server memory and causing a denial of service.
This is distinct from CVE-2026-28789 (concurrent map writes crash). That CVE was about the panic from unsynchronized map access — the fix added a sync.RWMutex. This vulnerability is about the unbounded growth of the map even WITH the mutex, as no cleanup mechanism exists.
Affected Versions
- All versions with OAuth2 support, including >= 3000.10.3 (which patched CVE-2026-28789)
Details
In service/internal/auth/otoauth2/restapi_auth_oauth2.go:
The HandleOAuthLogin handler adds a new state on every request:
The HandleOAuthCallback handler updates existing states but never removes them:
There is no TTL, no expiry check, no periodic cleanup, and no max size limit on registeredStates.
Memory Impact Per State
Each map entry consists of:
- Key: ~24 bytes (base64 string)
- Value:
*oauth2Statestruct containing:providerConfig *oauth2.Config(pointer, 8 bytes + shared config)providerName string(~8-16 bytes)Username string(empty initially)Usergroup string(empty initially)
- Go map overhead: ~100-150 bytes per entry
Estimated: ~200 bytes per state entry
At 1 million states ≈ 200 MB of memory consumed. At 10 million states ≈ 2 GB of memory consumed.
Attack Vector
The /oauth/login endpoint is publicly accessible (unauthenticated). Each request is lightweight (no heavy computation like argon2). The server writes a cookie and returns a 302 redirect. An attacker can send thousands of requests per second.
PoC
Prerequisites
- OliveTin instance with at least one OAuth2 provider configured
- Network access to
/oauth/login
Config
Step 1: Baseline health check
Step 2: Flood with state-creation requests
Step 3: Python PoC for sustained memory exhaustion
Step 4: Verify memory growth (Docker)
Impact
- Who is impacted: All OliveTin deployments with any OAuth2 provider configured
- Attack requirements: Unauthenticated network access to
/oauth/login - Effect: Gradual memory exhaustion leading to OOM kill or service degradation
- Persistence: Memory is never reclaimed (states are never deleted) even after the attack stops — a restart is required
- Distinction from CVE-2026-28789: That CVE was a race condition crash (concurrent map writes). This is unbounded memory growth that persists even with the mutex fix applied.
Suggested Fix
- Add a TTL to OAuth2 states (e.g., 15 minutes matching the cookie
MaxAge) - Add a maximum state count (e.g., 10,000) with LRU eviction
- Clean up states after successful callback
- Add periodic garbage collection for expired states
Пакеты
github.com/OliveTin/OliveTin
>= 0.0.0-20251024001301-45f9c18bc3ee, < 0.0.0-20260708075951-ec114e95d297
0.0.0-20260708075951-ec114e95d297
Связанные уязвимости
OliveTin gives access to predefined shell commands from a web interface. From 3000.0.0 until 3000.17.0, the service/internal/auth/otoauth2/restapi_auth_oauth2.go OAuth2 login handler stores per-login state in the registeredStates map on every /oauth/login request without expiring, deleting, or bounding entries, allowing an unauthenticated attacker to exhaust memory and cause a denial of service. This issue is fixed in version 3000.17.0.