summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Gregory <andrew.gregory.8@gmail.com>2019-03-01 02:23:20 +0100
committerAllan McRae <allan@archlinux.org>2019-03-01 02:25:46 +0100
commit9702703633bec2c007730006de2aeec8587dfc84 (patch)
treee0d7aabf9362a91965c377cbab5a3f15322db2d7
parent0b36d8781783d7f4a0086e12eb7cae542e7f6bd7 (diff)
downloadpacman-9702703633bec2c007730006de2aeec8587dfc84.tar.gz
pacman-9702703633bec2c007730006de2aeec8587dfc84.tar.xz
Sanitize file name received from Content-Disposition header
When installing a remote package with "pacman -U <url>", pacman renames the downloaded package file to match the name given in the Content-Disposition header. However, pacman does not sanitize this name, which may contain slashes, before calling rename(). A malicious server (or a network MitM if downloading over HTTP) can send a content-disposition header to make pacman place the file anywhere in the filesystem, potentially leading to arbitrary root code execution. Notably, this bypasses pacman's package signature checking. For example, a malicious package-hosting server (or a network man-in-the-middle, if downloading over HTTP) could serve the following header: Content-Disposition: filename=../../../../../../usr/share/libalpm/hooks/evil.hook and pacman would move the downloaded file to /usr/share/libalpm/hooks/evil.hook. This invocation of "pacman -U" would later fail, unable to find the downloaded package in the cache directory, but the hook file would remain in place. The commands in the malicious hook would then be run (as root) the next time any package is installed. Discovered-by: Adam Suhl <asuhl@mit.edu> Signed-off-by: Allan McRae <allan@archlinux.org> (cherry picked from commit d197d8ab82cf10650487518fb968067897a12775)
-rw-r--r--lib/libalpm/dload.c3
1 files changed, 2 insertions, 1 deletions
diff --git a/lib/libalpm/dload.c b/lib/libalpm/dload.c
index 36ae4ee1..570111cb 100644
--- a/lib/libalpm/dload.c
+++ b/lib/libalpm/dload.c
@@ -534,7 +534,8 @@ static int curl_download_internal(struct dload_payload *payload,
if(payload->content_disp_name) {
/* content-disposition header has a better name for our file */
free(payload->destfile_name);
- payload->destfile_name = get_fullpath(localpath, payload->content_disp_name, "");
+ payload->destfile_name = get_fullpath(localpath,
+ get_filename(payload->content_disp_name), "");
} else {
const char *effective_filename = strrchr(effective_url, '/');
if(effective_filename && strlen(effective_filename) > 2) {