CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-26331
8.8

yt-dlp: Downloading Shells Instead of Videos via --netrc-cmd

Amit Schendel
Amit Schendel
Senior Security Researcher

Feb 24, 2026·6 min read·10 visits

PoC Available

Executive Summary (TL;DR)

If you use `yt-dlp` with the `--netrc-cmd` option to handle credentials, you are vulnerable to RCE. Attackers can embed shell commands in URLs (e.g., subdomains) which get passed unsanitized to your system shell. Update to version 2026.02.21 immediately.

A high-severity OS command injection vulnerability in yt-dlp allows attackers to execute arbitrary code via crafted URLs. By exploiting the `--netrc-cmd` feature and permissive hostname extraction logic, a malicious link can turn a video download into a full system compromise.

The Hook: When Features Bite Back

We all love yt-dlp. It is the Swiss Army knife of media archiving, capable of ripping video from the darkest corners of the internet with a single command. It’s a tool built by hackers, for hackers (and data hoarders). But like any complex tool with a million switches, some of those switches are connected to dynamite. Enter --netrc-cmd.

This specific flag is designed for the power users—the ones who don't just want to put their credentials in a static .netrc file. It allows you to specify a shell command that yt-dlp will execute to fetch credentials dynamically. You give it a template, like pass show /internet/{}, and yt-dlp replaces {} with the machine name (hostname) of the site you're downloading from.

Sounds convenient, right? It is, until you realize that "machine name" isn't always just youtube.com. For certain extractors, that machine name is derived directly from the URL you fed the tool. If you're letting the internet dictate the arguments to your shell commands without a bouncer at the door, you're asking for a bad time. And in CVE-2026-26331, the bouncer wasn't just on break; he didn't exist.

The Flaw: Trusting the Untrustable

The core of this vulnerability lies in a classic misunderstanding of trust boundaries. The developers assumed that a hostname or a "machine identifier" extracted from a URL would be a benign string—something alphanumeric like vimeo or pornhub. For the vast majority of extractors, this is hardcoded. But for a select few—specifically GetCourseRuIE, TeachableIE, TeachableCourseIE, and PornHubIE—the extraction logic was dynamic.

These extractors pulled the "machine" identifier directly from the URL using regular expressions that were far too permissive. For example, they might look for anything that isn't a slash. This means a URL like https://;rm -rf /;@example.com could technically parse validly in terms of structure, but the "machine" part becomes ;rm -rf /;.

Here is the fatal error: yt-dlp takes this dirty input, stuffs it into your command template (replacing {}), and then hands the whole mess over to Python's subprocess.Popen with shell=True. Using shell=True is the programming equivalent of driving blindfolded. It tells the OS to interpret the string as a full shell command, metacharacters and all. The semicolon isn't just part of a string anymore; it's a command separator. The game is over.

The Code: Autopsy of a Shell Injection

Let's look at the "smoking gun." The vulnerability lived in how the netrc_machine variable was handled before being passed to the shell. Prior to the fix, the code essentially did this:

# Vulnerable Logic (Pseudocode)
cmd = self.params['netrc_cmd'].replace('{}', netrc_machine)
# The fatal flaw: shell=True
subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, ...)

If netrc_machine contained ; calc.exe, the resulting command sent to the OS would be echo ; calc.exe. The shell sees an empty echo, finishes it, and then happily executes calc.exe.

The fix, implemented in commit 1fbbe29b99dc61375bf6d786f824d9fcf6ea9c1a, applies a strict whitelist approach. Instead of trying to play whack-a-mole with dangerous characters, the developers defined exactly what a valid machine name looks like.

# The Fix (yt_dlp/extractor/common.py)
ALLOWED = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_'
 
# ... inside _get_netrc_login_info ...
if netrc_machine.startswith(('-', '_')) or not all(c in ALLOWED for c in netrc_machine):
    raise ExtractorError(f'Invalid netrc machine: {netrc_machine!r}', expected=True)

Notice two things here: First, the ALLOWED string limits characters to alphanumerics, dots, dashes, and underscores. No semicolons, no ampersands, no backticks. Second, they explicitly ban strings starting with - or _. This prevents Argument Injection, where an attacker could theoretically pass a flag (like --output /etc/passwd) to the password manager command even if they couldn't execute arbitrary code.

