Bonjour,
On commence à
entrer dans des niveaux où le fun arrive. A partir de ce niveau la
compréhension du code C est obligatoire. Vous pouvez bien entendu
tenter de comprendre ma solution mais je vous encourage vivement si
vous êtes perdu à simplement vous documentez (ou a posez vos
questions). L'ip que je vais utiliser sera bien entendu mon ip local ici (192.168.1.43)Je vais tenter d'expliquer au mieux de la manière la
plus simple (à mes yeux) tout en étant technique. Voici ce que l'on
nous demande ici :
The setuid binary at /home/flag10/flag10 binary will upload any file given, as long as it meets the requirements of the access() system call.
To do this level, log in as the level10 account with the password level10. Files for this level can be found in /home/flag10.
On nous donne aussi
un fichier source C plutôt évolué ici sans forcement être
compliqué pour autant :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <stdio.h> #include <fcntl.h> #include <errno.h> #include <sys/socket.h> #include <netinet/in.h> #include <string.h> int main(int argc, char **argv) { char *file; char *host; if(argc < 3) { printf("%s file host\n\tsends file to host if you have access to it\n", argv[0]); exit(1); } file = argv[1]; host = argv[2]; if(access(argv[1], R_OK) == 0) { int fd; int ffd; int rc; struct sockaddr_in sin; char buffer[4096]; printf("Connecting to %s:18211 .. ", host); fflush(stdout); fd = socket(AF_INET, SOCK_STREAM, 0); memset(&sin, 0, sizeof(struct sockaddr_in)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(host); sin.sin_port = htons(18211); if(connect(fd, (void *)&sin, sizeof(struct sockaddr_in)) == -1) { printf("Unable to connect to host %s\n", host); exit(EXIT_FAILURE); } #define HITHERE ".oO Oo.\n" if(write(fd, HITHERE, strlen(HITHERE)) == -1) { printf("Unable to write banner to host %s\n", host); exit(EXIT_FAILURE); } #undef HITHERE printf("Connected!\nSending file .. "); fflush(stdout); ffd = open(file, O_RDONLY); if(ffd == -1) { printf("Damn. Unable to open file\n"); exit(EXIT_FAILURE); } rc = read(ffd, buffer, sizeof(buffer)); if(rc == -1) { printf("Unable to read from file: %s\n", strerror(errno)); exit(EXIT_FAILURE); } write(fd, buffer, rc); printf("wrote file!\n"); } else { printf("You don't have access to %s\n", file); } } |
Nous avons aussi un
fichier token disponible dans la racine du flag de disponible mais
sans droit en lecture :
On voit que le
programme va demander deux arguments ici un fichier un host pour se
connecter (sur le port 18211). Ensuite un test pour se connecter à
l'host puis si connecter une écriture sur le fd (qui est le socket
de l'host donné en paramètre donc). Une fois tout ça passé le
contenu du fichier passé en paramètre sera alors read puis write sur
le fd du socket donc. Et enfin un petit message pour dire que tout
est bon.
Le programme est pas
bien compliqué le seul soucis ici sera lié au moment de lancer les deux
lignes suivante :
if(access(argv[1], R_OK) == 0 ffd = open(file, O_RDONLY);
En effet par le man
on peut comprendre simplement qu'il y a un gros soucis ici je cite
donc :
Utiliser access pour vérifier si un utilisateur a le droit, par exemple, d'ouvrir un fichier avant d'effectuer réellement l'ouverture avec open(2), risque de créer un trou de sécurité. En effet, l'utilisateur peut exploiter le petit intervalle de temps entre la vérification et l'accès pour modifier le fichier (via un lien symbolique en général).
On est exposé ici à
une faille relativement connu du simple nom de TOCTOU qui va
implicitement nous faire utiliser le principe du race condition.
Sachant que nous
sommes ici dans un cas bien réel de race condition ainsi que dans
une utilisation d'un programme qui va se connecter en réseau nous
allons devoir utiliser netcat qui va nous faciliter la tâche je vous
invite à tenter de comprendre de ce logiciel qui est indispensable à
comprendre. Je vais utiliser un
peu de shell basique histoire de simplement gagner du temps.Voici un test
basique pour voir que tout fonctionne bien dans des fichiers sans
restriction :
Donc voila le
résultat du premier test basique avec un fichier dont on a les
droits maintenant avec un fichier sans droit :
Maintenant nous
allons dans l'ordre créer un link forcer (via -f) depuis notre
fichier token vide vers un fichier link token donc et nous allons
link le fichier token sur lequel nous n'avons aucun droit vers ce
même link le tout en boucle infini. Pour ça je vais utiliser une
commande assez basique :
Nous allons ensuite
lancer un client netcat en mode serveur en boucle en ayant une redirection sur sa sortie vers un fichier (ici du nom de out) :
Et enfin voici le
programme lancé lui aussi en boucle infini avec le fichier relink en
boucle :
Après quelques
seconde une simple lecture du fichier out va nous donner le contenu
du fichier token.
Il suffit alors
d'utiliser le mot de passe que l'on a pour récupérer notre flag !
Aucun commentaire:
Enregistrer un commentaire