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.
AI_ONLY_REPORT
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 FD_t rpmgiOpen(const char * path, const char * fmode)
{
char * fn = rpmExpand(path, NULL);
FD_t fd = Fopen(fn, fmode);
if (fd == NULL || Ferror(fd)) {
rpmlog(RPMLOG_ERR, _("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/rpm_manifest_poc)` 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/rpm_manifest_poc)
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/rpm_manifest_poc
```
4. A successful reproduction creates `/tmp/rpm_manifest_poc` 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 FD_t rpmgiOpen(const char * path, const char * fmode)
{
char * fn = rpmExpand(path, NULL);
FD_t fd = Fopen(fn, fmode);
+ FD_t fd = Fopen(path, fmode);
if (fd == NULL || Ferror(fd)) {
rpmlog(RPMLOG_ERR, _("open of %s failed: %s\n"), fn,
Fstrerror(fd));
+ rpmlog(RPMLOG_ERR, _("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
AI_ONLY_REPORT 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 FD_t rpmgiOpen(const char * path, const char * fmode) { char * fn = rpmExpand(path, NULL); FD_t fd = Fopen(fn, fmode); if (fd == NULL || Ferror(fd)) { rpmlog(RPMLOG_ERR, _("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/rpm_manifest_poc)` 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/rpm_manifest_poc) 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/rpm_manifest_poc ``` 4. A successful reproduction creates `/tmp/rpm_manifest_poc` 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 FD_t rpmgiOpen(const char * path, const char * fmode) { char * fn = rpmExpand(path, NULL); FD_t fd = Fopen(fn, fmode); + FD_t fd = Fopen(path, fmode); if (fd == NULL || Ferror(fd)) { rpmlog(RPMLOG_ERR, _("open of %s failed: %s\n"), fn, Fstrerror(fd)); + rpmlog(RPMLOG_ERR, _("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