OliveTin: Unauthenticated DoS via concurrent map writes in OAuth2 state handling
Description
OliveTin gives access to predefined shell commands from a web interface. Prior to version 3000.10.3, an unauthenticated denial-of-service vulnerability exists in OliveTin’s OAuth2 login flow. Concurrent requests to /oauth/login can trigger unsynchronized access to a shared registeredStates map, causing a Go runtime panic (fatal error: concurrent map writes) and process termination. This allows remote attackers to crash the service when OAuth2 is enabled. This issue has been patched in version 3000.10.3.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
An unauthenticated denial-of-service DoS via concurrent map writes in OliveTin's OAuth2 login flow causes Go runtime panic and process termination.
Vulnerability
OliveTin, a web interface for executing predefined shell commands, contains an unauthenticated denial-of-service vulnerability in its OAuth2 login flow prior to version 3000.10.3. The root cause is unsynchronized access to a shared registeredStates map in the OAuth2 handler. When multiple concurrent requests hit the /oauth/login endpoint, Go's runtime map writes occur without proper locking, triggering a Go runtime panic (fatal error: concurrent map writes) and immediate process termination [1][4].
Exploitation
Exploitation
An attacker can exploit this vulnerability by sending a high volume of concurrent HTTP requests to the /oauth/login endpoint while OAuth2 is enabled. No authentication is required, and the attack is network-reachable via the publicly registered route. The advisory includes a proof-of-concept using 80 workers and 120,000 requests to reliably trigger the crash [4. The vulnerability affects all versions prior to the patch, including container images like ghcr.io/olivetin/olivetin:3000.10.0 [4].
Impact
Successful exploitation results in a complete denial of service: the OliveTin process crashes, making the web interface and all shell command execution unavailable until the service is manually restarted. Since the crash is caused by a runtime panic, there is no graceful recovery. The impact is limited to availability; there is no evidence of data corruption or unauthorized access [1][4].
Mitigation
The issue has been patched in OliveTin version 3000.10.3. The fix introduces a sync.RWMutex to protect all reads and writes to the registeredStates map, as shown in the commit f044d90 [2]. Users should upgrade to the latest version immediately. If upgrading is not possible, disabling OAuth2 authentication is a viable workaround, as the vulnerable code path is only reachable when OAuth2 is enabled [1][4].
AI Insight generated on May 18, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/OliveTin/OliveTinGo | < 0.0.0-20260301235225-f044d90d5525c | 0.0.0-20260301235225-f044d90d5525c |
Affected products
2- OliveTin/OliveTinv5Range: < 3000.10.3
Patches
1f044d90d5525security: Remote crash in OAuth2 GHSA-45m3-398w-m2m9 Thanks @kule500 for the responsible disclosure. CVE to follow.
1 file changed · +27 −17
service/internal/auth/otoauth2/restapi_auth_oauth2.go+27 −17 modified@@ -11,6 +11,7 @@ import ( "io" "net/http" "os" + "sync" "time" authTypes "github.com/OliveTin/OliveTin/internal/auth/authpublic" @@ -21,6 +22,7 @@ import ( type OAuth2Handler struct { cfg *config.Config + mu sync.RWMutex registeredStates map[string]*oauth2State registeredProviders map[string]*oauth2.Config } @@ -144,11 +146,13 @@ func (h *OAuth2Handler) HandleOAuthLogin(w http.ResponseWriter, r *http.Request) return } + h.mu.Lock() h.registeredStates[state] = &oauth2State{ providerConfig: provider, providerName: providerName, Username: "", } + h.mu.Unlock() h.setOAuthCallbackCookie(w, r, "olivetin-sid-oauth", state) @@ -177,7 +181,9 @@ func (h *OAuth2Handler) checkOAuthCallbackCookie(w http.ResponseWriter, r *http. return nil, state, false } + h.mu.RLock() registeredState, ok := h.registeredStates[state] + h.mu.RUnlock() if !ok { log.Errorf("State not found in server: %v", state) http.Error(w, "State not found in server", http.StatusBadRequest) @@ -287,8 +293,10 @@ func (h *OAuth2Handler) HandleOAuthCallback(w http.ResponseWriter, r *http.Reque userInfoClient := h.createUserInfoClient(ctx, registeredState.providerConfig, tok, clientSettings) userinfo := getUserInfo(h.cfg, userInfoClient, providerConfig) + h.mu.Lock() h.registeredStates[state].Username = userinfo.Username h.registeredStates[state].Usergroup = h.computeUsergroup(userinfo, providerConfig) + h.mu.Unlock() http.Redirect(w, r, "/", http.StatusFound) } @@ -366,34 +374,36 @@ func getDataField(data map[string]any, field string) string { return stringVal } -func (h *OAuth2Handler) CheckUserFromOAuth2Cookie(context *authTypes.AuthCheckingContext) *authTypes.AuthenticatedUser { - cookie, err := context.Request.Cookie("olivetin-sid-oauth") - - user := &authTypes.AuthenticatedUser{} - - if err != nil { - return nil +func (h *OAuth2Handler) lookupOAuth2UserByState(state string) (*authTypes.AuthenticatedUser, bool) { + h.mu.RLock() + serverState, found := h.registeredStates[state] + if !found { + h.mu.RUnlock() + return nil, false + } + user := &authTypes.AuthenticatedUser{ + Username: serverState.Username, + UsergroupLine: serverState.Usergroup, + Provider: "oauth2", + SID: state, } + h.mu.RUnlock() + return user, true +} - if cookie.Value == "" { +func (h *OAuth2Handler) CheckUserFromOAuth2Cookie(context *authTypes.AuthCheckingContext) *authTypes.AuthenticatedUser { + cookie, err := context.Request.Cookie("olivetin-sid-oauth") + if err != nil || cookie.Value == "" { return nil } - serverState, found := h.registeredStates[cookie.Value] - + user, found := h.lookupOAuth2UserByState(cookie.Value) if !found { log.WithFields(log.Fields{ "sid": cookie.Value, "provider": "oauth2", }).Warnf("Stale session") - return nil } - - user.Username = serverState.Username - user.UsergroupLine = serverState.Usergroup - user.Provider = "oauth2" - user.SID = cookie.Value - return user }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-45m3-398w-m2m9ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-28789ghsaADVISORY
- github.com/OliveTin/OliveTin/commit/f044d90d5525c4c8e3f421b32ed7eff771c22d36ghsax_refsource_MISCWEB
- github.com/OliveTin/OliveTin/security/advisories/GHSA-45m3-398w-m2m9ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.