Someone can erase songs from your neighbor's mixtape, but it's a mixtape at a garage sale nobody attends
CVE-2026-58447 is a Broken Object Level Authorization (BOLA) flaw in Invidious, an open-source, self-hosted alternative frontend to YouTube written in Crystal. The POST /playlist_ajax?action=remove_video endpoint validates that the *requesting user* owns the playlist_id parameter, but then executes DELETE FROM playlist_videos WHERE index = $1 using a globally-scoped integer index pulled from the query string — without confirming that index belongs to the caller's playlist. Any authenticated user can grab a victim's video indexId values from the unauthenticated GET /api/v1/playlists/:plid JSON response, convert the hex value to a decimal Int64, and POST it to permanently delete that entry from the victim's playlist. Affected versions: all Invidious releases through 2.20260626.0. Fixed in commit 77ad416 (PR #5790), which adds AND plid = $2 to the SQL predicate.
The vendor CVSS v3.1 score of 6.5 MEDIUM is technically defensible for the BOLA pattern itself — network-reachable, low-complexity, requires only low-privilege auth, pure integrity impact. But the score wildly over-represents enterprise risk. Invidious is a hobbyist privacy tool with roughly 6 surviving public instances serving ~52,000 total users worldwide and an unknown but very small number of self-hosted instances. The worst-case impact is playlist vandalism — a user's curated video list loses entries, with no notification. There is zero path to system compromise, data exfiltration, privilege escalation, or lateral movement. For a team managing 10,000 enterprise hosts, this CVE is noise.
4 steps from start to impact.
Obtain an account on the target Invidious instance
- Target instance has open registration or attacker already has credentials
- Invidious instance is running a version ≤ 2.20260626.0
- Many instances disable open registration entirely
- Enterprise environments almost never run Invidious — this is hobbyist software
- The 6 surviving public instances serve a combined ~52K users total
Enumerate victim playlist video indexes
GET /api/v1/playlists/:plid — an unauthenticated endpoint that returns full playlist metadata including per-video indexId values in hexadecimal. These index values are globally unique database row identifiers, not scoped to any playlist.- Target playlist must be public or unlisted (not private)
- Attacker must know or discover the victim's playlist ID
- Private playlists are not enumerable via the API
- Playlist IDs follow the
IVPLprefix convention but are not sequentially guessable
/api/v1/playlists/ GET requests from a single session could be logged, but no known scanner or IDS signature exists for this pattern.Convert indexId and craft deletion request
indexId to a decimal Int64 value and constructs a POST /playlist_ajax?action=remove_video request. The request body includes the attacker's *own* playlist_id (which passes the ownership check) and the *victim's* index value as the set_video_id parameter, along with a valid CSRF token from the attacker's session.- Valid CSRF token from attacker's session
- Attacker owns at least one playlist (to supply a passing playlist_id)
- Requires understanding the hex-to-Int64 conversion — documented in the issue report but not trivial for casual users
- No weaponized tooling (e.g., Metasploit module, nuclei template) exists for this CVE
Video deleted from victim's playlist
DELETE FROM playlist_videos WHERE index = $1 with the victim's index value. Because the SQL has no AND plid = $2 clause, the row is deleted regardless of which playlist it belongs to. The victim receives no notification — the video simply disappears from their playlist. The deletion is permanent and not reversible through the UI.- Vulnerable Invidious version is running
- Impact is limited to playlist integrity — no system compromise, no data exfiltration, no privilege escalation
- Deleted content is a *reference* to a YouTube video, not the video itself — the victim can re-add it
playlist_videos table deletion logs in PostgreSQL if log_statement = 'mod' is enabled.registration_enabled: false in config.yml. This prevents new attacker accounts from being created, limiting the attack surface to existing users. Since this is a LOW-severity finding, there is no noisgate mitigation SLA — proceed directly to remediation within 365 days.AND plid = $2 to the playlist video deletion query. Pull the latest master, rebuild the Docker image or binary, and redeploy. No tagged release exists yet, so building from source is required. Per the noisgate remediation SLA for LOW findings, treat this as backlog hygiene with no hard deadline.indexId values from the public playlist API — private playlists are not exposed via GET /api/v1/playlists/:plid. This breaks step 2 of the attack path.- WAF rules — the malicious request is a normal-looking POST to a legitimate endpoint with valid CSRF tokens. No WAF signature can distinguish it from legitimate playlist editing.
- Network segmentation — the attack originates from an authenticated user session within the application. Network-level controls don't help.
- Rate limiting — a single request deletes a single video entry. The attacker doesn't need high request volume.
The supporting signals.
| In-the-wild exploitation | None observed. Not listed in CISA KEV. No mentions in threat intelligence feeds, GreyNoise, or campaign reporting. The target population (~6 public instances, unknown small number of self-hosted) makes mass exploitation economically irrational. |
|---|---|
| Proof-of-concept | De facto PoC exists in the bug report. Issue #5777 by researcher geo-chen contains detailed step-by-step reproduction including the hex-to-Int64 conversion and exact HTTP request format. No standalone exploit repo found on GitHub, no pocindex.io entry, no Nuclei template, no ExploitDB or Metasploit module. |
| EPSS score | 0.00389 (~96th percentile low — only ~0.4% probability of exploitation in the next 30 days). Consistent with a niche open-source project with no weaponized tooling. |
| KEV status | Not listed. No federal agency usage of Invidious is known or plausible. |
| CVSS vectors | v3.1: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N (6.5 Medium) — v4.0: CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N (7.1 High). The I:H rating is generous — 'high integrity impact' here means deleting a playlist entry, not corrupting a database or signing key. |
| Affected versions | All Invidious releases through 2.20260626.0. The project uses date-based versioning (YYYY.MMDD.patch). |
| Fixed version | Commit 77ad416, merged via PR #5790. No tagged release yet — operators must build from master post-commit. |
| Exposure footprint | Minimal. The official instance list shows 6 public HTTPS instances globally with ~52,000 combined registered users. Self-hosted instances are untracked but the project's niche appeal (privacy-focused YouTube proxy) and YouTube's aggressive IP blocking have severely contracted the ecosystem. |
| Disclosure timeline | Reported 2026-06-15 by geo-chen via GitHub issue. Fix merged 2026-06-26. CVE published 2026-06-30. 11-day fix turnaround. |
| Reporting researcher | geo-chen — filed Issue #5777 with full reproduction steps and root-cause analysis pointing to the unscoped SQL DELETE in PlaylistVideos.delete(). |
Sources.
- GitHub Issue #5777 — Original vulnerability report by geo-chen
- GitHub PR #5790 — Fix adding playlist ownership check to SQL DELETE
- GitHub Commit 77ad416 — The actual code fix
- OpenCVE — CVE-2026-58447 detail page
- SentinelOne Vulnerability Database — CVE-2026-58447
- Invidious Official Instance List — current health and user counts
- Invidious Documentation — Self-hosting and configuration
Why this verdict
- Blast radius is trivial. The maximum impact is deletion of playlist video *references* (not the videos themselves) from another user's curated list. There is no path to system compromise, privilege escalation, data exfiltration, lateral movement, or denial of service. The victim can re-add the video. This is vandalism, not a security breach.
- Near-zero enterprise footprint. Invidious is a hobbyist privacy tool for watching YouTube without tracking. It is not enterprise software. No Fortune 500 or mid-market company lists Invidious in their asset inventory. If it appears on your network, it's shadow IT on a developer's Docker host — not a managed service.
- Role multiplier: no high-value role applies. Invidious is not identity infrastructure, not a CA, not a hypervisor, not CI/CD, not a backup system, not a database engine, not a network edge appliance, not a kernel-mode agent, not SIEM, not OT/ICS. It is a consumer-grade web application for watching YouTube videos. 0% of installs occupy any high-value role. The deployment-role floor does not activate.
- Authentication required. The attacker must hold a valid account on the specific Invidious instance. Many instances disable open registration. This is a meaningful friction gate for the ~6 remaining public instances.
- No weaponized tooling. No Metasploit module, no Nuclei template, no standalone exploit script. The PoC requires manual hex-to-Int64 conversion and CSRF token extraction. EPSS is 0.00389 — bottom of the barrel.
- Vendor MEDIUM is a CVSS artifact. The 6.5 score mechanically follows from AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H — but I:H ('high integrity impact') was designed for scenarios like unauthorized database modification or file system tampering, not deleting a line from a playlist. The CVSS framework lacks granularity to express 'trivial data modification in a non-critical application.'
Why not higher?
The vendor MEDIUM (6.5) and the CVSS v4.0 HIGH (7.1) reflect the *mechanical BOLA pattern* — and if Invidious were enterprise software managing regulated data, those scores would be appropriate. But it isn't. The affected asset class has zero enterprise deployment, zero high-value-role overlap, and the impact ceiling is playlist vandalism with no system-level consequences. Upgrading past LOW would misallocate patch engineering time away from actual threats.
Why not lower?
IGNORE would be appropriate only if the software literally cannot appear in a managed environment. It *can* — a developer or privacy-conscious team might self-host an instance, and the BOLA is real and trivially exploitable by any co-user. If you discover Invidious in your fleet, this bug should be patched as backlog hygiene, making LOW the correct floor rather than IGNORE.
Crowdsourced verification payload.
Run this on the host where your Invidious instance is deployed, or any host with network access to the instance's API. Requires curl and jq. Invoke as: bash check_cve_2026_58447.sh https://your-invidious-instance.example.com. No authentication required — the script checks the application version only.
#!/usr/bin/env bash
# CVE-2026-58447 — Invidious BOLA playlist deletion check
# Checks whether the running Invidious instance is vulnerable based on
# the reported software version from the /api/v1/stats endpoint.
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
INSTANCE_URL="${1:-}"
if [[ -z "$INSTANCE_URL" ]]; then
echo "Usage: $0 <invidious-instance-url>"
echo "Example: $0 https://inv.nadeko.net"
exit 2
fi
# Strip trailing slash
INSTANCE_URL="${INSTANCE_URL%/}"
# Fetch version from stats API
RESPONSE=$(curl -sf --max-time 10 "${INSTANCE_URL}/api/v1/stats" 2>/dev/null) || {
echo "UNKNOWN — could not reach ${INSTANCE_URL}/api/v1/stats"
exit 2
}
VERSION=$(echo "$RESPONSE" | jq -r '.software.version // empty' 2>/dev/null)
if [[ -z "$VERSION" ]]; then
echo "UNKNOWN — could not parse version from stats API response"
exit 2
fi
echo "Detected Invidious version: $VERSION"
# Vulnerable versions: <= 2.20260626.0
# Version format: MAJOR.YYYYMMDD.PATCH
# Extract date component
DATE_PART=$(echo "$VERSION" | grep -oP '\d{8}' | head -1)
if [[ -z "$DATE_PART" ]]; then
# Non-standard version string — check for known fix commit hash in build
BRANCH=$(echo "$RESPONSE" | jq -r '.software.branch // empty' 2>/dev/null)
echo "Non-standard version format. Branch: ${BRANCH:-unknown}"
echo "UNKNOWN — cannot determine patch status from version '$VERSION'"
exit 2
fi
DATE_INT=$((10#$DATE_PART))
if [[ $DATE_INT -le 20260626 ]]; then
echo "VULNERABLE — version $VERSION is <= 2.20260626.0 (CVE-2026-58447)"
echo "Fix: rebuild from master branch post-commit 77ad416"
exit 1
else
echo "PATCHED — version $VERSION post-dates the fix (2026-06-26)"
exit 0
fi