VAITP Dataset

← Back to the dataset

CVE-2026-24489

Gakido allows HTTP header injection via CRLF in header names and values.

  • CVSS 5.3
  • CWE-93
  • Input Validation and Sanitization
  • Remote

Gakido is a Python HTTP client focused on browser impersonation and anti-bot evasion. A vulnerability was discovered in Gakido prior to version 0.1.1 that allowed HTTP header injection through CRLF (Carriage Return Line Feed) sequences in user-supplied header values and names. When making HTTP requests with user-controlled header values containing `\r\n` (CRLF), `\n` (LF), or `\x00` (null byte) characters, an attacker could inject arbitrary HTTP headers into the request. The fix in version 0.1.1 adds a `_sanitize_header()` function that strips `\r`, `\n`, and `\x00` characters from both header names and values before they are included in HTTP requests.

CVSS base score
5.3
Published
2026-01-27
OWASP
A03 Injection
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Input Validation and Sanitization
Subcategory
Command Injection
Accessibility scope
Remote
Impact
Unauthorized Access
Affected component
Gakido
Fixed by upgrading
Yes

Solution

Upgrade Gakido to version 0.1.1 or later.

Vulnerable code sample

import sys
from urllib.parse import urlparse

# This code represents a vulnerable version of the Gakido client (prior to 0.1.1)
# as described in the fictional CVE-2026-24489.
# The vulnerability lies in the lack of sanitization for header names and values,
# allowing for CRLF injection.

class VulnerableGakidoClient:
    """
    A simplified, hypothetical representation of the Gakido HTTP client
    before the fix for CVE-2026-24489 was applied.
    """
    def __init__(self, version="0.1.0"):
        self.version = version

    def _prepare_raw_request(self, method, url, headers=None):
        """
        Prepares the raw HTTP request string. This is where the vulnerability
        is present, as it does not sanitize header values.
        """
        parsed_url = urlparse(url)
        host = parsed_url.netloc
        path = parsed_url.path or "/"

        request_lines = []
        request_lines.append(f"{method.upper()} {path} HTTP/1.1")
        request_lines.append(f"Host: {host}")

        if headers:
            # VULNERABLE PART:
            # The code iterates through user-supplied headers and adds them
            # to the request without stripping special characters like \r, \n, or \x00.
            # An attacker can inject a CRLF sequence (\r\n) followed by new headers.
            for name, value in headers.items():
                request_lines.append(f"{name}: {value}")

        # The final double CRLF signifies the end of the headers.
        raw_request = "\r\n".join(request_lines) + "\r\n\r\n"
        return raw_request.encode('utf-8')

    def get(self, url, headers=None):
        """
        Simulates making a GET request and prints the raw request that would be sent.
        """
        print("--- Raw HTTP Request (as sent by the vulnerable client) ---")
        
        # In a real client, this raw request would be sent over a socket.
        # Here we just print it to demonstrate the injection.
        raw_request = self._prepare_raw_request("GET", url, headers)
        sys.stdout.buffer.write(raw_request)
        sys.stdout.flush()
        print("\n-----------------------------------------------------------")


if __name__ == '__main__':
    # --- Demonstration of the Vulnerability ---

    # 1. Attacker crafts a malicious header value.
    # The value for 'User-Agent' contains a Carriage Return Line Feed (\r\n),
    # which is used to terminate the current header and start a new, injected one.
    malicious_headers = {
        "Accept": "text/html",
        "User-Agent": (
            "Normal-Gakido-Client/0.1.0\r\n"
            "Injected-Header: This header was injected by an attacker!\r\n"
            "Another-Injected-Header: So was this one."
        )
    }

    # 2. The application uses the vulnerable client with the user-controlled headers.
    client = VulnerableGakidoClient()
    target_url = "http://example.com/login"

    print("Demonstrating CVE-2026-24489: HTTP Header Injection")
    print(f"Target URL: {target_url}")
    print("Malicious Headers to be sent:")
    print(malicious_headers)
    print("\n")

    # 3. The client constructs and "sends" the request.
    # The output will show that 'Injected-Header' and 'Another-Injected-Header'
    # have been successfully added to the outgoing HTTP request, even though
    # they were not part of the original headers dictionary's keys.
    client.get(target_url, headers=malicious_headers)

Patched code sample

import collections.abc

class GakidoFixedClient:
    """
    A mock HTTP client class to demonstrate the fix for CVE-2026-24489.
    This class includes the sanitization logic described in the vulnerability report.
    """

    def _sanitize_header(self, value: str) -> str:
        """
        Strips characters that could lead to HTTP header injection.
        This function represents the core fix for the vulnerability.
        It removes carriage returns, line feeds, and null bytes.
        """
        # Ensure the value is a string before performing replacements
        str_value = str(value)
        
        # Sequentially replace forbidden characters with an empty string
        sanitized_value = str_value.replace('\r', '').replace('\n', '').replace('\x00', '')
        
        return sanitized_value

    def prepare_request_headers(self, headers: collections.abc.Mapping) -> dict:
        """
        Prepares headers for an HTTP request by sanitizing both names and values.
        """
        sanitized_headers = {}
        if headers:
            for name, value in headers.items():
                # Apply the sanitization to both the header name and its value
                sanitized_name = self._sanitize_header(name)
                sanitized_value = self._sanitize_header(value)
                
                # Only add the header if the name is not empty after sanitization
                if sanitized_name:
                    sanitized_headers[sanitized_name] = sanitized_value
        return sanitized_headers

# --- Demonstration ---

if __name__ == "__main__":
    # 1. Instantiate the client with the fix
    client = GakidoFixedClient()

    # 2. Define a malicious payload attempting to inject a new header
    #    The `\r\n` sequence is used to terminate the current header and start a new one.
    #    A null byte `\x00` is also included in a header name.
    malicious_headers = {
        'User-Agent': 'Legitimate-Agent/1.0',
        'X-User-ID': '12345\r\nInjected-Header: InjectedValue',
        'Accept\x00-Language': 'en-US'
    }

    print("--- Demonstrating Fix for Header Injection ---")
    
    print("\nOriginal (malicious) headers provided by user:")
    for name, value in malicious_headers.items():
        # Use repr() to make special characters like \r, \n, \x00 visible
        print(f"  {repr(name)}: {repr(value)}")

    # 3. Process the headers using the fixed method
    sanitized_headers = client.prepare_request_headers(malicious_headers)

    print("\nSanitized (fixed) headers after processing:")
    for name, value in sanitized_headers.items():
        print(f"  {repr(name)}: {repr(value)}")

    print("\nResult: The CRLF sequence and null byte were successfully stripped, preventing header injection.")

Payload

import gakido

client = gakido.Gakido()
target_url = "http://example.com"

malicious_headers = {
    "User-Agent": "NormalBrowser/1.0\r\nX-Injected-Header: MaliciousValue"
}

client.get(target_url, headers=malicious_headers)

Cite this entry

@misc{vaitp:cve202624489,
  title        = {{Gakido allows HTTP header injection via CRLF in header names and values.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-24489},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-24489/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::