Implementacion practica Padding Oracle Attack

Iniciado por AlbertoBSD, 20 Noviembre 2020, 05:29 AM

0 Miembros y 1 Visitante están viendo este tema.

AlbertoBSD

Recientemente me interesa ese tipo de temas otra vez, he aprendido mucho y me gustaría compartirlo con ustedes.

Si no saben de que va el tema aquí dejo unos links.

Block cipher mode of operation
https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation

Wikipedia Padding oracle attack
https://en.wikipedia.org/wiki/Padding_oracle_attack

Padding oracle attack
https://robertheaton.com/2013/07/29/padding-oracle-attack/

Este tipo de Ataque tiene ciertas condiciones iniciales para ser llevado acabo.

  • El cliente solo puede saber si su paquete fue aceptado o no.
    Esto es debido al check que hace el servidor sobre el mensaje recibido

  • El servidor no Cambia de KEY utilizada durante el proceso de cifrado y descifrado.
    Esto es debido a una mala implementación, ya que el servidor debería de renovar el KEY cada X tiempo y con cada cliente distinto.

  • El servidor tiene algún leak de información ya sea por error o mediante otro tipo de ataque.

  • El cliente solo podrá descifrar Una parte de la información, excepto por el Bloque inicial


    Dejo a continuación una imagen de prueba y el código, proximamente subire un video hablando del tema.




    [youtube=640,360]https://www.youtube.com/watch?v=GTl4ytJ3jBU[/youtube]

    Codigo, este codigo ejemplifica el cliente y servidor mediante un hilo distinto, lo hice de esta manera para no complicarme con el protocolo en RED de los mismo, se puede hacer sin hilos, y solo con llamadas a funcion, pero la idea es garantizar que el cliente no tiene acceso al servidor.



    /*
    Desarollado por AlbertoBSD
    email alberto.bsd@gmail.com
    g++ -O3 -o opk_example opk_example.c -Wint-to-pointer-cast  -pthread
    */

    #include <time.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include<pthread.h>
    #include<unistd.h>
    #include"ctaes/ctaes.c"

    #define AES_BLOCKSIZE 16

    struct timespec tim, tim2,sim,sim2;

    void crear_server();
    char *tohex(char *ptr,int length);
    void *process_server(void *vargp);

    int MyCBCEncrypt(AES256_ctx *ctx, const unsigned char iv[AES_BLOCKSIZE], const unsigned char* data, int size, bool pad, unsigned char* out);
    int MyCBCDecrypt(AES256_ctx *ctx, const unsigned char iv[AES_BLOCKSIZE], const unsigned char* data, int size, bool pad, unsigned char* out);

    /* Values between Server and client */
    int values_do;
    int values_pad;
    int values_length;
    int values_leaked;
    char *values_enc;
    char *values_leak;

    pthread_mutex_t mtx_values;  //Mutex for those values

    int main(){
     /*
       Set global values;
     */
     tim.tv_sec = 0;
     tim.tv_nsec = 50000;

     sim.tv_sec = 0;
     sim.tv_nsec = 50000;
     values_do = 0;
     values_pad = 0;
     values_length = 0;
     values_leaked = 0;
     values_enc = (char*) malloc(48);
     values_leak = (char*) malloc(48);
     crear_server();  //create child "server"

     //This main process is the client
     int i,j,k,entrar;
     char *secret,*temp,*try_enc;
     char *decrypted;
     unsigned char GUESS;
     secret = (char*) malloc(48);
     try_enc = (char*) malloc(48);
     decrypted = (char*) malloc(16);
     memset(decrypted,0,16);
     do  {  
       sleep(1);
     }while(values_leaked==0); //We need to wait to the leaked data

     memcpy(secret,values_leak,48);
     
     temp = tohex(secret,48);
     printf("process_client: leaked is %s\n",temp);
     free(temp);
     i = 0;
     j = 0;
     while(i < 16)  {
       memcpy(try_enc,secret,32);
       
       pthread_mutex_lock(&mtx_values);
       switch(values_do)  {
         case 0:
           GUESS = j;
           decrypted[15-i] = GUESS;
           for(k = 0; k <= i;k++)  {
             try_enc[15-k] = try_enc[15-k] ^ decrypted[15-k] ^ (unsigned char)(i+1);
           }
           
           values_do = 1;
           values_length = 32;
           memcpy(values_enc,try_enc,32);
         break;
         case 1:
         break;
         case 2:
           if(values_pad)  {
             i++;
             printf("Encontrado valor: %c : %.2x\n",GUESS,GUESS);
             j = 0;
           }
           else  {
             j++;
           }
           values_do = 0;
         break;
       }
       pthread_mutex_unlock(&mtx_values);
       nanosleep(&tim , &tim2);
     }
     printf("Decrypted data: %s\n",decrypted);
     
    }

    void *process_server(void *vargp)  {
     AES256_ctx ctx;
     FILE *urandom;
     const char *secret = "The password is: Ywgo/@g:2$0Qsz<";
     char *key,*dec,*enc,*iv,*temp;
     int length,i,pad_valid,outlen;
     unsigned char pad;
     key = (char*) malloc(32);
     dec = (char*) malloc(48);
     enc = (char*) malloc(48);
     iv  = (char*) malloc(16);
     urandom = fopen("/dev/urandom","rb");
     fread(key,1,32,urandom);
     fread(iv,1,16,urandom);
     fclose(urandom);
     
     AES256_init(&ctx,(const unsigned char*) key);
     
     /* LEAK THE secret */
     pthread_mutex_lock(&mtx_values);
     memset(enc,0,48);


     outlen = MyCBCEncrypt(&ctx, (const unsigned char*) iv, (const unsigned char*) secret, strlen(secret), true, (unsigned char*) enc);

     memcpy(values_leak,enc,outlen);
     values_leaked = 1;
     pthread_mutex_unlock(&mtx_values);
     /*END LEAK*/
     
     do  {
       nanosleep(&sim , &sim2);
       pthread_mutex_lock(&mtx_values);
       if(values_do == 1)  {
         length = values_length;
         pad_valid = 0;
         if(length <= 48)  {
           memcpy(enc,values_enc,length);

           outlen = MyCBCDecrypt(&ctx,( const unsigned char*) iv, (const unsigned char*) enc, length, true, (unsigned char*) dec);
           if(outlen > 0)  {
             pad_valid =  1;
             printf("Decrypted data seems legit : %i bytes\n",outlen);
             temp = tohex(dec,length);
             printf("Decrypted data %s\n",temp);
             free(temp);
           }
           else  {
             printf("Decrypted data doesnt seems legit\n",outlen);
             temp = tohex(dec,length);
             printf("Decrypted data %s\n",temp);
             free(temp);
           }
         }
         values_do = 2;
         values_pad = pad_valid;
       }
       pthread_mutex_unlock(&mtx_values);
     }while(1);
     pthread_exit(NULL);
    }

    void crear_server()  {
     int s;
     pthread_t tid;
     pthread_attr_t attr;
     s = pthread_attr_init(&attr);
     if (s != 0)  {
       perror("pthread_attr_init");
       exit(6);
     }
     s = pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
     if(s != 0)  {
       perror("pthread_attr_setstacksize");
       exit(8);
     }
     s = pthread_create(&tid,&attr,process_server,NULL);
     if(s != 0)  {
       perror("pthread_create");
     }
     pthread_attr_destroy(&attr);
    }

    char *tohex(char *ptr,int length){
     char *buffer;
     int offset = 0;
     unsigned char c;
     buffer = (char *) malloc((length * 2)+1);
     for (int i = 0; i <length; i++) {
     c = ptr[i];
     sprintf((char*) (buffer + offset),"%.2x",c);
     offset+=2;
     }
     buffer[length*2] = 0;
     return buffer;
    }


    int MyCBCDecrypt(AES256_ctx *ctx, const unsigned char iv[AES_BLOCKSIZE], const unsigned char* data, int size, bool pad, unsigned char* out)
    {
     int written = 0;
     bool fail = false;
     const unsigned char* prev = iv;
     if (!data || !size || !out)
       return 0;
     if (size % AES_BLOCKSIZE != 0)
       return 0;
     while (written != size) {
       AES256_decrypt(ctx, 1, out, data + written);
       for (int i = 0; i != AES_BLOCKSIZE; i++)
         *out++ ^= prev[i];
       prev = data + written;
       written += AES_BLOCKSIZE;
     }
     if (pad) {
       unsigned char padsize = *--out;
       fail = !padsize | (padsize > AES_BLOCKSIZE);
       padsize *= !fail;
       for (int i = AES_BLOCKSIZE; i != 0; i--)
         fail |= ((i > AES_BLOCKSIZE - padsize) & (*out-- != padsize));
       written -= padsize;
     }
     return written * !fail;
    }

    int MyCBCEncrypt(AES256_ctx *ctx, const unsigned char iv[AES_BLOCKSIZE], const unsigned char* data, int size, bool pad, unsigned char* out)
    {
     int written = 0;
     int padsize = size % AES_BLOCKSIZE;
     unsigned char mixed[AES_BLOCKSIZE];

     if (!data || !size || !out)
       return 0;

     if (!pad && padsize != 0)
       return 0;

     memcpy(mixed, iv, AES_BLOCKSIZE);

     // Write all but the last block
     while (written + AES_BLOCKSIZE <= size) {
       for (int i = 0; i != AES_BLOCKSIZE; i++)
         mixed[i] ^= *data++;
       AES256_encrypt(ctx, 1, out + written, mixed);
       memcpy(mixed, out + written, AES_BLOCKSIZE);
       written += AES_BLOCKSIZE;
     }
     if (pad) {
       // For all that remains, pad each byte with the value of the remaining
       // space. If there is none, pad by a full block.
       for (int i = 0; i != padsize; i++)
         mixed[i] ^= *data++;
       for (int i = padsize; i != AES_BLOCKSIZE; i++)
         mixed[i] ^= AES_BLOCKSIZE - padsize;
       AES256_encrypt(ctx, 1, out + written, mixed);
       written += AES_BLOCKSIZE;
     }
     return written;
    }



    Saludos!
Donaciones
1Coffee1jV4gB5gaXfHgSHDz9xx9QSECVW