Fedora Account System
Red Hat Associate
Red Hat Customer
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