Skip to content

Commit

Permalink
sosreport: Fix command injection with crafted report names [CVE-2024-…
Browse files Browse the repository at this point in the history
…2947]

Files in /var/tmp/ are controllable by any user. In particular, an
unprivileged user could create an sosreport* file containing a `'` and a
shell command, which would then run with root privileges when the
admin Cockpit user tried to delete the report.

Use the `cockpit.file()` API instead, which entirely avoids shell. The
main motivation for using shell and the glob was to ensure that the
auxiliary files like *.gpg and *.sha256 get cleaned up -- do that
explicitly (which is much safer anyway), and let our tests make sure
that we don't leave files behind.

https://bugzilla.redhat.com/show_bug.cgi?id=2271614
https://bugzilla.redhat.com/show_bug.cgi?id=2271815

Cherry-picked from main commit 9c4cc9b
  • Loading branch information
martinpitt committed Apr 2, 2024
1 parent 8f15cb9 commit f43f0de
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 3 deletions.
11 changes: 10 additions & 1 deletion pkg/sosreport/sosreport.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,16 @@ function sosDownload(path) {
}

function sosRemove(path) {
return cockpit.script(cockpit.format("rm -f '$0' '$0'.*", path), { superuser: true, err: "message" });
// there are various potential extra files; not all of them are expected to exist,
// the file API tolerates removing nonexisting files
const paths = [
path,
path + ".asc",
path + ".gpg",
path + ".md5",
path + ".sha256",
];
return Promise.all(paths.map(p => cockpit.file(p, { superuser: true }).replace(null)));
}

const SOSDialog = () => {
Expand Down
6 changes: 4 additions & 2 deletions test/verify/check-sosreport
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ only-plugins=release,date,host,cgroups,networking
# while the download is ongoing, it will have an *.xz.tmpsuffix name, gets renamed to *.xz when done
testlib.wait(lambda: len(downloaded_sosreports()) > 0)
report_gpg = downloaded_sosreports()[0]
report = report_gpg.removesuffix(".gpg")
base_report_gpg = os.path.basename(report_gpg)
report = report_gpg.replace(".gpg", "")
base_report = base_report_gpg.removesuffix(".gpg")

m.execute(f"test -f /var/tmp/{base_report_gpg}")

Expand All @@ -100,7 +101,8 @@ only-plugins=release,date,host,cgroups,networking
b.click("tr:contains(mylabel) button.pf-v5-c-dropdown__toggle")
b.click("tr:contains(mylabel) li:contains(Delete)")
b.click("#sos-remove-dialog button:contains(Delete)")
testlib.wait(lambda: m.execute(f"! test -f /var/tmp/{base_report_gpg} && echo yes"))
# ensure it removes the report itself, and auxiliary files like .gpg
m.execute(f"while ls /var/tmp/{base_report}*; do sleep 1; done", stdout=None, timeout=10)

# error reporting
self.write_file("/usr/sbin/sos", """#!/bin/sh
Expand Down

0 comments on commit f43f0de

Please sign in to comment.