From d0644cd8f1f7eab93a13fa1b4a77daf8a3267e12 Mon Sep 17 00:00:00 2001 From: Aurelien Aptel Date: Mon, 27 Jul 2020 10:34:44 +0200 Subject: [PATCH v1] CVE-2020-14342: mount.cifs: fix shell command injection A bug has been reported recently for the mount.cifs utility which is part of the cifs-utils package. The tool has a shell injection issue where one can embed shell commands via the username mount option. Those commands will be run via popen() in the context of the user calling mount. The bug requires cifs-utils to be built with --with-systemd (enabled by default if supported). A quick test to check if the mount.cifs binary is vulnerable is to look for popen() calls like so: $ nm mount.cifs | grep popen U popen@@GLIBC_2.2.5 If the user is allowed to run mount.cifs via sudo, he can obtain a root shell. sudo mount.cifs -o username='`sh`' //1 /mnt If mount.cifs has the setuid bit, the command will still be run as the calling user (no privilege escalation). The bug was introduced in June 2012 with commit 4e264031d0da7d3f2 ("mount.cifs: Use systemd's mechanism for getting password, if present."). Affected versions: cifs-utils-5.6 cifs-utils-5.7 cifs-utils-5.8 cifs-utils-5.9 cifs-utils-6.0 cifs-utils-6.1 cifs-utils-6.2 cifs-utils-6.3 cifs-utils-6.4 cifs-utils-6.5 cifs-utils-6.6 cifs-utils-6.7 cifs-utils-6.8 cifs-utils-6.9 cifs-utils-6.10 Bug: https://bugzilla.samba.org/show_bug.cgi?id=14442 Signed-off-by: Paulo Alcantara (SUSE) Signed-off-by: Aurelien Aptel --- mount.cifs.c | 93 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 72 insertions(+), 21 deletions(-) diff --git a/mount.cifs.c b/mount.cifs.c index 40918c1..6c98b94 100644 --- a/mount.cifs.c +++ b/mount.cifs.c @@ -1695,6 +1695,73 @@ drop_child_privs(void) return 0; } +#ifdef ENABLE_SYSTEMD +static int get_passwd_by_systemd(const char *prompt, char *input, int capacity) +{ + int fd[2]; + pid_t pid; + int offs = 0; + int rc = 1; + + if (pipe(fd) == -1) { + fprintf(stderr, "Failed to create pipe: %s\n", strerror(errno)); + return 1; + } + + pid = fork(); + if (pid == -1) { + fprintf(stderr, "Unable to fork: %s\n", strerror(errno)); + close(fd[0]); + close(fd[1]); + return 1; + } + if (pid == 0) { + close(fd[0]); + dup2(fd[1], STDOUT_FILENO); + if (execlp("systemd-ask-password", "systemd-ask-password", prompt, NULL) == -1) { + fprintf(stderr, "Failed to execute systemd-ask-password: %s\n", + strerror(errno)); + } + exit(1); + } + + close(fd[1]); + for (;;) { + if (offs+1 >= capacity) { + fprintf(stderr, "Password too long.\n"); + kill(pid, SIGTERM); + rc = 1; + break; + } + rc = read(fd[0], input + offs, capacity - offs); + if (rc == -1) { + fprintf(stderr, "Failed to read from pipe: %s\n", strerror(errno)); + rc = 1; + break; + } + if (!rc) + break; + offs += rc; + input[offs] = '\0'; + } + if (wait(&rc) == -1) { + fprintf(stderr, "Failed to wait child: %s\n", strerror(errno)); + rc = 1; + goto out; + } + if (!WIFEXITED(rc) || WEXITSTATUS(rc)) { + rc = 1; + goto out; + } + + rc = 0; + +out: + close(fd[0]); + return rc; +} +#endif + /* * If systemd is running and systemd-ask-password -- * is available, then use that else fallback on getpass(..) @@ -1714,27 +1781,11 @@ get_password(const char *prompt, char *input, int capacity) && (lstat("/sys/fs/cgroup/systemd", &b) == 0) && (a.st_dev != b.st_dev); - if (is_systemd_running) { - char *cmd, *ret; - FILE *ask_pass_fp = NULL; - - cmd = ret = NULL; - if (asprintf(&cmd, "systemd-ask-password \"%s\"", prompt) >= 0) { - ask_pass_fp = popen (cmd, "re"); - free (cmd); - } - - if (ask_pass_fp) { - ret = fgets(input, capacity, ask_pass_fp); - pclose(ask_pass_fp); - } - - if (ret) { - int len = strlen(input); - if (input[len - 1] == '\n') - input[len - 1] = '\0'; - return input; - } + if (is_systemd_running && !get_passwd_by_systemd(prompt, input, capacity)) { + int len = strlen(input); + if (len && input[len - 1] == '\n') + input[len - 1] = '\0'; + return input; } #endif -- 2.27.0