Torrent enclosures write with overwrite enabled straight into a folder other feeds share, so one publisher can replace another feed's downloaded episodes #331
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found by the security-audit skill (run-1, 2026-10-20). Source-read only: nothing was run, so this is a lead that still needs its local check, not a confirmed finding. Two independent verifiers did not disprove it.
Torrents::fetch adds every torrent with output_folder = dest_dir and overwrite: true (torrent.rs:64-67). Because output_folder is given, librqbit 9.0.1 uses it as is and adds no per-torrent subfolder (session.rs:1285-1291). With allow_overwrite, its filesystem storage opens each torrent file at output_folder/ with create(true), truncate(false), read(true), write(true) (storage/filesystem/fs.rs:141-148), the initializing phase resizes it to the torrent's length (initializing.rs:275) and checks the existing pieces, and the pieces that do not match are downloaded and written over the existing bytes. dest_dir is download_dir/folder_for(...) (main.rs:2333-2334): under the default organize = feed it is the sanitised feed title unless a folder override is set, and the title comes from the publisher's feed XML, so a publisher (or a household user adding their own feed) can give a feed the same title as another; under organize = date it is one MM-DD-YYYY folder shared by every feed that day. A torrent whose file names equal another feed's downloaded episode therefore rewrites that episode's bytes in place, and other subscribers then play the replaced content. HTTP downloads are protected by unique_path (download.rs:257-271); torrents have nothing like it. The recorded path of the attacker's torrent is dest_dir.join(name) (torrent.rs:106-107), the same file, so deleting or reaping the attacker's episode would also remove the victim's file. Torrents are enabled by default (config.rs:224). Single-file and per-file torrent paths cannot traverse, so this stays within download_dir; the separate multi-file info.name issue is a different root cause.
Claimed root cause. torrent.rs:66 sets overwrite: true and torrent.rs:65 gives each torrent the shared feed folder as its output folder with no subfolder of its own, while folder_for (download.rs:278-291) lets different feeds resolve to the same directory. Nothing makes a torrent's files unique or refuses to open an existing file the way unique_path does for HTTP downloads.
Local check. {'local': "In a sandboxed scratch daemon with loopback only and a scratch download_dir: feed A (HTTP enclosure, title 'Show') downloads victim.mp3 into download_dir/Show; record its SHA-256. Feed B, a second fixture feed also titled 'Show', offers a single-file torrent named victim.mp3 with different bytes, seeded by a local librqbit or another client on loopback. Download B's enclosure, wait for the torrent to finish, and compare the SHA-256 of download_dir/Show/victim.mp3 with A's original and check A's and B's enclosure rows point at the same path. A changed hash confirms it. Regression test: a torrent whose files would land on an existing file is refused or given its own subfolder (e.g. sub-directory per enclosure, or overwrite only when resuming the same info hash).", 'deployment': 'Owner-observed and non-destructive: check whether production sets organize = date or has two subscribed feeds whose sanitised titles collide (SELECT title, count() FROM feeds GROUP BY title HAVING count() > 1, read only), and whether any torrent enclosure is present; do not seed an attacking torrent against production.'}
Owner check (no probing). Owner-observed and non-destructive: check whether production sets organize = date or has two subscribed feeds whose sanitised titles collide (SELECT title, count() FROM feeds GROUP BY title HAVING count() > 1, read only), and whether any torrent enclosure is present; do not seed an attacking torrent against production.
Full detail:
/config/security-audit-skill/ipx/run-1/NEEDS-VALIDATION.md(fingerprintipx:torrent.rs:fetch:overwrite-shared-folder).Fixed in
ec3dedf, with #318: every enclosure's torrent gets its own folder, sooverwrite: truecan only rewrite that enclosure's own files. Deployed to production (ipx -dev).