Low severityNVD Advisory· Published Jul 18, 2025· Updated Jul 18, 2025
Invite token is used as part of the secure communication
CVE-2025-6227
Description
Mattermost versions 10.5.x <= 10.5.7, 9.11.x <= 9.11.16 fail to negotiate a new token when accepting the invite which allows a user that intercepts both invite and password to send synchronization payloads to the server that originally created the invite via the REST API.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/mattermost/mattermost-serverGo | >= 10.5.0, < 10.5.8 | 10.5.8 |
github.com/mattermost/mattermost-serverGo | >= 9.11.0, < 9.11.17 | 9.11.17 |
github.com/mattermost/mattermost/server/v8Go | < 8.0.0-20250612074655-8f8612c63783 | 8.0.0-20250612074655-8f8612c63783 |
Affected products
1- Range: 10.5.0
Patches
1fbf105f6ef8fImproves the invite mechanism for remote clusters (#31025)
5 files changed · +36 −11
server/channels/app/remote_cluster.go+1 −0 modified@@ -202,6 +202,7 @@ func (a *App) CreateRemoteClusterInvite(remoteId, siteURL, token, password strin RemoteId: remoteId, SiteURL: siteURL, Token: token, + Version: 2, } if err := invite.IsValid(); err != nil {
server/platform/services/remotecluster/invitation.go+19 −4 modified@@ -13,13 +13,21 @@ import ( // AcceptInvitation is called when accepting an invitation to connect with a remote cluster. func (rcs *Service) AcceptInvitation(invite *model.RemoteClusterInvite, name string, displayName string, creatorId string, siteURL string, defaultTeamId string) (*model.RemoteCluster, error) { + // Generate new token for RemoteToken only if invite version is 2 or greater + var remoteToken string + if invite.Version >= 2 { + remoteToken = model.NewId() // Generate new token for v2+ protocol + } else { + remoteToken = invite.Token // Use the token from the invite for backwards compatibility + } + rc := &model.RemoteCluster{ RemoteId: invite.RemoteId, Name: name, DisplayName: displayName, DefaultTeamId: defaultTeamId, Token: model.NewId(), - RemoteToken: invite.Token, + RemoteToken: remoteToken, SiteURL: invite.SiteURL, CreatorId: creatorId, } @@ -37,6 +45,11 @@ func (rcs *Service) AcceptInvitation(invite *model.RemoteClusterInvite, name str url := fmt.Sprintf("%s/%s", rcSaved.SiteURL, ConfirmInviteURL) + // for the invite confirm message, we need to use the token that + // the originating server sent in the invite instead of the one + // we're storing as a refresh + rc.RemoteToken = invite.Token + resp, err := rcs.sendFrameToRemote(PingTimeout, rc, frame, url) if err != nil { rcs.server.GetStore().RemoteCluster().Delete(rcSaved.RemoteId) @@ -63,9 +76,11 @@ func (rcs *Service) AcceptInvitation(invite *model.RemoteClusterInvite, name str func makeConfirmFrame(rc *model.RemoteCluster, siteURL string) (*model.RemoteClusterFrame, error) { confirm := model.RemoteClusterInvite{ - RemoteId: rc.RemoteId, - SiteURL: siteURL, - Token: rc.Token, + RemoteId: rc.RemoteId, + SiteURL: siteURL, + Token: rc.Token, + RefreshedToken: rc.RemoteToken, + Version: 2, } confirmRaw, err := json.Marshal(confirm) if err != nil {
server/platform/services/remotecluster/recv.go+5 −0 modified@@ -69,6 +69,11 @@ func (rcs *Service) ReceiveInviteConfirmation(confirm model.RemoteClusterInvite) rc.SiteURL = confirm.SiteURL rc.RemoteToken = confirm.Token + // If the accepting cluster sent a RefreshedToken (its RemoteToken), set it as our Token + if confirm.Version >= 2 && confirm.RefreshedToken != "" { + rc.Token = confirm.RefreshedToken + } + rcUpdated, err := store.Update(rc) if err != nil { return nil, fmt.Errorf("cannot apply invite confirmation for remote %s: %w", confirm.RemoteId, err)
server/public/model/remote_cluster.go+6 −4 modified@@ -363,10 +363,12 @@ type RemoteClusterPing struct { // RemoteClusterInvite represents an invitation to establish a simple trust with a remote cluster. type RemoteClusterInvite struct { - RemoteId string `json:"remote_id"` - RemoteTeamId string `json:"remote_team_id"` // Deprecated: this field is no longer used. It's only kept for backwards compatibility. - SiteURL string `json:"site_url"` - Token string `json:"token"` + RemoteId string `json:"remote_id"` + RemoteTeamId string `json:"remote_team_id"` // Deprecated: this field is no longer used. It's only kept for backwards compatibility. + SiteURL string `json:"site_url"` + Token string `json:"token"` + RefreshedToken string `json:"refreshed_token,omitempty"` // New token generated by the remote cluster when accepting an invitation + Version int `json:"version,omitempty"` } func (rci *RemoteClusterInvite) IsValid() *AppError {
server/public/model/remote_cluster_test.go+5 −3 modified@@ -160,9 +160,11 @@ func TestRemoteClusterInviteEncryption(t *testing.T) { func makeInvite(url string) RemoteClusterInvite { return RemoteClusterInvite{ - RemoteId: NewId(), - SiteURL: url, - Token: NewId(), + RemoteId: NewId(), + SiteURL: url, + Token: NewId(), + RefreshedToken: NewId(), + Version: 2, } }
Vulnerability mechanics
Generated by null/stub 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.