A pickpocket who can only rearrange the contents of other shoppers' carts, and only if they're already in the store
Invidious is a self-hosted, privacy-preserving frontend for YouTube written in Crystal (Kemal framework, Postgres backend). The bug is a classic IDOR (CWE-639) in the remove_video action on the playlist endpoint: the code accepted an attacker-supplied global video *index* without checking that the playlist actually belonged to the caller. Combined with the fact that per-video indices are exposed by the public playlist JSON API, any logged-in user of the same instance could permanently delete videos from *other users'* playlists. Affects Invidious ≤ 2.20260626.0; fixed in commit 77ad416 on the master branch.
Vendor pegged this at CVSS 6.5 MEDIUM with C:N/I:H/A:N. The integrity call is fair — data belonging to another user is mutated without authorization — but the deployment reality drags the operational severity down further. Invidious is not typically an enterprise application; it is deployed by hobbyists, privacy-focused communities, and small self-hosters. The affected data is playlist entries, not credentials, code, or business records. In practitioner terms this is LOW: real, worth fixing, but nowhere near patch-cycle-jumping territory.
4 steps from start to impact.
Register or sign in on the target Invidious instance
- Instance allows account registration OR the attacker already holds any user account
- Network reach to the instance (usually public HTTPS)
- Locked-down private instances disable registration
- Corporate/family instances gate accounts behind SSO
users tableEnumerate a target playlist and harvest video indices
/api/v1/playlists/<plid>) to pull the ordered list of videos. Each entry carries an internal index value — a Postgres bigint used as the row key in the playlist_videos table. These indices are globally unique across playlists, which is the design flaw that enables the IDOR.- Target playlist is queryable (public, unlisted with known ID, or discoverable via search)
- Purely private playlists are not enumerable without their plid
- Rate-limited instances slow bulk enumeration
POST the harvested index to remove_video on any playlist you own
/playlist_ajax / remove_video action) supplying the *victim's* video index. The vulnerable code path resolved the row by index alone and issued a DELETE FROM playlist_videos WHERE index = ? without joining against the caller's plid/owner. No public weaponized tool exists — this is a two-line curl/Python job the attacker writes themselves.- Valid session cookie / CSRF token for the attacker's own account
- Fixed instances (commit 77ad416+) reject the request
- Reverse proxies with per-IP write throttling limit scale
remove_video where the caller's user_id does not match the affected playlist's owner — trivially auditable in Postgres post-factoImpact: silent, permanent deletion of other users' playlist contents
- Postgres PITR / pg_dump backups restore state if the operator takes them
The supporting signals.
| In-the-wild exploitation | None observed. Not on CISA KEV; no public campaigns, no honeypot hits reported by GreyNoise or Shodan. |
|---|---|
| Public PoC | No standalone PoC repo. The fix diff at commit 77ad416 effectively documents the vulnerable code path — weaponization is trivial for anyone reading it. |
| EPSS | 0.00225 (~0.2%) — bottom-quartile probability of exploitation in the next 30 days. |
| KEV status | Not listed. |
| CVSS 3.1 vector | AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N → network reach, low complexity, authenticated (PR:L), no user interaction, integrity-only impact. The PR:L and C:N/A:N are the ceiling-setters. |
| Affected versions | Invidious ≤ v2.20260626.0 (all releases prior to the fix commit). |
| Fixed version | Master branch commit 77ad416; expected in the next tagged release after v2.20260626.0. No distro backports — Invidious is not packaged in Debian/RHEL stable channels. |
| Exposure population | The public Invidious instance list tracks ~40–60 public instances globally. Shodan/Censys queries for Server: Kemal + Invidious title fingerprints return a few hundred hosts including private ones. Not an enterprise-scale attack surface. |
| Disclosed | 2026-06-30, fix committed same day. |
| Credit | Reported to the iv-org maintainers; CVE assigned via MITRE. Individual reporter not publicly credited in the CVE record at time of writing. |
noisgate verdict.
The single decisive factor is blast radius limited to per-user playlist rows on a niche self-hosted YouTube frontend — no credentials, no code execution, no cross-tenant data, and the affected component is not present on typical enterprise fleets. Authenticated prerequisite (PR:L) plus integrity-only impact on non-critical user data keeps this firmly in the backlog-hygiene bucket.
Why this verdict
- Authenticated precondition (PR:L): attacker must hold a valid account on the same instance — implies either open registration or prior social engineering / account takeover. Removes drive-by exploitation.
- Integrity-only impact (C:N/I:H/A:N): no credential theft, no RCE, no denial of service, no data exfiltration. Damage is bounded to playlist row deletions, which are recoverable from any Postgres backup.
- Deployment-role audit — no high-value role exists for Invidious: it is a personal / community media frontend. It is not identity, not PKI, not backup, not hypervisor, not CI/CD, not an edge appliance. The role-multiplier floor rule does not raise the verdict because no realistic deployment role turns this bug into fleet, domain, or supply-chain compromise.
- Exposure population is tiny in enterprise terms: dozens of public instances, low hundreds of private ones. Sub-1% of the Fortune 5000 runs Invidious anywhere.
- No in-the-wild activity, no KEV, EPSS 0.2%. No opportunistic mass exploitation signal.
- Vendor 6.5 baseline is reasonable for an individual instance operator; downgrade to LOW reflects enterprise portfolio prioritization, not disagreement with the technical scoring.
Why not higher?
MEDIUM would imply the bug warrants scheduled remediation ahead of routine backlog. It does not — the impact is user-visible annoyance recoverable from Postgres backups, with no lateral movement or credential exposure. HIGH/CRITICAL are inapplicable: no code execution, no auth bypass, no cross-tenant escalation, no high-value-role component.
Why not lower?
IGNORE is not appropriate because the bug is real, trivially exploitable once the patch diff is public, and permanently destroys user data. Operators of public Invidious instances do need to upgrade; LOW correctly signals 'fix in the next window' rather than 'do nothing'.
What to do — in priority order.
- Disable new-user registration on public instances until patched — Cuts off the primary attacker acquisition path (PR:L becomes unreachable). Set
registration_enabled: falseinconfig.ymland restart the container. As a LOW-severity finding under the noisgate mitigation SLA there is no mandatory mitigation deadline, but for public-facing instances treat this as a same-week hygiene step. - Snapshot the Postgres
playlistsandplaylist_videostables before patch and daily thereafter — Because the bug destroys rows with no soft-delete, backups are your only recovery. Runpg_dump -t playlists -t playlist_videoson cron; retain 30 days. Do this before you deploy 77ad416 so you have a known-good baseline. - Add a reverse-proxy rule that logs every POST to
/playlist_ajaxwith the session user ID — Gives you post-facto attribution and lets you tell affected users what was deleted. Ten lines of nginxlog_formatplus a Loki/ELK query onaction=remove_videowhere source user differs from playlist owner. - Pull master or wait for the next tagged release, then redeploy the container — The fix is a single commit —
git pull && docker compose build && docker compose up -d. Under the noisgate remediation SLA for LOW you have up to 365 days, but there is no reason to wait: the delta is trivial and the fix does not change data schemas.
- WAF signatures — the request looks like a normal authenticated playlist edit; there is no malicious payload to match on, only a semantic ownership violation.
- MFA — the attacker uses a legitimate MFA-completed session of *their own* account. Second factor does nothing to stop the IDOR.
- Rate limiting alone — a patient attacker can iterate at whatever rate the limiter permits and still hit every playlist eventually.
Crowdsourced verification payload.
Run this on the Invidious host itself (the container host or the VM running the Crystal binary). It inspects the deployed git revision. Invoke as ./check-invidious-cve-2026-58447.sh /opt/invidious where the argument is the Invidious source directory. Requires read access to that directory; no root needed.
#!/usr/bin/env bash
# noisgate verifier — CVE-2026-58447 (Invidious IDOR in playlist remove_video)
# Output: VULNERABLE / PATCHED / UNKNOWN
set -u
INV_DIR="${1:-/opt/invidious}"
FIX_COMMIT="77ad416"
LAST_VULN_TAG="v2.20260626.0"
if [[ ! -d "$INV_DIR/.git" ]]; then
echo "UNKNOWN: $INV_DIR is not a git checkout of iv-org/invidious"
exit 2
fi
cd "$INV_DIR" || { echo "UNKNOWN: cannot cd to $INV_DIR"; exit 2; }
# Fetch tags/commits quietly so we can resolve the fix ref
git fetch --quiet --tags origin 2>/dev/null || true
# 1. Does the checked-out HEAD contain the fix commit in its history?
if git merge-base --is-ancestor "$FIX_COMMIT" HEAD 2>/dev/null; then
echo "PATCHED: HEAD ($(git rev-parse --short HEAD)) contains fix commit $FIX_COMMIT"
exit 0
fi
# 2. Fallback — check the tagged release string in the source
CURRENT_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "unknown")
if [[ "$CURRENT_TAG" == "$LAST_VULN_TAG" || "$CURRENT_TAG" < "$LAST_VULN_TAG" ]]; then
echo "VULNERABLE: release $CURRENT_TAG is at or before $LAST_VULN_TAG and fix commit $FIX_COMMIT is not in history"
exit 1
fi
echo "UNKNOWN: could not conclusively determine patch status (tag=$CURRENT_TAG, HEAD=$(git rev-parse --short HEAD))"
exit 2
If you remember one thing.
playlist_videos Postgres table today, and roll master (or the next tag past v2.20260626.0) containing commit 77ad416 at your next maintenance window — well inside the noisgate remediation SLA ceiling of 365 days. If you don't run Invidious, close the ticket with a one-line 'not in inventory' note and move on.Sources
- Invidious release v2.20260626.0 (last vulnerable tag)
- iv-org/invidious repository
- Invidious official documentation — Instances
- Invidious project site
- OWASP API1:2023 Broken Object Level Authorization
- CWE-639: Authorization Bypass Through User-Controlled Key
- CISA Known Exploited Vulnerabilities Catalog
- FIRST EPSS model
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.