CVE-2023-28609
Description
api/auth.go in Ansible Semaphore before 2.8.89 mishandles authentication.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Ansible Semaphore before 2.8.89 has an authentication bypass in api/auth.go because the authentication handler does not return a boolean to indicate failure, allowing unauthorized access.
The vulnerability resides in api/auth.go where the authenticationHandler function did not return a boolean value to indicate whether authentication succeeded. Instead, it only wrote HTTP status codes and returned without a value, meaning the caller could not distinguish between success and failure. This allowed requests to proceed even after authentication failures.
An attacker can exploit this by sending requests with invalid or missing authentication tokens. The server would write an unauthorized status but continue processing the request because the handler's return value was ignored. No special network position is required; the attacker only needs to be able to send HTTP requests to the Semaphore API.
Successful exploitation allows an attacker to bypass authentication entirely, gaining unauthorized access to Semaphore's API endpoints. This could lead to reading sensitive data, executing tasks (e.g., Ansible playbooks), or modifying configurations, depending on the permissions of the unauthenticated context.
The issue was fixed in version 2.8.89 by changing the function signature to return a boolean and ensuring that the caller checks this value before proceeding [1][4]. Users should upgrade to at least this version. No workarounds are documented.
AI Insight generated on May 20, 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/ansible-semaphore/semaphoreGo | < 2.8.89 | 2.8.89 |
Affected products
2- Ansible/Semaphoredescription
Patches
13e4a62b7f2b1fix: authentization bug
2 files changed · +21 −22
api/auth.go+21 −14 modified@@ -11,7 +11,7 @@ import ( "time" ) -func authenticationHandler(w http.ResponseWriter, r *http.Request) { +func authenticationHandler(w http.ResponseWriter, r *http.Request) bool { var userID int authHeader := strings.ToLower(r.Header.Get("authorization")) @@ -25,7 +25,7 @@ func authenticationHandler(w http.ResponseWriter, r *http.Request) { } w.WriteHeader(http.StatusUnauthorized) - return + return false } userID = token.UserID @@ -34,20 +34,20 @@ func authenticationHandler(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("semaphore") if err != nil { w.WriteHeader(http.StatusUnauthorized) - return + return false } value := make(map[string]interface{}) if err = util.Cookie.Decode("semaphore", cookie.Value, &value); err != nil { w.WriteHeader(http.StatusUnauthorized) - return + return false } user, ok := value["user"] sessionVal, okSession := value["session"] if !ok || !okSession { w.WriteHeader(http.StatusUnauthorized) - return + return false } userID = user.(int) @@ -58,7 +58,7 @@ func authenticationHandler(w http.ResponseWriter, r *http.Request) { if err != nil { w.WriteHeader(http.StatusUnauthorized) - return + return false } if time.Since(session.LastActive).Hours() > 7*24 { @@ -70,13 +70,13 @@ func authenticationHandler(w http.ResponseWriter, r *http.Request) { } w.WriteHeader(http.StatusUnauthorized) - return + return false } if err := helpers.Store(r).TouchSession(userID, sessionID); err != nil { log.Error(err) w.WriteHeader(http.StatusUnauthorized) - return + return false } } @@ -87,26 +87,29 @@ func authenticationHandler(w http.ResponseWriter, r *http.Request) { log.Error(err) } w.WriteHeader(http.StatusUnauthorized) - return + return false } if util.Config.DemoMode { if !user.Admin && r.Method != "GET" && !strings.HasSuffix(r.URL.Path, "/tasks") && !strings.HasSuffix(r.URL.Path, "/stop") { w.WriteHeader(http.StatusUnauthorized) - return + return false } } context.Set(r, "user", &user) + return true } // nolint: gocyclo func authentication(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - authenticationHandler(w, r) - next.ServeHTTP(w, r) + ok := authenticationHandler(w, r) + if ok { + next.ServeHTTP(w, r) + } }) } @@ -115,10 +118,14 @@ func authenticationWithStore(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { store := helpers.Store(r) + var ok bool + db.StoreSession(store, r.URL.String(), func() { - authenticationHandler(w, r) + ok = authenticationHandler(w, r) }) - next.ServeHTTP(w, r) + if ok { + next.ServeHTTP(w, r) + } }) }
api/router.go+0 −8 modified@@ -348,16 +348,8 @@ func servePublic(w http.ResponseWriter, r *http.Request) { } func getSystemInfo(w http.ResponseWriter, r *http.Request) { - //updateAvailable, err := util.CheckUpdate() - - //if err != nil { - // helpers.WriteError(w, err) - // return - //} - body := map[string]interface{}{ "version": util.Version, - //"update": updateAvailable, "ansible": util.AnsibleVersion(), "demo": util.Config.DemoMode, }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.