CVE-2026-95519: Rpm: code execution via macro expansion of manifest entries in `rpmgi` (`-q -p` / verify manifest flows)
A flaw was found in rpm. An attacker can supply a crafted manifest file that, when processed by a user or automation using rpm -q -p or similar manifest-processing flows, leads to arbitrary code execution. This occurs because manifest entries are unexpectedly macro-expanded before being opened, allowing embedded shell commands to run with the privileges of the rpm process. Successful exploitation can lead to a full compromise of confidentiality, integrity, and availability for the affected account.
Other sources
AIONLYREPORT package: rpm-6.0.1-5.1.hum1 ------ Summary: Code Execution via Macro Expansion of Manifest Entries in rpmgi (-q -p / verify manifest flows): attacker-controlled manifest entries are macro-expanded before open, allowing %() shell command execution in the caller context when non-package files are processed as manifests. Requirements to exploit: An attacker must supply a crafted manifest file and cause a user or automation to invoke rpm -q -p or a similar manifest-processing package-file flow on it without --nomanifest. No prior privileges are required beyond supplying the manifest; the resulting command executes with the privileges of the rpm process. Component affected: rpm-6.0.1-5.1.hum1, primarily lib/rpmgi.cc rpmgiOpen(), reached from manifest parsing in lib/manifest.cc during package-file query/verify handling. Version affected: rpm-6.0.1-5.1.hum1 Patch available: no released package fix established; proposed patch included below Version fixed: unknown Upstream coordination: Not notified. CVSS: CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H - 7.8 (HIGH) AV:L - Exploitation occurs through a local manifest file processed by the rpm CLI. AC:L - A single crafted manifest entry using RPM macro syntax is sufficient; no race or uncommon precondition is required. PR:N - The attacker does not need an account on the target system; delivering the crafted manifest is enough. UI:R - A user or automation must run an affected package-file query/verify flow on the attacker-controlled manifest. S:U - Exploitation affects the same security scope as the rpm process that parses the manifest. C:H - Successful command execution can read data available to the invoking context. I:H - Successful command execution can modify files or system state available to the invoking context. A:H - Successful command execution can disrupt or destroy data or service availability available to the invoking context. Impact: Important. This issue requires user-assisted processing of attacker-supplied manifest content, so it does not meet the criteria for Critical impact. However, once triggered it can execute arbitrary commands in the rpm caller context, which is sufficient to fully compromise confidentiality, integrity, and availability for that account. If the affected workflow is run by an administrator or privileged automation, the consequence is correspondingly severe. Embargo: no Reason: This is a user-assisted local CLI issue with a straightforward operational mitigation (--nomanifest) and no silent remote attack surface. Acknowledgement: Aisle Research Vulnerability Details: rpm package-file query handling constructs an rpmgi iterator for -p inputs. When the supplied file is not recognized as an RPM header, the iterator falls back to manifest handling. Manifest entries are read as plaintext strings and later reopened through rpmgiOpen(), which macro-expands the path before Fopen(): cpp static FDt rpmgiOpen(const char path, const char fmode) { char fn = rpmExpand(path, NULL); FDt fd = Fopen(fn, fmode); if (fd == NULL || Ferror(fd)) { rpmlog(RPMLOGERR, ("open of %s failed: %s\n"), fn, Fstrerror(fd)); if (fd != NULL) (void) Fclose(fd); fd = NULL; } free(fn); return fd; } RPM macro expansion supports executable forms, including %() shell escapes. As a result, a manifest entry such as %(id > /tmp/rpmmanifestpoc) is evaluated before the open attempt, and the embedded command runs in the caller context. This is unexpected for rpm-manifest(5), which documents manifests as plaintext glob/path entries naming RPM packages or other manifests rather than executable macro content. The concrete reproduction below demonstrates the issue in -q -p. Query and verify share the same package-file source handling around rpmgi, so manifest-processing verify flows should be treated as relevant as well, but the directly reproduced case here is query mode. Steps to reproduce: 1. Create a proof-of-concept manifest: bash cat > /tmp/poc.mft <<'EOF' %(id > /tmp/rpmmanifestpoc) EOF 2. Run package-file query mode on the manifest: bash rpm -q -p /tmp/poc.mft 3. Confirm the side effect: bash cat /tmp/rpmmanifestpoc 4. A successful reproduction creates /tmp/rpmmanifestpoc containing the output of id from the invoking user context. 5. Control check: disable manifest processing and rerun: bash rpm -q -p --nomanifest /tmp/poc.mft 6. With --nomanifest, the manifest should not be processed and the side-effect file should not be created by this invocation. Mitigation: Until a fix is available, do not run rpm package-file query or verify operations on untrusted manifest files. Use --nomanifest when handling untrusted inputs, and if manifest support is required in automation, pre-validate entries so that only expected literal paths or globs are accepted and macro syntax is rejected. Proposed Fix: Stop macro-expanding manifest-derived paths in rpmgiOpen() and open the original path string directly. diff diff --git a/lib/rpmgi.cc b/lib/rpmgi.cc — a/lib/rpmgi.cc +++ b/lib/rpmgi.cc @@ -49,8 +49,7 @@ static FDt rpmgiOpen(const char path, const char fmode) { char fn = rpmExpand(path, NULL);
FDt fd = Fopen(fn, fmode); + FDt fd = Fopen(path, fmode);
if (fd == NULL || Ferror(fd)) { rpmlog(RPMLOGERR, ("open of %s failed: %s\n"), fn, Fstrerror(fd)); + rpmlog(RPMLOGERR, ("open of %s failed: %s\n"), path, Fstrerror(fd)); if (fd != NULL) (void) Fclose(fd); fd = NULL; }
free(fn);
return fd; } ------ This report was generated using AI technology. Always review AI-generated content prior to use
— Red Hat
Rpm: code execution via macro expansion of manifest entries in rpmgi (-q -p / verify manifest flows)
— Microsoft
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Configuration
Use the --nomanifest option when running rpm package-file query or verify operations on untrusted manifest files to disable manifest processing.
rpm --nomanifest = enabled - Compensating control
When manifest support is required in automation, pre-validate manifest entries so that only expected literal paths or globs are accepted and RPM macro syntax is rejected.
Event History
Frequently Asked Questions
Who is exposed to this issue?
Users and automation that process attacker-supplied or otherwise untrusted manifest files through rpm -q -p or similar manifest-processing package-file flows are exposed. Commands execute with the privileges of the rpm process, so impact depends on the account running rpm.
What does an attacker need to exploit it?
An attacker needs to provide a crafted manifest file and cause a user or automated process to invoke an affected rpm manifest-processing flow on it. The attacker does not need prior privileges beyond being able to supply that file and trigger or persuade the processing action.
What can be done if patching is not immediately possible?
Do not process untrusted files with rpm -q -p or similar manifest-processing flows. Use --nomanifest where applicable to prevent the manifest handling required for exploitation.
How can I identify potentially affected workflows?
Review scripts, automation, and operator procedures for rpm -q -p, verification manifest flows, or other rpm package-file processing that accepts non-package files as manifests. Prioritize workflows where files originate from untrusted users, external systems, or attacker-controllable locations.