jeudi 23 juillet 2015

Nebulla - level 10

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