Bug 2462222 (CVE-2026-84838) - CVE-2026-84838 rpm: Command injection in rpmuncompress via unescaped filenames passed to popen()
Summary: CVE-2026-84838 rpm: Command injection in rpmuncompress via unescaped filename...
Keywords:
Status: NEW
Alias: CVE-2026-84838
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: 2527541
Blocks:
TreeView+ depends on / blocked
 
Reported: 2026-04-26 18:35 UTC by OSIDB Bzimport
Modified: 2026-09-02 15:23 UTC (History)
3 users (show)

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


Attachments (Terms of Use)

Description OSIDB Bzimport 2026-04-26 18:35:03 UTC
AI_ONLY_REPORT
package: rpm-4.19.1.1-23.el10
------
Summary: Command Injection in rpmuncompress via Unescaped Filenames:  
attacker-controlled archive filenames are embedded into shell command  
strings and executed via `popen()`, allowing arbitrary command execution in  
the invoking user's context.
Requirements to exploit: An attacker must supply a compressed file or  
source archive whose filename contains shell metacharacters and cause a  
user or automated workflow to invoke `rpmuncompress` on it. No prior  
privileges are required, but any resulting commands run with the privileges  
of the invoking process.
Component affected: `rpm-4.19.1.1-23.el10`, `tools/rpmuncompress.c`  
(`doUncompress()`, `doUntar()`, and the `popen()` execution path in  
`main()`)
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.7 (HIGH)
AV:L - The attacker must influence a local filename that is later  
processed by `rpmuncompress`; this is not a network-reachable trigger by  
itself.
AC:L - The payload is carried directly in the filename, and the  
vulnerable code passes it into shell command strings without robust  
escaping.
PR:N - No prior privileges on the target account are required to deliver  
or prepare a maliciously named archive.
UI:R - A user or automated build or extraction workflow must invoke  
`rpmuncompress` on the attacker-controlled filename.
S:U - The impact stays within the security scope of the invoking process.
C:H - Successful command injection can read data available to the  
invoking user.
I:H - Successful command injection can modify files or build outputs  
available to the invoking user.
A:H - Successful command injection can disrupt or destroy data available  
to the invoking user.
Impact: Moderate. The flaw can execute arbitrary shell commands in the  
context of the user running `rpmuncompress`, so confidentiality, integrity,  
and availability can all be affected for that user. However, exploitation  
is local and user-assisted: an attacker must get a maliciously named  
archive processed by the helper or by a workflow that invokes it. Under Red  
Hat's severity guidance, that is better classified as Moderate than  
Important because the available evidence does not show remote compromise or  
privilege escalation by itself.
Embargo: no
Reason: The issue is local and user-assisted, and there is  
straightforward short-term mitigation by avoiding untrusted archive  
filenames until a fixed package is available.
Acknowledgement: Aisle Research
Vulnerability Details: `rpmuncompress` constructs shell command strings  
from archive filenames and then executes those strings through the shell.  
In `doUncompress()`, the filename is appended directly to the command. In  
`doUntar()`, the filename is placed inside single quotes, but embedded  
single quotes in a filename break out of that quoting and let shell  
metacharacters be interpreted. The resulting command is later executed with  
`popen(cmd, "r")`. Available package code also indicates that this helper  
is used from build-time source and patch unpacking paths via  
`%{__rpmuncompress}`, so exposure is not limited to purely manual  
invocation.
Focused code snippet showing the vulnerable command construction sites:
```diff
       cmd = rstrscat(&cmd, " ", fn, NULL);
...

           rasprintf(&buf, "%s '%s' | %s %s -", zipper, fn, tar, taropts);

           rasprintf(&buf, "%s '%s' && %s spec '%s' --ruby > '%s'",

                   zipper, fn, gem, fn, gemspec);

           rasprintf(&buf, "%s '%s'", zipper, fn);
...

       rasprintf(&buf, "%s %s '%s'", tar, taropts, fn);
```


