close
Skip to content

Auth2 SSO Callback Server Is a Global Singleton, Race Condition & Port Hijack #312

@rootp1

Description

@rootp1

Describe the bug

The OAuth2 SSO Callback Server in the CLI (used during login --sso) acts as a global singleton due to its reliance on http.DefaultServeMux. This leads to race conditions and potential port hijacking vulnerabilities.

In cmd/login.go:268-270 (approximate location):

srv := &http.Server{Addr: "localhost:" + strconv.Itoa(port)}
http.HandleFunc("/auth/callback", callbackHandler)

This logic instantiates an http.Server but does not supply a dedicated Handler, meaning it falls back to the process-global http.DefaultServeMux. http.HandleFunc modifies this shared router, and this route is never cleaned up.

The critical impacts include:

  1. Port Hijacking / Token Exposure: If a malicious process running locally on a multi-tenant machine binds to port 58085 first, the CLI's temporary HTTP server will fail to start (log.Fatalf), but the callback/authorization details from the user's browser may be successfully routed to the attacker. While PKCE prevents the attacker from directly exchanging the authorization code (as the code_verifier is in the memory of the CLI process), the id_token (passed as r.FormValue("id_token") in the fallback implicit flow) might be extracted directly by the attacking server.
  2. Global Mux Pollution & Race Conditions: The URL handler /auth/callback persists permanently inside the process's default HTTP mux. If login is called twice (e.g., in a CI script, or if the first login fails midway), the subsequent calls overwrite the first handler. http.HandleFunc is not completely goroutine-safe when dynamically modifying the mux while a server handles requests. It can incorrectly interleave or fail across consecutive executions in the same process footprint.

Expected behavior

  1. A locally isolated http.ServeMux should be used per server instance instead of http.DefaultServeMux. For example:
    mux := http.NewServeMux()
    mux.HandleFunc("/auth/callback", callbackHandler)
    srv := &http.Server{
        Addr: "localhost:" + strconv.Itoa(port),
        Handler: mux,
    }
  2. The port binding error logic should be explicit: if the expected local port is already bound by another process, login() should abort safely and alert the user immediately, rather than quietly failing inside a goroutine or leaving the user vulnerable to authorization route hijacking.

Actual behavior

The http.HandleFunc manipulates the global http.DefaultServeMux. The CLI process could leak handlers or have subsequent authentication operations overwrite global states over one another.

How to Reproduce?

  1. Open a separate terminal and mock an attacker process by listening on port 58085:
    nc -l -p 58085
  2. Run the microcks-cli login --sso command.
  3. Observe that the OAuth authentication flow continues locally.
  4. When the browser redirects to http://localhost:58085/auth/callback..., the attacker nc instance intercepts the HTTP request, collecting the query parameters (which may contain code or id_token depending on the SSO provider mapping configuration).

Alternatively, inspecting the source code validates the reliance on http.DefaultServeMux:

  1. Find srv := &http.Server{Addr: "localhost:" + strconv.Itoa(port)} inside cmd/login.go.
  2. Notice that http.HandleFunc is being used, making /auth/callback globally registered on http.DefaultServeMux.

Microcks version or git rev

microcks-cli2 (Current main/issue-branch code base as of reported date)

Install method (docker-compose, helm chart, operator, docker-desktop extension,...)

Compiled from source (go build)

Additional information

This issue affects multi-tenant environments where CI/CD setups or shared developer workstations might have concurrent or conflicting listeners. Fixing the HTTP handler scoping provides a resilient boundary.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions