Un problème récurrent
Il vous est surement arrivé de devoir exploiter un code semblable à celui ci-dessous afin d’ouvrir un shell et ainsi récupérer un flag.
// programme.exe
#include <stdio.h>
#include <stdlib.h>
int main(){
....
....
if ( pass == "OK" ){
printf( "Enter shell\n" );
system( "/bin/bash" );
printf( "Leave shell\n" );
}
....
....
}
Supposons que vous avez réussi à remplir la condition :
pass == "OK"
Pour ce faire vous générez un payload avec python (ou tout autre langage) :
python3 -c 'print("ma_super_payload")' | ./programme.exe
En exécutant cette commande vous avez de grande chance de tomber sur les lignes suivantes :
$ python3 -c 'print("ma_super_payload")' | ./programme.exe
Enter shell
Leave shell
Une histoire de pipes
La première chose à comprendre ici, c’est le concept des pipes. Lorsque vous exécutez la commande
$ python3 -c 'print("ma_super_payload")' | ./programme.exe
Lorsque vous utilisez |
, l’interpréteur de commandes crée un pipe (tube) et lance deux processus : python et programme.exe. La sortie standard de python est connectée (redirigée) à l’entrée du pipe, et la sortie du pipe est connectée à l’entrée standard de programme.exe.
Bien entendu, la sortie de programme.exe est connectée à la sortie terminal, tout comme l’entrée du terminal est connecté à l’entrée standard de python. Lorsque python
se termine après avoir écrit les données sur la sortie standard (donc le pipe), le kernel ferme ses descripteurs de fichiers. L’entrée du pipe, dont l’entrée a été fermée, ajoutera un signal EOF dans son buffer.
Lorsque system( "/bin/bash" )
est lancé, celui-ci tente de lire les données sur le l’entrée standard de programme.exe mais ne rencontre que le caractère ASCII EOF. Le shell lit donc ce caractère et considère qu’il n’y a plus rien à lire sur l’entrée standard et se ferme directement.
Il faut savoir que le caractère EOF signifie "End Of Transmission". Il est donc interprété ainsi par le shell.
La solution
Pour résoudre le problème, il faudrait avoir un script qui mettra votre payload dans la sortie standard (ex: print() sur python) , et qui ensuite, récupérera vos commandes via son entrée standard ( ex: input() sur python) pour les écrire sur sa sortie standard connecté au pipe.
Écrire ce petit programme n’est pas complexe mais pourquoi faire compliqué quand on peut faire simple. En effet, il est possible de faire tout cela grâce à la commande cat
.
Voici le sésame :
cat <( python -c 'print "ma_super_payload"' ) - | ./programme.exe
Explications
Expliquons un peu plus en détails cette commande magique.
La première partie permet de mettre sur l’entrée standard de cat le résultat de notre script python. Attention ici, <(
ne doit pas contenir d’espace.
//programme1 <( programme2 )
cat <( python -c 'print "ma_super_payload"' )
La deuxième partie consiste à dire au programme cat
de ne pas fermer sa sortie standard, même après y avoir écrit le payload. Pour cela on utilise l’argument -
. Grâce à cette argument, cat
va vous demander via son entrée standard ce que vous souhaitez écrire sur sa la sortie standard et cela jusqu’à ce que vous l’arrêtiez avec un [CTRL] + C
par exemple.
cat <( python -c 'print "ma_super_payload"' ) - | ./programme.exe