184 lines
4.4 KiB
Diff
184 lines
4.4 KiB
Diff
|
From 610ea2412e2f31fbf3e662bf7dfadf7d73ce2934 Mon Sep 17 00:00:00 2001
|
||
|
From: Aurelien Aptel <aaptel@suse.com>
|
||
|
Date: Mon, 27 Jul 2020 10:34:44 +0200
|
||
|
Subject: [PATCH] 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
|
||
|
Reported-by: Vadim Lebedev <vadim@mbdsys.com>
|
||
|
|
||
|
Signed-off-by: Paulo Alcantara (SUSE) <paulo@paulo.ac>
|
||
|
Signed-off-by: Aurelien Aptel <aaptel@suse.com>
|
||
|
---
|
||
|
mount.cifs.c | 96 ++++++++++++++++++++++++++++++++++++++++------------
|
||
|
1 file changed, 75 insertions(+), 21 deletions(-)
|
||
|
|
||
|
diff --git a/mount.cifs.c b/mount.cifs.c
|
||
|
index 40918c1..4feb397 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(..)
|
||
|
@@ -1708,35 +1775,22 @@ get_password(const char *prompt, char *input, int capacity)
|
||
|
int is_systemd_running;
|
||
|
struct stat a, b;
|
||
|
|
||
|
+ memset(input, 0, capacity);
|
||
|
+
|
||
|
/* We simply test whether the systemd cgroup hierarchy is
|
||
|
* mounted */
|
||
|
is_systemd_running = (lstat("/sys/fs/cgroup", &a) == 0)
|
||
|
&& (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 (input[len - 1] == '\n')
|
||
|
+ input[len - 1] = '\0';
|
||
|
+ return input;
|
||
|
}
|
||
|
#endif
|
||
|
+ memset(input, 0, capacity);
|
||
|
|
||
|
/*
|
||
|
* Falling back to getpass(..)
|
||
|
--
|
||
|
2.27.0
|
||
|
|