25/05/2006 à 15h17 : Challenge Securitech : mes solutions...

Bon je vais présenter dans ce billet les éléments qui m'ont permis de marquer quelques points...
En espérant que ça pourra être utile pour certains.
Pour comprendre quelque chose, je vous invite à consulter les sujets sur le site officiel.
Le code que je donne ici est "brut", c'est à dire tel quel au moment où j'ai validé l'épreuve.
C'est donc du "quick & dirty" et je préfère ne pas remanier le code... vous allez donc voir que je code comme un porc !


Challenge 9

J'ai commencé par ce challenge car j'ai vu que c'était le premier où d'autres membres ont réussi à marquer des points.
On a donc 50 images de 25x1731 pixels, qu'il faut remettre dans l'ordre.
J'imagine que la solution "manuelle" est possible, mais c'est quand même plus sympa avec un programme.
Le sujet suggère d'ailleurs une solution information en utilisant QTimage.
Personnellement, je connais déjà quelques fonctions pratiques avec SDL (grâce à zeRace notamment).
Donc l'idée est de charger toutes les bandes de papier en mémoire, puis de les trier.
On prend une bande de papier, et on la compare à toute les autres.
On parcoure la bande de pixel à droite de la première bande et la bande de pixel à gauche de la seconde.
L'image est en niveau de gris, donc j'ai fixé un seuil arbitraire : 150. Au dessous de cette intensité, le pixel est considéré comme "noir".
A hauteur égale sur la bande de papier, si le point est noir à gauche et à droite, alors on a un point de concordance, sinon on diminue le score.
Une fois qu'on a parcouru toutes les bandes, on garde celle qui a le meilleur score, c'est celle qui va à droite de la bande qu'on a choisi.
Une première boucle me permet de vérifier que la bande "43" est la première, donc je met cette valeur comme bande de début, et lorsque je trouve la suivante, je la prend comme nouvelle bande.
A chaque itération, j'affiche la bande sur la sortie standard, et je continue pour toutes les autres bandes.
A la fin j'ai donc toutes les numéros de bande dans un ordre, que je met en dur dans le code et qui me permet d'afficher toutes* les bandes dans l'ordre à l'écran, et de lire le texte.
* en fait presque toutes les bandes, ma technique ne marche pas ou mal pour les bandes "blanches" qui sont sur le bord de l'image...

Voici le code :
#include <SDL_image.h>

Uint8 getpixel(SDL_Surface *surface, int x, int y)
{
	int bpp = surface->format->BytesPerPixel;
	Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
	return p[0];
}

int main(void)
{
	SDL_Surface *f[50];
	SDL_Surface *screen;
	SDL_Rect pos;
	char temp[20];
	int i,j,x,y,g,d,match;
	int best,ii,jj;
	int n=0;
	for (i=0;i<50;i++)
	{
		sprintf(temp,"f.%.2d.bmp",i);
		f[i]=IMG_Load(temp);
	}
	
	i=43;
	while (n++<50)
	{
		best=-999999;
		for (j=0;j<50;j++) if (i!=j) // 25 x 1731 | 0..24 x 0..1730
		{
			match=0;
			for (y=0;y<1730;y++)
			{
				d=getpixel(f[i],24,y)<150;
				g=getpixel(f[j],0 ,y)<150;
				if (d || g)
				{
					if (d == g) match++;
					else match--;
				}
			}
			if (match>best)
			{
				ii=i;
				jj=j;
				best=match;
			}
		}
		printf("%d,",ii,jj);
		i=jj;
	}
	printf("\n");

	int good[50]={43,15,45,30,48,2,29,12,16,10,26,21,11,8,27,44,13,46,23,49,22,28,
	              19,24,32,39,42,9,4,20,40,41,36,37,25,47,0,5,6,3,31,1,35,14,7,17};
	SDL_Init(SDL_INIT_VIDEO);
	int flags=SDL_HWSURFACE|SDL_ANYFORMAT;
	screen=SDL_SetVideoMode(25*50,1731,32,flags);
	for (i=0;i<50;i++)
	{
		pos.x=i*25;
		pos.y=0;
		pos.w=25;
		pos.h=1730;
		SDL_BlitSurface(f[good[i]],NULL,screen,&pos);
	}
      	for (;;)
	{
		SDL_Flip(screen);
		SDL_Delay(500);
	}
}
Pour compiler : gcc niv9.c -o niv9 `sdl-config --cflags --libs` -lSDL_image
Il ne reste plus qu'à lire et à extraire le mot souhaité.
Certains on trouvé une 2ème solution pour ce niveau, je pense à une truc de stégano mais je n'ai pas trouvé...


