Bug 2478409 (CVE-2026-84233) - CVE-2026-84233 rpm: Command Execution via Macro Expansion in `rpmuncompress -x` for Crafted `.gem` Filenames
Summary: CVE-2026-84233 rpm: Command Execution via Macro Expansion in `rpmuncompress -...
Keywords:
Status: NEW
Alias: CVE-2026-84233
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
medium
medium
Target Milestone: ---
Assignee: Product Security DevOps Team
QA Contact:
URL:
Whiteboard:
Depends On: 2527527
Blocks:
TreeView+ depends on / blocked
 
Reported: 2026-05-18 03:34 UTC by OSIDB Bzimport
Modified: 2026-09-02 15:11 UTC (History)
3 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed:
Embargoed:


Attachments (Terms of Use)

Description OSIDB Bzimport 2026-05-18 03:34:00 UTC
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

Comment 1 Christopher Lusk 2026-06-26 17:33:35 UTC
Tracker filed for rhel-10.3: https://issues.redhat.com/browse/RHEL-189190


Note You need to log in before you can comment on or make changes to this bug.