Fedora Account System
Red Hat Associate
Red Hat Customer
AI_ONLY_REPORT package: rpm-4.19.1.1-23.el10 ------ Summary: Command Execution via Macro Expansion in `rpmuncompress -x` for Crafted `.gem` Filenames: attacker-controlled `.gem` basenames reach `rpmExpand()` through `rpmGetPath()`, allowing `%()` shell macro execution while the extraction command is being constructed. Requirements to exploit: An attacker must supply a crafted `.gem` filename containing RPM macro syntax such as `%(...)`, and a user or automated workflow must invoke `rpmuncompress -x` on that file. No prior privileges are required, but any resulting command runs with the privileges of the invoking account. Component affected: `rpm-4.19.1.1-23.el10`, `tools/rpmuncompress.c` (`doUntar()` `.gem` handling), with the expansion sink in `rpmio/rpmfileutil.c` (`rpmGetPath()`) and shell execution in `rpmio/macro.c` (`doShellEscape()`). Version affected: `rpm-4.19.1.1-23.el10` 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 requires local access to provide a crafted filename to `rpmuncompress`; no remote trigger is established from the available evidence. AC:L - Once a crafted `.gem` filename is processed, no race, bypass, or special environmental condition is needed. PR:N - The attacker does not need an existing account or elevated privileges to create or supply the malicious file. UI:R - A user or automated build workflow must invoke `rpmuncompress -x` on the crafted filename. S:U - The vulnerable helper and the resulting command execution occur within the same security scope. C:H - Successful exploitation can execute arbitrary commands and read data accessible to the invoking user or build account. I:H - Arbitrary commands can modify files, outputs, and state reachable by the invoking user or build account. A:H - Arbitrary commands can delete data, terminate processes, or otherwise disrupt work in that user context. Impact: Important. This issue does not meet Red Hat's Critical criteria because exploitation is not remote and requires user or workflow interaction with a crafted local filename. It is stronger than Moderate because, once the vulnerable `.gem` extraction path is exercised, the attacker gains arbitrary command execution in the invoking account's context and can directly compromise the confidentiality, integrity, and availability of resources available to that account. Embargo: no Reason: The flaw requires a crafted local filename and user or build interaction, and practical short-term mitigations are available by avoiding or renaming untrusted `.gem` inputs before extraction. The available evidence does not indicate a need for embargoed handling. Acknowledgement: Aisle Research Vulnerability Details: When `rpmuncompress` handles a `.gem` file in extract mode, it derives a basename-based string and passes that filename-derived value into `rpmGetPath()` to build the `.gemspec` output name: ```c } else if (at->compressed == COMPRESSED_GEM) { char *tmp = xstrdup(fn); const char *bn = basename(tmp); size_t nvlen = strlen(bn) - 3; char *gem = rpmGetPath("%{__gem}", NULL); char *gemspec = NULL; char gemnameversion[nvlen]; rstrlcpy(gemnameversion, bn, nvlen); gemspec = rpmGetPath("", gemnameversion, ".gemspec", NULL); rasprintf(&buf, "%s '%s' && %s spec '%s' --ruby > '%s'", zipper, fn, gem, fn, gemspec); ``` `rpmGetPath()` concatenates its arguments and then calls `rpmExpand(dest, NULL)`. In the macro engine, `%(...)` is treated as a shell escape and reaches `doShellEscape()`, which executes the expanded command via `popen(buf, "r")`. Because `rpmuncompress` builds the command string before it checks `if (dryrun)`, a payload such as `foo%(touch /tmp/rpm_macro_poc).gem` executes during command construction, including under `rpmuncompress -n -x`. This is directly reproducible through the helper CLI. Packaging or automation workflows may also be exposed if they invoke `rpmuncompress -x` on attacker-controlled `.gem` source filenames. Steps to reproduce: 1. Build or use `rpmuncompress` from this package. 2. Create a file with a `.gem` suffix and a macro payload in its filename. In the reproduced case, 13 bytes of content were sufficient for detection to proceed: ```bash printf '1234567890123' > 'foo%(touch /tmp/rpm_macro_poc).gem' ``` 3. Run extraction in dry-run mode: ```bash rpmuncompress -n -x 'foo%(touch /tmp/rpm_macro_poc).gem' ``` 4. Verify the side effect: ```bash ls -l /tmp/rpm_macro_poc ``` 5. `/tmp/rpm_macro_poc` exists, showing that the `%()` payload executed while `rpmuncompress` was constructing the `.gemspec` path rather than during the later extraction stage. Mitigation: Until a fix is shipped, do not invoke `rpmuncompress -x` on untrusted `.gem` filenames. Renaming the file to remove RPM macro syntax before extraction, or using an extractor that does not expand RPM macros in filename-derived strings, blocks the demonstrated path. In automated build environments, reject or sanitize untrusted source archive names before extraction. Proposed Fix: Avoid passing filename-derived content through `rpmGetPath()` in the `.gem` branch. Plain string concatenation is sufficient for `.gemspec` construction and prevents macro evaluation of attacker-controlled basenames. ```diff diff --git a/tools/rpmuncompress.c b/tools/rpmuncompress.c @@ -106,7 +106,8 @@ static char *doUntar(const char *fn) char gemnameversion[nvlen]; rstrlcpy(gemnameversion, bn, nvlen); gemspec = rpmGetPath("", gemnameversion, ".gemspec", NULL); + /* Avoid macro expansion on filename-derived input */ + gemspec = rstrscat(NULL, gemnameversion, ".gemspec", NULL); ``` ------ This report was generated using AI technology. Always review AI-generated content prior to use
Tracker filed for rhel-10.3: https://issues.redhat.com/browse/RHEL-189190