The Exploit: Popping Shells via URL

Let's construct a realistic attack scenario. You are a security researcher (or a miscreant) targeting a user known to use yt-dlp with the --netrc-cmd flag. Perhaps they have a cron job that downloads videos from a list, or they use a wrapper script.

The Setup: The victim runs yt-dlp with a command to fetch passwords: yt-dlp --netrc-cmd "echo Password for {}" [URL]

The Payload: You craft a malicious URL targeting the GetCourseRu extractor, which is vulnerable to this injection. You want to run id.

https://;id;#.getcourse.ru/video

The Execution Flow:

  1. yt-dlp parses the URL. The regex captures ;id;# as the machine identifier.
  2. It substitutes this into the command: echo Password for ;id;#.
  3. The shell executes:
    • echo Password for (prints text)
    • ; (separator)
    • id (EXECUTES THE PAYLOAD)
    • ;# (comments out the rest of the line)

> [!WARNING] > The Redirect Trap: You don't even need the user to paste the ugly URL directly. yt-dlp follows redirects. You can send a link to https://tinyurl.com/cute-cat-video. yt-dlp resolves it, gets redirected to your malicious getcourse.ru payload, and triggers the RCE automatically. This makes the attack vector significantly wider, effectively turning any processed link into a potential bomb.

The Impact: Why You Should Care

This is a High Severity (8.8) vulnerability for a reason. While it requires the user to enable a specific flag (--netrc-cmd), the consequences are catastrophic. We aren't talking about a crash or a memory leak; we are talking about arbitrary code execution.

If this runs on a developer's laptop, the attacker has access to SSH keys, source code, and browser cookies. If this runs on a server (e.g., a Discord bot that downloads videos or a web service wrapping yt-dlp), the attacker now owns that server. They can pivot into the internal network, install persistence, or exfiltrate data.

The fact that yt-dlp is often used in automated pipelines makes this particularly dangerous. A single malicious entry in a download queue could compromise the entire pipeline. The barrier to entry is low—just a crafted string—and the impact is total compromise of the user context running the application.

The Fix: Remediation Strategy

The remediation is straightforward: Update yt-dlp immediately.

The patch was released in version 2026.02.21. If you are running anything older (specifically between 2023.06.21 and 2026.02.21), you are exposed.

If you cannot update for some reason (perhaps you enjoy living dangerously or are stuck in corporate bureaucracy hell), you must audit your usage:

  1. Disable --netrc-cmd: Do not use this flag. Use a static .netrc file or standard credential passing mechanisms.
  2. Sanitize Configurations: Check your yt-dlp.conf files. If you see --netrc-cmd with {} in it, comment it out.

But really, just run yt-dlp -U. It takes five seconds and saves you from being the punchline of a security conference talk.

Official Patches

GitHub (yt-dlp)Official Release 2026.02.21
GitHub (yt-dlp)GitHub Security Advisory

Fix Analysis (1)

Technical Appendix

CVSS Score
8.8/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Affected Systems

yt-dlp (Python)Video downloading automation pipelinesDiscord bots using yt-dlpWeb services wrapping yt-dlp

Affected Versions Detail

Product
Affected Versions
Fixed Version
yt-dlp
yt-dlp
>= 2023.06.21, < 2026.02.212026.02.21
AttributeDetail
CWECWE-78 (OS Command Injection)
CVSS8.8 (High)
Attack VectorNetwork (via malicious URL)
ConstraintRequires --netrc-cmd usage
Exploit StatusProof-of-Concept Available
PatchStrict Alphanumeric Whitelist

MITRE ATT&CK Mapping

T1059Command and Scripting Interpreter
Execution
T1204.001User Execution: Malicious Link
Execution
T1566Phishing
Initial Access
CWE-78
OS Command Injection

Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')

Known Exploits & Detection

GitHubProof of Concept demonstrating command injection via GetCourseRu extractor

Vulnerability Timeline

Fix committed to master
2026-02-14
yt-dlp version 2026.02.21 released
2026-02-21
GHSA Advisory Published
2026-02-23
CVE-2026-26331 Published
2026-02-24

References & Sources

  • [1]NVD Entry
  • [2]GHSA Advisory

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.