Remote cluster PATCH response leaked authentication tokens (CVE-2026-7184)
Remote-cluster PATCH returned the updated model before clearing token fields
CVSS 3.1 base score
MediumVector: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Summary
Source
patchRemoteCluster()
server/channels/api4/remote_cluster.go:638
Sink
patchRemoteCluster()
server/channels/api4/remote_cluster.go:666
Mattermost's remote-cluster API already treated token and remote_token as sensitive fields: list, create, accept, and get handlers called RemoteCluster.Sanitize() before serializing objects to clients. The PATCH handler was the missed trust boundary. After decoding a caller-controlled RemoteClusterPatch and applying it through App.PatchRemoteCluster, patchRemoteCluster() wrote updatedRC directly to the HTTP response without sanitizing it first.
The public advisory MMSA-2026-00662 / CVE-2026-7184 describes this as a Medium CWE-201 issue affecting Mattermost 11.6.x <= 11.6.1, 11.5.x <= 11.5.4, and 10.11.x <= 10.11.15, with fixed versions 11.7.0, 11.6.2, 11.5.5, and 10.11.17. The original fix is PR #36288 / commit bd8fc92226726da06c8fabaef568cc9ebaee1cb8; release-line backports include #36310, #36311, and #36313.
Severity
- Attack vectorAV
- Network
- Attack complexityAC
- Low
- Privileges requiredPR
- Low
- User interactionUI
- None
- ScopeS
- Unchanged
- ConfidentialityC
- High
- IntegrityI
- None
- AvailabilityA
- None
Metric values as published in the disclosure vector. Meters show how far each value raises exposure.
Source-to-sink trace
- Source · attacker-controlledserver/
channels/ api4/ remote_cluster.go:638 patchRemoteCluster()
- Step 01server/
channels/ api4/ remote_cluster.go:638-642 An authenticated caller supplies the PATCH body. The handler decodes that body into a
RemoteClusterPatch; changing a harmless editable field such asdisplay_nameis enough to reach the vulnerable response path.go var patch model.RemoteClusterPatch if jsonErr := json.NewDecoder(r.Body).Decode(&patch); jsonErr != nil { c.SetInvalidParamWithErr("remotecluster", jsonErr) return } - Step 02server/
channels/ api4/ remote_cluster.go:623-626 The endpoint is intentionally reachable to users with
manage_secure_connections. That role can manage secure connections but is not equivalent to full system-administrator access to remote-cluster secrets.go func patchRemoteCluster(c *Context, w http.ResponseWriter, r *http.Request) { c.RequirePermissionToManageSecureConnections() if c.Err != nil { return } - Step 03server/
channels/ api4/ remote_cluster.go:657-661 App.PatchRemoteClusterreturns the updated storedRemoteClusterobject. That object still contains the trust credentials used by the remote-cluster relationship.go updatedRC, err := c.App.PatchRemoteCluster(c.Params.RemoteId, &patch) if err != nil { c.Err = err return } - Step 04server/
public/ model/ remote_cluster.go:67-74 The model exposes both secret fields as JSON fields and provides
Sanitize()specifically to blank them before response serialization.go Token string `json:"token"` RemoteToken string `json:"remote_token"` func (rc *RemoteCluster) Sanitize() { rc.Token = "" rc.RemoteToken = "" } - Step 05server/
channels/ api4/ remote_cluster.go:663-666 (before fix) Before PR #36288, the handler added the unsanitized object to the audit result and encoded the same object directly to the client, exposing
tokenandremote_tokenin the PATCH response.go auditRec.Success() auditRec.AddEventResultState(updatedRC) if err := json.NewEncoder(w).Encode(updatedRC); err != nil { c.Logger.Warn("Error while writing response", mlog.Err(err)) } - Step 06server/
channels/ api4/ remote_cluster.go:660-668 (fix, commit bd8fc92) The accepted upstream fix sanitizes the updated object before both audit result capture and JSON encoding. Regression coverage now checks that PATCH responses return empty
TokenandRemoteTokenvalues.go updatedRC.Sanitize() auditRec.Success() auditRec.AddEventResultState(updatedRC) if err := json.NewEncoder(w).Encode(updatedRC); err != nil { c.Logger.Warn("Error while writing response", mlog.Err(err)) } - Sinkserver/
channels/ api4/ remote_cluster.go:666 patchRemoteCluster()
Impact
Reported impact
The direct impact is disclosure of remote-cluster authentication material that should remain server-side. In deployments where those values are accepted by token-authenticated remote-cluster endpoints, the leaked credentials can let the caller impersonate a trusted remote-cluster peer or abuse remote-cluster trust flows.
Attack surface
Mattermost deployments with remote-cluster support enabled and at least one existing remote-cluster record. The vulnerable route is PATCH /api/v4/remotecluster/{remote_id} and requires an authenticated session with manage_secure_connections.
Preconditions
The attacker must have the secure-connection management permission and know a valid remote cluster ID. No user interaction is required, and the PATCH body can change an ordinary editable field such as display_name.
Attack path
- 1
Authenticate as a user with
manage_secure_connections. - 2
Send
PATCH /api/v4/remotecluster/{remote_id}with a valid patch body. - 3
Read the JSON response body.
- 4
Extract
tokenandremote_tokenfrom the unsanitized response.
Proof of concept
- 01
Environment setup
Use a vulnerable Mattermost build before PR #36288 with remote-cluster support enabled. Authenticate as a user that has
manage_secure_connections, and identify an existing remote cluster ID through the admin UI or a permitted API path. - 02
Target configuration
Set
MM_URLto the Mattermost base URL andRC_IDto an existing remote cluster ID. Use a session cookie or bearer token for a secure-connection manager. - 03
Exploit delivery
Send a benign PATCH request and inspect the returned object:
bash curl -sk \ -H "Authorization: Bearer $MM_TOKEN" \ -H 'Content-Type: application/json' \ -X PATCH "$MM_URL/api/v4/remotecluster/$RC_ID" \ --data '{"display_name":"leak-check"}' | jq '.token, .remote_token' - 04
Expected response
Vulnerable builds return non-empty
tokenand/orremote_tokenvalues. Fixed builds return those fields as empty strings, matching the behavior of the other remote-cluster response paths. - 05
Outcome
A permitted secure-connection manager can read remote-cluster trust secrets from the PATCH response on vulnerable builds. PR #36288 prevents the disclosure by sanitizing before serialization.
Remediation
Guidance
Sanitize every remote-cluster object before it crosses an API response or audit boundary. A dedicated public response DTO would further reduce the chance that future handlers accidentally serialize secret-bearing model fields.
updatedRC, err := c.App.PatchRemoteCluster(c.Params.RemoteId, &patch)if err != nil {c.Err = errreturn}auditRec.Success()auditRec.AddEventResultState(updatedRC)if err := json.NewEncoder(w).Encode(updatedRC); err != nil {c.Logger.Warn("Error while writing response", mlog.Err(err))}updatedRC, err := c.App.PatchRemoteCluster(c.Params.RemoteId, &patch)if err != nil { c.Err = err return} updatedRC.Sanitize() auditRec.Success()auditRec.AddEventResultState(updatedRC) if err := json.NewEncoder(w).Encode(updatedRC); err != nil { c.Logger.Warn("Error while writing response", mlog.Err(err))}
Check the upstream record for the project's current remediation status.
Investigate the paths that matter in your codebase.
Winfunc can trace relevant code paths, preserve supporting evidence, and prepare remediation suggestions for engineering review within an agreed scope.
