La base 64 est une système de comptage qui utilise des nombres mais aussi des lettres et des symboles. Cette base est essentiellement utilisée pour l'échange de pièce jointe dans les mails pour contenir des fichiers binaires dans certain type de conteneur (tel que XHTML, MHTML, ...).
Cet article aura pour but de trouver une autre utilité à cette base.
0x01. RECODER L'ENCODAGE
Voici un code non optimisé mais fait par mes soins :
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> //-- Constante des chaines de caractères différents plus le = du complément ---- #define ERROR_00 "00 : Parametres incorrect." #define ERROR_01 "01 : Impossible d accéder au fichier en lecture." #define ERROR_02 "02 : Erreur lors de l ecriture du fichier." #define ERROR_03 "03 : L ecriture dans le fichier a echoue." #define ERROR_FF "FF : Une erreur est apparue." #define DEFAULT_WIDTH 72 #define MINIMAL_WIDTH 4 //-- Definitiondes chaines de caractères différents plus le = du complément ---- #define bool int #define true 1 #define false 0 //-- Constante des chaines de caractères différents plus le symbole du complément ---- char ConstChar[65]="AQWmzoslVFRBGTYeidZSXED%CH928v7NJUI56+apqkKLOPM04rufjtgyhwnxbc1@3"; //char ConstChar[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" //-- Encode en base 64 --------------------------------------------------------- void EncodeBase64(int src[3], int dst[4], short len) { dst[0] = (src[0] & 0xFC) >> 2; dst[1] = ((src[0] & 0x03) << 4) | ((src[1] & 0xF0) >> 4); if(len==2) dst[2]=64; else dst[2] = ((src[1] & 0x0F) << 2) | ((src[2] & 0xC0) >> 6); if(len>=1) dst[3]=64; else dst[3] = src[2] & 0x3F; } //-- Lit le fichier et l encode dans le fichier destination -------------------- void EncodeFile(char strSrcFile[256], char strDstFile[256], int iCRLF) { bool loop=true; int rep[3]={0,0,0},index=0; int cod[4]={0,0,0,0}; // Determine le nombre de mots de 6 bits a codes restants, donc le nombre // de = a mettre en fin d encodage unsigned short len; FILE *o_s, *i_s; // Test pour une erreur de flux en lecture i_s=fopen(strSrcFile,"rb"); if(!i_s) {printf("\\n\\n\\a+ Erreur : %s",ERROR_01); exit(-1);} // Test pour une erreur de flux en ecriture o_s=fopen(strDstFile,"wb"); if(!o_s) {printf("\\n\\n\\a+ Erreur : %s",ERROR_02); exit(-1);} if( strcmp(strSrcFile,"-tmp.-tmp") ) printf("\\nEncoding %s to %s...",strSrcFile,strDstFile); do { len=0; rep[0]=fgetc(i_s); rep[1]=fgetc(i_s); rep[2]=fgetc(i_s); if( rep[0] < 0 ) { len=0; rep[0] = 0; break; } if( rep[1] < 0 ) { len++; rep[1] = 0; loop=false; } if( rep[2] < 0 ) { len++; rep[2] = 0; loop=false; } EncodeBase64(rep,cod,len); // Une erreur d ecriture > exit. if( fputc(ConstChar[cod[0]],o_s) < 0 ) { printf("\\n\\n\\a+ Erreur : %s",ERROR_03); exit(-1); } else { index++; } if(index==iCRLF) {fprintf(o_s,"/r\\n");index = 0;} fputc(ConstChar[cod[1]],o_s); index++; if(index==iCRLF) {fprintf(o_s,"/r\\n");index = 0;} fputc(ConstChar[cod[2]],o_s); index++; if(index==iCRLF) {fprintf(o_s,"/r\\n");index = 0;} fputc(ConstChar[cod[3]],o_s); index++; if(index==iCRLF) {fprintf(o_s,"/r\\n");index = 0;} } while( loop == true ); if( strcmp(strSrcFile,"-tmp.-tmp") ) printf("done.\\n"); else printf("\\n"); fclose(i_s); fclose(o_s); } //-- Encode en base 2 ---------------------------------------------------------- void DecToBin(int n, int *CODE, int NbrBitsLength) { int i,nb=NbrBitsLength; for(i=nb-1;i>=0;i--) { CODE[i]=n%2; n/=2; if(n<0) break; } } //-- Fonction puissance -------------------------------------------------------- int powa(int x, int n) { int i=0,r=1; for(i=0;i<n;i++) { r*=x; } return(r); } /*-- Encode en base 10 depuis la base 2 ---------------------------------------- 2 ^ 7 = 128 * 1 = 128 8 - i + Start - 1 + 2 ^ 6 = 64 * 1 = 192 + 2 ^ 5 = 32 * 1 = 224 8 - 8 + 8 - 1 = 7 + 2 ^ 4 = 16 * 1 = 240 8 - 9 + 9 - 1 = 6 + 2 ^ 3 = 8 * 1 = 248 8 - 16 + 16 - 1 = 7 + 2 ^ 2 = 4 * 1 = 252 8 - 17 + 17 - 1 = 6 + 2 ^ 1 = 2 * 1 = 254 on revient en 7 à chaque fois... + 2 ^ 0 = 1 * 1 = 255 Donc on a bien un octet code sur 8 bits (0 -> 255 => 0 -> FF) */ int BinToDec(int *CODE, int Start, int Last) { int i,R=0; for(i=Start;i<=Last;i++) { R+=CODE[i]*(int)powa(2,8-i+Start-1); } return(R); } //-- Decode en base 64 --------------------------------------------------------- void DecodeBase64(int src[4], int dst[3]) { int i,j,k,step=-6; int tmp[6],buff[24]; for(j=0;j<4;j++) // Dans nos quatres mots de 8 bits... { // on cherche la correspondance step+=6; // avec l alphabet base 64 for(i=0;i<65;i++) // et on rempli le buffer 6 bits par 6 { if(src[j]==ConstChar[i]) { if(i==64) // Si on est sur le caractère de { // complément = alors on est sur src[j]=0; // le dernier octet } else { DecToBin(i,tmp,6); // On met en binaire l équivalent du for(k=step;k<step+6;k++) // caractère lu et trouvé dans la base { // puis on le sauvegarde par mots buff[k]=tmp[k%6]; // de 6 bits dans une variable } // temporaire pour ensuite regrouper } // les quatres mots de 6 bits en } // un seul buffer de 24 bits } } dst[0]=BinToDec(buff,0,7); // qu on pourra ici décomposer dst[1]=BinToDec(buff,8,15); // en 3 mots de 8 bits dst[2]=BinToDec(buff,16,23); // } //-- Lit le fichier et le décode dans le fichier destination ------------------- void DecodeFile(char strSrcFile[256], char strDstFile[256]) { bool loop=true; int rep[4]={0,0,0,0}; int dcd[3]={0,0,0},tmp; unsigned short len; FILE *i_s=fopen(strSrcFile,"rb"); FILE *o_s=fopen(strDstFile,"wb"); if(!i_s) {printf("\\n\\n\\a+ Erreur : %s",ERROR_01); exit(-1);} if(!o_s) {printf("\\n\\n\\a+ Erreur : %s",ERROR_02); exit(-1);} if( strcmp(strSrcFile,"-tmp.-tmp") ) printf("\\nDecoding %s to %s...",strSrcFile,strDstFile); else printf("\\n"); do { len=0; // Un caractere est mis dans une variable temporaire pour tester si on // est sur un saut de ligne, si c est le cas on lit le caractere suivant // et on sauvegarde le caractere d apres dans notre tampon REP, autrement // on sauvegarde directement dans le tampon REP tmp=fgetc(i_s); if(tmp==0xD) { fgetc(i_s); rep[0]=fgetc(i_s); } else rep[0]=tmp; tmp=fgetc(i_s); if(tmp==0xD) { fgetc(i_s); rep[1]=fgetc(i_s); } else rep[1]=tmp; tmp=fgetc(i_s); if(tmp==0xD) { fgetc(i_s); rep[2]=fgetc(i_s); } else rep[2]=tmp; tmp=fgetc(i_s); if(tmp==0xD) { fgetc(i_s); rep[3]=fgetc(i_s); } else rep[3]=tmp; if( rep[0] < 0 ) { rep[0] = 0; break; } if( rep[1] < 0 ) { rep[1] = 0; loop=false; } // Si le caractere est if( rep[2] < 0 ) { rep[2] = 0; loop=false; } // celui de fin de fichier if( rep[3] < 0 ) { rep[2] = 0; loop=false; } // EOF == -1 DecodeBase64(rep,dcd); // On decode les 4 mots // en 3 octets // Une erreur d ecriture > exit. // Puis on les ecrits en if(fputc(dcd[0],o_s)<0) {printf("\\n\\n\\a+ Erreur : %s",ERROR_03); exit(-1);} if( rep[2] > 0 ) fputc(dcd[1],o_s); // verifiant bien qu ils if( rep[3] > 0 ) fputc(dcd[2],o_s); // ne proviennent pas de = // le saut de ligne } while( loop == true ); // Tout ceci tant que // on est pas a la fin. if( strcmp(strSrcFile,"-tmp.-tmp") ) printf("done.\\n"); fclose(i_s); fclose(o_s); } //-- Synopsis ------------------------------------------------------------------ void Usage(int more) { if(more) printf("\\nConvertisseur Base64Encoded (par Nicolas@secureinfo.free.fr)" "\\nB64 [-f|-s] [-e|-d] [input_file|chaine_de_caractere] [output_file] [[-l 80]]" "\\n " "\\n-f : specifie le traitement de fichier " "\\n-s : specifie une chaine de caractere " "\\n-e : encodage en base 64 " "\\n-d : decodage en base 64 " "\\n-l : (optionel) longueur de la ligne encodee \\n" "\\nEncodage d un fichier ............: b64 -f -e test.txt code.txt" "\\nDecodage d un fichier ............: b64 -f -d code.txt decode.txt" "\\nLigne de 50 caracteres ...........: b64 -f -e test.txt code.txt -l 50" "\\nPar defaut le programme encodera un fichier sur une ligne de 72 caracteres" "\\nle minimum de caractere sur une ligne est de 4 caracteres (24bits, 3octets)" "\\nEncodage d une chaine de caractere: b64 -s -e ABCD" "\\nDecodage d une chaine de caractere: b64 -s -d QUJDRA==\\n"); else printf("\\nConvertisseur Base64Encoded" "\\nB64 [-f|-s] [-e|-d] [input_file|chaine_de_caractere] [output_file] [[-l 80]]\\n" "\\nPar defaut le programme encodera un fichier sur une ligne de 72 caracteres" "\\nle minimum de caractere sur une ligne est de 4 caracteres (24bits, 3octets)\\n"); } //-- Ecriture du fichier temporaire -------------------------------------------- void WriteInFile(char *Buffer, char *FileName) { FILE *flux; flux=fopen(FileName,"wb"); if(!flux) {printf("\\n\\n\\a+ Erreur : %s",ERROR_02); exit(-1);} fprintf(flux,"%s",Buffer); fclose(flux); } //-- Affichage du fichier temporaire ------------------------------------------- void ViewFile(char *FileName) { int rep; FILE *flux; flux=fopen(FileName,"rb"); if(!flux) {printf("\\n\\n\\a+ Erreur : %s",ERROR_01); exit(-1);} while( (rep=fgetc(flux)) != EOF ) printf("%c",rep); fclose(flux); unlink("-tmp.-tmp"); unlink("_tmp._tmp"); } //-- Determine l operation a effectuer (encodage, decodage) -------------------- #define is_ok strlen(argv[3])>=1 / && strlen(argv[3])<=256 / && strlen(argv[4])>=1 / && strlen(argv[4])<=256 #define is_ok_s strlen(argv[3])>=1 / && strlen(argv[3])<=1024/ void SelectOperation(int argc, char *argv[]) { // Traitement d une chaine de caractere if( argc == 4 ) { if( ! strcmp(argv[1],"-s") ) { if( ! strcmp(argv[2],"-e") ) // -> Encode { // la chaine de caractere if( is_ok_s ) { // Ecriture dans un fichier temporaire WriteInFile(argv[3],"-tmp.-tmp"); EncodeFile( "-tmp.-tmp" , "_tmp._tmp" , DEFAULT_WIDTH ); ViewFile("_tmp._tmp"); printf("\\n\\n"); } } if( ! strcmp(argv[2],"-d") ) // -> Decode { // la chaine de caractere if( is_ok_s ) { WriteInFile(argv[3],"-tmp.-tmp"); DecodeFile( "-tmp.-tmp" , "_tmp._tmp" ); ViewFile("_tmp._tmp"); printf("\\n\\n"); } } } return; } // Il s agit d encoder ou de decoder un fichier if( argc == 5 ) { if(!strcmp(argv[1],"-f")) // Fichier { if(is_ok) { if( ! strcmp(argv[2],"-e") ) // -> Encode le fichier EncodeFile( argv[3] , argv[4] , DEFAULT_WIDTH ); if( ! strcmp(argv[2],"-d") ) // -> Decode le fichier DecodeFile( argv[3] , argv[4] ); } } return; } // Il s agit d encoder avec une largeur specifiee if( argc == 7 ) { if( ! strcmp(argv[1],"-f") ) { if( is_ok ) { if( ! strcmp(argv[2],"-e") ) if( ! strcmp(argv[5],"-l") ) if( atoi(argv[6]) > 4) EncodeFile( argv[3] , argv[4] , atoi(argv[6])); else {Usage(0);return;} } } return; } // Affiche l aide en complet selon le cas if( argc == 2 ) if( ! strcmp(argv[1],"-h") ) {Usage(1);return;} Usage(0); } //-- Programme principal ------------------------------------------------------- int main(int argc, char *argv[]) { // Selectionne l operation a effectue selon les arguments SelectOperation(argc, argv); }
0x02. UTILITE DANS LES SCRIPTS
Tout simplement de la manière suivante :
#!/bin/bash b64e="/usr/local/bin/b64e" # Decode la chaine base 64 encodée password=$($b64e -s -d "GS8wT5zuGSVjTSVgGSGrYgt0vsd+8sof8gXxG58fTSGuYSVhT5z4TSA4" |cut -d ';' -f2) # Exemple : connexion via MySQL mysql -uroot -hlocalhost -p$password
Après, on peut renforcer le mot de passe en multipliant les encodage ou en changeant les 8 en _ par exemple.
La source étant présente on peut en faire ce qu'on veut.
0x03. ATTAQUER UNE BASE 64 ALTEREE
Pas compliqué, il suffit de substituer à une base 64 normale. Le but de cet article n'est pas d'attaquer une base 64 décalée, mais de protéger du "shoulder hacking", ou de l'attaque qui consiste à regarder par dessus l'épaule.
De plus "GS8wT5zuGSVjTSVgGSGrYgt0vsd+8sof8gXxG58fTSGuYSVhT5z4TSA4" est plus long à retenir que "password"
Exemple :
# b64 -s -e 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrLz0= # b64e -s -e 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' iXFmdzEsdjUFZ6PGSXweXooZXtdEE+vCDEkUCaT6HDHp9s+K9grP2ac48%FfvlEgvyUw75ArG5GjTSCyYm6LBfj3 # On peut déduire que : Q = i U = X ... Alphabet_norm="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" Alphabet_code="AQWmzoslVFRBGTYeidZSXED%CH928v7NJUI56+apqkKLOPM04rufjtgyhwnxbc1@3"
=> Écrit par : Nicolas, le 26 octobre 2010