Challenge 10

J'ai continué sur le challenge 10 car il était disponible assez tôt dans le tournoi.
Autant le dire tout de suite je n'ai pas terminé ce niveau, et j'y ai passé pas mal de temps donc je ne me souviens plus exactement dans quel ordre j'ai trouvé quoi...
Au début j'ai lancé plein de fois la commande avec des paramètres différents pour "voir" ce qui se passait.
royale@royale:~/sec/10$ ./poeut XXXXXXXXXXXXXXXXXXXX
1CB17F857D5B4BAB8268D22F376CE61C-352-59969536-0C20C20C20C20C20C2937A584F13BA3BA3BA3BA
Les tirets en les différents paquets font penser qu'il y a concaténation de plusieurs choses. Le premier paquet fait 32 caractères, et donc je teste des fonctions "connues".
royale@royale:~/sec/10$ echo -n XXXXXXXXXXXXXXXXXXXX | md5sum
1cb17f857d5b4bab8268d22f376ce61c  -
Bingo. Mais bon ce n'est pas très utile vu que MD5 n'est pas réversible.
En testant d'autres chaines ciblées, on arrive à trouver la formule derrière les 2ème et 3ème portions.
Le 2ème paquet est la somme des 4 premiers caractères, et les 4 suivants le "produit" des 4 premiers caractères.
Ca nous donne une petite équation, dont on sait que les solutions sont entières.
Ensuite on s'attaque à la dernière portion, la plus complexe, mais aussi la plus "réversible" qui va nous permettre de trouver quelques caractères.
Vu que la formule ne saute pas aux yeux, j'ai essayé de regarder dans le code binaire... Avec un éditeur hexadecimal on trouve les paroles de "Be quick or be dead" de Iron Maiden... Je pense que ça sert juste à "obscurcir" le code et que ça n'a pas d'utilité (quoique je n'ai pas fini l'épreuve donc certains caractères me manquent encore).
J'ai passé le binaire dans 2 désassembleurs : W32DASM que je connaissais un peu ne comprend pas grand chose au code, et la démo de IDA que je découvre est déjà plus doué mais le code est très difficile à lire. Je pense qu'un débogueur genre SoftICE aurait eu plus de succès mais je n'ai pas eu le courage de me pencher la dessus ;)
Je continue donc sur la technique "au petit bonheur la chance".
En "brute forçant" un peu on arrive à isoler le début de la formule : les caractères 5 à 10 codes la première portion du dernier paquet, le 10ème caractère de la chaine d'entrée détermine les 3 premiers caractères, le 9ème les 3 suivants, etc.
Je "pense" donc que la chaine solution est de la forme : ----iopkdm----------.
On trouve d'autres portions "probables" par brute force, peut-être un "mi" à la fin ?
Les caractères 11 à 16 participent à une formule commune sur laquelle j'ai buté... Ils donnent une portion de la chaine de sortie, par exemple :
----iopkdm---a----mi: FCDB2D33
----iopkdm---b----mi: FE9D936A
----iopkdm---c----mi: FF5FF95D
----iopkdm---d----mi: FA10EFD8
----iopkdm---e----mi: FBD285EF
----iopkdm---f----mi: F9943BB6
----iopkdm---g----mi: F8565181
----iopkdm---h----mi: F30A16BC
----iopkdm---i----mi: F2C87C8B
----iopkdm---j----mi: F08EC2D2
----iopkdm---k----mi: F14CA8E5
----iopkdm---l----mi: F403BE60
----iopkdm---m----mi: F5C1D457
----iopkdm---n----mi: F7876A0E
----iopkdm---o----mi: F6450039
----iopkdm---p----mi: E13FE474
----iopkdm---q----mi: E0FD8E43
----iopkdm---r----mi: E2BB301A
----iopkdm---s----mi: E3795A2D
----iopkdm---t----mi: E6364CA8
----iopkdm---u----mi: E7F4269F
----iopkdm---v----mi: E5B298C6
----iopkdm---w----mi: E470F2F1
----iopkdm---x----mi: EF2CB5CC
----iopkdm---y----mi: EEEEDFFB
----iopkdm---z----mi: ECA861A2
J'ai isolé les caractères qui m'intéressent en sortie, et on devine une logique (mais laquelle ?).
Bref j'ai arrêté là vu que ça coince, mais j'espèrais qu'avec ces 6 caractères et les contraintes qu'on a sur les 4 premiers, le problème doit pouvoir ce finir par "brute force", par exemple en vérifiant le MD5.
Et j'ai squizzé mes expériences les plus obscures vu qu'il n'y a pas grand chose qui en ressort...