Steps to reproduce:
1. Build or install the `rpmuncompress` helper from the affected package.
2. Create a valid compressed file whose name contains shell metacharacters:
```bash
printf 'hello\n' | gzip -c > "poc;id>/tmp/rpmuncompress_poc;#.gz"
```
3. Run the helper on the malicious filename:
```bash
rpmuncompress "poc;id>/tmp/rpmuncompress_poc;#.gz" >/dev/null 2>&1
```
4. Verify command execution:
```bash
test -s /tmp/rpmuncompress_poc && echo VULNERABLE
```
Expected result: `/tmp/rpmuncompress_poc` is created and contains the  
output of `id`.
A related trigger exists in extraction mode (`-x`) because `doUntar()` uses  
`'%s'` for `fn` without escaping embedded single quotes.
Mitigation: Until a fixed package is available, do not pass untrusted  
archive filenames to `rpmuncompress` or workflows that invoke  
`%{__rpmuncompress}`. Rename source and patch archives to remove shell  
metacharacters before build or extraction steps process them.
Proposed Fix: Escape all attacker-controlled path fragments before  
embedding them into shell command strings, including the derived `.gemspec`  
path in the gem handling branch. The following minimal patch applies  
`%{shescape:...}` consistently at each vulnerable construction site:
```diff
diff --git a/tools/rpmuncompress.c b/tools/rpmuncompress.c
— a/tools/rpmuncompress.c
+++ b/tools/rpmuncompress.c
@@ -70,10 +70,13 @@ static char *doUncompress(const char *fn)
{
char *cmd = NULL;
+    char *qfn = NULL;
const struct archiveType_s *at = getArchiver(fn);
if (at) {
+        qfn = rpmExpand("%{shescape:", fn, "}", NULL);
cmd = rpmExpand(at->cmd, " ", at->unpack, NULL);
       cmd = rstrscat(&cmd, " ", fn, NULL);
+        cmd = rstrscat(&cmd, " ", qfn, NULL);
+        free(qfn);
      }
      return cmd;
  }
@@ -82,6 +85,7 @@ static char *doUntar(const char *fn)
  {
      const struct archiveType_s *at = NULL;
      char *buf = NULL;
+    char *qfn = NULL;
      char *tar = NULL;
      const char *taropts = verbose ? "-xvvof" : "-xof";
@@ -89,6 +93,7 @@ static char *doUntar(const char *fn)
      if ((at = getArchiver(fn)) == NULL)
          goto exit;
+    qfn = rpmExpand("%{shescape:", fn, "}", NULL);
@@ -98,7 +103,7 @@ static char *doUntar(const char *fn)
          if (needtar) {

           rasprintf(&buf, "%s '%s' | %s %s -", zipper, fn, tar, taropts);
+            rasprintf(&buf, "%s %s | %s %s -", zipper, qfn, tar, taropts);
          } else if (at->compressed == COMPRESSED_GEM) {
@@ -111,11 +116,14 @@ static char *doUntar(const char *fn)

           rasprintf(&buf, "%s '%s' && %s spec '%s' --ruby > '%s'",

                   zipper, fn, gem, fn, gemspec);
+            char *qgemspec = rpmExpand("%{shescape:", gemspec, "}", NULL);
+            rasprintf(&buf, "%s %s && %s spec %s --ruby > %s",
+                    zipper, qfn, gem, qfn, qgemspec);
+            free(qgemspec);
@@ -118,10 +126,11 @@ static char *doUntar(const char *fn)

           rasprintf(&buf, "%s '%s'", zipper, fn);
+            rasprintf(&buf, "%s %s", zipper, qfn);
          }
@@ -122,10 +131,11 @@ static char *doUntar(const char *fn)

       rasprintf(&buf, "%s %s '%s'", tar, taropts, fn);
+        rasprintf(&buf, "%s %s %s", tar, taropts, qfn);
      }


exit:
+    free(qfn);
free(tar);
return buf;
}
```
------
This report was generated using AI technology. Always review AI-generated  
content prior to use


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