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.
- CWE
- CWE-93
- 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 ::