Challenge 14

On attaque la série des challenges "injections SQL". Ces exos ont mieux marché pour moi vu que je suis plus au courant de ce genre de problèmes lors de la création/maintenance de sites webs.
Dans la trace fournie pour cet exo, on trouve l'adresse d'un site web et un login / password valide.
Dans la page de "recherche" d'amis, la variable "hobbies" est vulnérable à une injection SQL.
Par exemple, on recopie le formulaire chez soi et on le bidouille avant de le soumettre. J'ai mis les champs "hobbies[]" en texte libre au lieu de cases à cocher, comme ça je peux y injecter ce que je veux. Si ça ne marche pas je reviens en arrière et je tente autre chose...
Si on y met "toto" on obtient un message d'erreur du genre "column `toto` invalid".
On devine que la table a une colonne "id" vu que c'est un des paramètres du formulaire dans d'autres pages.
Si on met "id" dans la variable hobbies[], on obtient "Arnaud" comme résultat, qui est le membre d'identifiant 1.
Si on essaye d'injecter une UNION, par exemple en mettant dans la variable hobbies[] :
id`=1 UNION SELECT *** FROM ***
On obtient une erreur car la fin de la requête SQL qu'on ne maitrise pas plante. Heureusement on peut la commenter avec le caractère #.
On veut savoir quel sont les noms des tables (on ne sait pas encore), donc on essaye différentes méthodes, et celle de MySQL 5 fonctionne, on injecte :
id`=1 UNION SELECT table_name,2,3 FROM INFORMATION_SCHEMA.TABLES #
Le 2 et le 3 en dur servent à avoir le bon nombre de colonnes pour l'union, sinon on a un message d'erreur. Du coup on "voit" le nom des tables s'afficher dans le formulaire de sortie, à la place des identifiants des membres qui correspondent à la recherche.
Les tables "SECRET_DATA" et "_USER_E_MEET_TB_" en particulier attirent l'oeil.
Pour trouver les colonnes de ces tables, on injecte :
id`=1 UNION SELECT column_name,2,3 FROM INFORMATION_SCHEMA.columns #
Petite astuce supplémentaire, la table "SECRET_DATA" n'est pas dans le même "schema" que les autres tables, il faut donc la préfixer.
On arrive à récupérer une première solution avec l'injection suivante :
id`=1 UNION SELECT 1,text,3 FROM PRIVATE_DATA.SECRET_DATA #
Qui affiche directement la colonne "text" de l'unique ligne de la table SECRET_DATA qui contient la solution.
On arrive aussi à trouver des infos sympas dans la table "_USER_E_MEET_TB_" :
id`=1 UNION SELECT 1,username,password FROM _USER_E_MEET_TB_ #
Mais les mots de passe semblent difficiles à casser, donc je n'ai pas poussé plus loin...


Challenge 4

Un challenge de cryptographie, dont on devine que la solution est "abordable" comme le texte n'est pas entièrement crypté.
La ponctuation est inchangée, et les lettres restent des lettres...
Au début j'essaye sans succès une approche statistique. On trouve plusieurs fois un mot qui semble être le même et qui se fini pareil :
MUABJ-KHCWFC
QZBKJ-QELMFC
ZFRIS-PUQLFC
YOZNW-GUQLFC
FWFMM-PUQLFC
UACEI-OVVLFC
EHIKE-KUQLFC
MICEY-UUQLFC
XCZUV-GUQLFC
YOQTQ-GUQLFC
UFRIS-PUQLFC
ESHEX-TRQLFC
FQBGX-KECSFC
OHVOI-UUQLFC
...
Le problème c'est qu'il y a beaucoup de mots qui peuvent correspondre à ce motif :
royale@royale:~/sec/4$ egrep '^.....-......$' /usr/share/dict/french 
aigue-marine
amour-propre
amuse-gueule
anglo-saxons
après-demain
après-guerre
après-rasage
avant-centre
avant-guerre
avant-postes
avant-projet
avant-propos
avant-trains
avant-veille
...
Donc je ne vais pas plus loin sur cette piste.
On essaye donc des algorithmes "classiques" comme Cesar, Vigenere (je découvre les noms officiels en même temps que le challenge).
Pour Vigenere, une technique d'attaque classique est celle du "mot probable" : http://www.apprendre-en-ligne.net/crypto/vigenere/motprobvig.html.
Ici, on ne sait pas de quoi le texte parle, mais on a la ponctuation qui aide à deviner des mots possibles.
Je vous passe tous mes essais infructueux... Puis j'ai fini par tomber sur :
YX'ENJBXZV'XOI
Il n'y a pas beaucoup de cas possibles où on a 2 apostrophes, et surtout en fin de mot. Le bloc est en fait : "QU'AUJOURD'HUI".
J'ai le petit programme suivant qui me donne "l'écart" entre le mot codé et le mot en clair, et j'espère y trouver la clef :
int main(int argc, char *argv[])
{
	int i;
	int t;
	for (i=0;i<strlen(argv[1]);i++) if (argv[1][i]>='A' && argv[1][i]<='Z')
	{
		t=argv[1][i]-argv[2][i];
		printf("%d ",t);
	}
	printf("\n");
	for (i=0;i<strlen(argv[1]);i++) if (argv[1][i]>='A' && argv[1][i]<='Z')
	{
		t=argv[1][i]-argv[2][i];
		if (t<0) t+=26;
		printf("%c",t+'A');
	}
	printf("\n");
}
Mais ça ne me donne rien d'exploitable :
royale@royale:~/sec/4$ ./vig YXENJBXZVXOI QUAUJOURDHUI
8 3 4 -7 0 -13 3 8 18 16 -6 0 
IDETANDISQUA
En continuant la même approche, je tombe sur une autre portion du texte :
- PH ! WNX, EC'L T-T-PZ YVSIPE ?
Les multiples tirets font penser à "A-T-IL", le "..'." est sûrement "QU'Y" vu qu'on a une forme intérrogative. Par extension je suppose que la phrase complète est peut-être :
- AH ! NON, QU'Y A-T-IL ENCORE ?
Et effectivement le résultat est meilleur :
royale@royale:~/sec/4$ ./vig PHWNXECLTTPZYVSIPE AHNONQUYATILENCORE
15 0 9 -1 10 -12 -18 -13 19 0 7 14 20 8 16 -6 -2 0 
PAJZKOINTAHOUIQUYA
La fin de la chaine de code supposée est bizarrement "AH OUI QU'Y A", en fait je me suis trompé, ce n'est pas "AH NON" mais "AH OUI" :
royale@royale:~/sec/4$ ./vig PHWNXECLTTPZYVSIPE AHOUIQUYATILENCORE
15 0 8 -7 15 -12 -18 -13 19 0 7 14 20 8 16 -6 -2 0 
PAITPOINTAHOUIQUYA
En fait c'est une variante de Vigénère, où le code est le texte lui même, décalé de 9 caractères.
Cette clef me permet de déchiffrer le texte à partir de la phrase "AH OUI QU'Y A-T-IL ENCORE...", mais forcément le mot que je cherche (la solution de l'énigme) est avant cette phrase, et mon approche ne me permet pas de remonter.
Mais avec la fin du texte et une recherche ciblée sur Google, on retrouve le texte complet original.
Avec ce texte on retrouve le début de la clef, et on peut déchiffrer tout le texte (oui il faut encore le déchiffrer car le mot est volontairement mal orthographié dans la solution de l'énigme).
Voici mon décodeur :
#include <stdio.h>

int main(int argc, char *argv[])
{
	char last[10]="RECLUSION";
	char c;
	int i;
	while (!feof(stdin))
	{
		c=getchar();
		if (c>='A' && c<='Z')
		{
			c-=last[0]-'A';
			if (c<'A') c+=26;
			for (i=0;i<8;i++) last[i]=last[i+1];
			last[8]=c;
		}
		putchar(c);
	}
}
Avec le programme fourni en annexe, on extrait facilement le mot solution.
Un effet amusant de la méthode de crypto utilisée est ce que j'ai remarqué au début, le fait que certaines statistiques de mots apparaissent.


Challenge 2

Bon là je n'ai pas gardé tous les détails... Mais l'application est encore une fois vulnérable à une injection SQL.
Une simple apostrophe suffit à faire planter le programme, qui en plus est "bavard" et donne des explications.
Plus précisément, c'est de l'XPath injection : http://www.webappsec.org/projects/threat/classes/xpath_injection.shtml.
Bref, on arrive à l'injection suivante :
' or position()=2 or ''=
Qui nous logue en admin.
Il y a également une 2ème injection possible sur l'autre champ :
' union select 1,'a',* from __confidential__ offset '0
Qui donne une autre solution.


Challenge 1

Là encore une injection SQL, l'apostrophe fait planter le formulaire qui donne beaucoup (trop) de détails.
On devine une requête de vérification du login/password de la forme :
SELECT ... FROM membres WHERE login='$login' AND password='$password';
En mettant ' OR 'a'<>' dans les 2 variables, on passe en force, puisque le serveur execute une requête bidon :
SELECT ... FROM membres WHERE login='' OR 'a'<>'' AND password='' OR 'a'<>'';
Mais il y en a un deuxième formulaire d'authentification derrière que je n'ai pas réussi à passer.


Bon je passe rapidement sur les échecs : Je n'ai pas eu le temps de regarder les autres sujets, ou bien j'ai juste "zieuté" pour deviner qu'ils étaient difficiles :p

En tout cas l'expérience était intéressante, et le classement général fait peur puisqu'il y a des acharnés qui ont tout trouvé (ou presque) !

5 commentaire(s)...

21/05/2006 à 18h36 : Aïe aïe aïe

Je ne blogue plus, c'est mal (tm) !
Mais je suis en vacances la semaine prochaine, donc je vais avoir plein de chose à raconter (sur mon blog ou sur notre blog).
Ces derniers jours, j'ai essayé de trouver un peu de temps libre pour participer au Challenge Securitech... Nostalgie de Prologin et intérêt pour le sujet...
Je craignais de faire chou blanc mais finalement j'ai réussi à marquer quelques points (4400 plus précisément, soit une 71ème place).
D'ailleurs si je trouve un peu de temps et/ou si ça intéresse quelqu'un, je posterai un billet avec mes solutions ;-)

5 commentaire(s)...

03/05/2006 à 20h35 : Lan oldskool

Ce week-end j'ai participé à une lan avec quelques anciens zeRezo.
Au programme : Counter-Strike et Trackmania Nations essentiellement.


On peut voir que l'on vieillit au nombre d'ordinateurs portables (boulot ou perso) présents :-)

5 commentaire(s)...

02/05/2006 à 22h43 : The IT Crowd : sélection de T-shirts !

Comme quoi il me reste de quoi faire pour compléter ma collection :











0 commentaire(s)...

02/05/2006 à 12h12 : Debian GNU/Linux on the ACER Aspire T160-MB7Z (amd64)



ACER


with AMD64


running Debian GNU


Linux

I just bought a new computer, an ACER Aspire T160-MB7Z based on a AMD Athlon 64bit processor.
And of course I wanted to run Debian GNU/Linux using 64bit technology on it :)
Usually I use the netinst CD to install a new Debian system, so this is what I tried first.
Unfortunately, the current image for AMD64 (debian-31r0a-amd64-netinst.iso) does not detect properly the SATA chipset, and so I could not start the installation process.
I'm going to give you the steps I had to follow, in case you want to do the same.
<disclaimer> Please note this is not a "howto" and I won't update this article, so it's just what I did and it worked, but it may not work later... </disclaimer>

First you should install a simple network card (a Realtek for example working with the 8139too driver) because the internal card which should work with "forcedeth" driver is not well recognized (at least it did not work for me). Opening the computer voids the warranty, so just give a try first with the forcedeth driver ;)
You should also disable USB during the installation, because the memory card reader will map the sda/sdb/sdc/sdd drives, and so the SATA disk becomes sde which seems to break the installation process (kernel panic after reboot).
I used the unofficial installer here: http://kmuto.jp/debian/d-i/. This is a minimal netinst installer, so you need an internet network connectivity to complete the installation process.
Burn the ISO image, boot it and follow the instructions here: http://kmuto.jp/b.cgi/debian/d-i-2615-amd64.htm.
When you can, you need to switch to another console (alt-F2) to manually load the module for SATA : modprobe sata_nv.
You will have to go back to the partition screen menu to see your sata drive.
After the installation reboot, you can switch back to standard Debian archives using your favorite mirror.
If you need specific stuff such as: Flash Player in Mozilla/Firefox, mplayer with w32codecs and/or OpenOffice.org, you may want to chroot a part of your drive to Debian 32bit, follow this howto.
Good luck!

0 commentaire(s)...

Archives...

07/2007 ] [ 04/2007 ] [ 03/2007 ] [ 02/2007 ] [ 01/2007 ] [ 12/2006 ] [ 11/2006 ] [ 10/2006 ] [ 09/2006 ] [ 08/2006 ] [ 07/2006 ] [ 06/2006 ] [ 05/2006 ] [ 04/2006 ] [ 03/2006 ] [ 02/2006 ] [ 01/2006 ] [ 12/2005 ] [ 11/2005 ] [ 10/2005 ] [ 09/2005 ] [ 08/2005 ] [ 07/2005 ] [ 06/2005 ] [ 05/2005 ] [ 04/2005 ] [ 03/2005 ] [ 02/2005 ] [ 01/2005 ] [ 12/2004 ] [ 11/2004 ] [ 10/2004 ] [ 09/2004 ] [ 08/2004 ] [ 07/2004 ] [ 06/2004 ] [ 05/2004 ] [ 04/2004 ] [ 03/2004 ] [ 02/2004 ] [ 01/2004 ] [ 12/2003 ] [ 11/2003 ] [ 10/2003 ] [ 09/2003 ] [ 08/2003 ] [ RSS ] [ RSS commentaires ]