CVE-2026-90959: Pulpcore: pulpcore: file:// scheme allowlist bypass in content upload file_url field enables arbitrary file read and pulp container registry signing key theft
A path traversal vulnerability was found in pulpcore. The content upload API accepts a 'fileurl' parameter that allows users with file repository privileges to specify a local file URL for Pulp to download and store. A URL scheme validation check uses a string prefix comparison that only rejects URLs beginning with 'file://', but Python's URL parser recognizes the 'file:' scheme without double slashes, creating a mismatch between what is validated and what is dispatched to the file downloader. An authenticated user with low-privilege repository permissions can supply a specially crafted URL using relative path traversal sequences to read any file accessible to the Pulp server process. In deployments that include Pulp Container, successful exploitation allows an attacker to read the container registry token signing private key and forge bearer tokens, granting unauthorized access to all private container repositories in the affected registry.
Other sources
Pulpcore provides a content upload API field 'fileurl' that allows any authenticated user with repository creator privileges to supply a URL from which Pulp will download and store content as an Artifact. The validator for this field (validateurl in RemoteSerializer, pulpcore/app/serializers/repository.py line 139) checks file paths ONLY when the URL string begins with 'file://' (using a case-insensitive startswith check). The DownloaderFactory (pulpcore/download/factory.py line 181) dispatches on the parsed URL scheme using Python's urlparse(), which correctly identifies 'file:../../path' (single colon, no double slash) as having scheme 'file'. This creates a validator/dispatcher mismatch: a URL like 'file:../../../../../../etc/pulp/certs/tokenprivatekey.pem' passes the string-based allowlist check (it does not start with 'file://') and is dispatched to FileDownloader. FileDownloader also calls RemoteSerializer().validateurl() which performs the same incomplete string check. The resolved path is computed as os.path.abspath(os.path.join(p.netloc, p.path)) where p.netloc is empty and p.path is the relative traversal sequence, making the final path relative to the Pulp process's working directory. The file is read and stored as an Artifact, then served through a File Distribution.
The reporter (Yonghwa Lee, Xint by Theori, underdog) demonstrated end-to-end exploitation: a non-staff non-superuser holding only file.filerepositorycreator and file.filedistributioncreator roles successfully reads /etc/pulp/certs/tokenprivatekey.pem (the Pulp Container registry JWT signing key at its documented default path), publishes it through their own distribution, downloads it, forges ES256 JWT bearer tokens, and performs unauthorized pull, push, persistence, and manifest deletion on private container repositories belonging to other users. Five independent trials confirmed this in the reporter's lab (results.txt included in PULP-001-artifact.zip).
Affected versions: pulpcore 3.117.1 and pulp-container 2.29.0 confirmed; current main branch also affected. No upstream fix is available at the time of ticket creation.
PSIRT Ticket: PSIRTSUPT-23758
— Red Hat
Affected Software
Event History
Frequently Asked Questions
What level of access does an attacker need?
The attacker must be authenticated and have low-privilege repository permissions, described as file repository or repository creator privileges. No user interaction is required.
Which deployments face the greatest impact?
Any deployment where a permitted user can submit content through the content upload API using the file_url field may expose files readable by the Pulp server process. Deployments that include Pulp Container are at greater risk because the container registry token-signing private key may be readable.
What could an attacker do after reading the Pulp Container signing key?
An attacker could forge bearer tokens for the affected container registry. This can grant unauthorized access to all private container repositories in that registry.