Exemple sur cas pratique

Kaddate |

Prennons l'exemple de ce programme (vous en aurez des similaires en TP):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv) {
  if (strncmp("\xde\xad\xbe\xef", argv[1], 4)) {
    puts("Nope");
    exit(1);
  }
  printf("Good job the password is : ");
  char encrypted[12] = "\x96\xcc\xc6\x80\xac\xef\xdb\xa3\xb7\xc6\xdb\x00";
  for (int i = 0; i < strlen(encrypted); i++) {
    printf("%c", encrypted[i] ^ "\xde\xad\xbe\xef"[i % 4]);
  }
  puts("");
  return 0;
}

On peut le compiler avec gcc -o example example.c. Puis on peut ensuite lancer :

  c ./example 
[1]    36644 segmentation fault (core dumped)  ./example
➜  c ./example Hello
Nope

Ok donc le programme demande un password en deuxième argument de commande. On peut commencer par identifier le type du fichier.

  c file ./example                                            
./example: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=742b049b6d975eca162f4705235bc8a40ca134ba, for GNU/Linux 4.4.0, not stripped

La commande file nous indique que c'est un fichier ELF, le format standard d'exécutable sous linux, on peut aussi voir qu'il est compilé dynamiquement, ça veut dire que le programme va utiliser les librairies externes au programme qu'il va venir charger en mémoire au lancement.

On peut tracer les librairies appellées avec la commande ldd.

  c ldd ./example
    linux-vdso.so.1 (0x00007f49b3575000)
    libc.so.6 => /usr/lib/libc.so.6 (0x00007f49b3200000)
    /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f49b3577000)

Dans ce cas on peut voir que le programme appelle ld-linux.so qui est la librairie principale qui va charger le programme en mémoire. On voit aussi l'appel à libc.so, la librairies principale sous linux qui contient énormément d'utilitaires.

À la vue de cette analyse, le programme ne semble pas charger de librarie particulière à part les libraries les plus courantes habituelles. Maintenant qu'on a tracé les libraries, on peut tracer les fonctions des librairies externes qui sont appellées. On peut faire ça avec la commande ltrace (pour library trace).

  c ltrace ./example Hello       
strncmp("\336\255\276\357", "Hello", 4)                                                         = 150
puts("Nope"Nope
)                                                                                    = 5
exit(1 <no return ...>
+++ exited (status 1) +++

Ok donc le programme appelle trois fonctions externes : strncmp(), puts(), exit(). puts() permet d'afficher du texte à l'écran et exit() de quitter le programme. strncmp() par contre vient de string compare n et permet de comparer deux strings entre elles.

strncmp("\336\255\276\357", "Hello", 4)

Le deuxieme argument correspond à notre argument de commande, et le premier à la string contre laquelle notre "Hello" est comparée. On peut essayer de remplacer notre "Hello" par la même strings, et voir ce qu'il se passe.

  c ./example `printf "\336\255\276\357"`
Good job the password is : HaxorBeLike

Ok ! on retrouve le flag, le mot de passe était donc directement dans le programme sans obfuscation intermédiaire, ou système de hashage. Dans ce cas précis, j'ai fait le solve avec des outils "dynamique" puisqu'on a tracé en temps réel le programme. je vous laisse tester la méthode statique ;) .