FCSC 2023 - Zéro Pointé
Dans cet article, nous allons explorer le déroulement pas à pas (walkthrough) du challenge ‘Zéro Pointé’ proposé lors du FCSC 2023.
Dans ce défi, nous avons exploité une vulnérabilité liée à un débordement d’entier dans un programme C pour déclencher un signal
SIGFPE
et exécuter la fonctionflag()
. Le code lit deux valeurs long, effectue une division, et traite un cas spécifique de division par zéro.
Analyse du code
À notre disposition nous avons le code source zero-pointe.c:
Vous pouvez télécharger le binaire ici: zero-pointe
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
static void flag(int sig) {
(void) sig;
char flag[128];
int fd = open("flag.txt", O_RDONLY);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
int n = read(fd, flag, sizeof(flag));
if (n == -1) {
perror("read");
exit(EXIT_FAILURE);
}
flag[n] = 0;
flag[strstr(flag, "\n") - flag] = 0;
if (close(fd) == -1) {
perror("close");
exit(EXIT_FAILURE);
}
printf("%s\n", flag);
exit(EXIT_SUCCESS);
}
long read_long() {
long val;
scanf("%ld", &val);
return val;
}
int main() {
long a;
long b;
long c;
if (signal(SIGFPE, flag) == SIG_ERR) {
perror("signal");
exit(EXIT_FAILURE);
}
a = read_long();
b = read_long();
c = b ? a / b : 0;
printf("%ld\n", c);
exit(EXIT_SUCCESS);
}
Le code source donne un aperçu des principales parties du programme:
- La fonction
flag()
lit et affiche le contenu du fichier"flag.txt"
. - La fonction
read_long()
lit une valeur long à partir de l’entrée standard. - La fonction main lit deux valeurs long (a et b) et effectue la division
a / b
.
Le but est de déclencher un signal SIGFPE
, qui appelle la fonction flag()
et affiche le contenu du fichier "flag.txt"
. Le signal SIGFPE
est déclenché lorsqu’une exception de calcul se produit, par exemple une division par zéro ou un débordement d’entier.
Exploitation
Après avoir examiné le code, nous avons déterminé que la meilleure approche pour déclencher le signal SIGFPE
consistait à provoquer un débordement d’entier lors de la division a / b
.
Pour ce faire, nous avons envoyé les valeurs suivantes:
a = -9223372036854775808
b = -1
Ces valeurs ont été choisies pour les raisons suivantes:
- a est égal à
LONG_MIN
, qui est la valeur la plus petite qu’un long peut stocker sur un système 64 bits. - b est égal à
-1
, ce qui garantit que la division par zéro ne se produit pas, car le code traite déjà ce cas spécifique.
Lorsque le code effectue la division a / b
, il en résulte un débordement d’entier.
En effet, la division de LONG_MIN
par -1
donnerait un résultat supérieur à LONG_MAX
, qui ne peut pas être représenté par un long.
Cette division avec débordement d’entier déclenche un signal SIGFPE
, qui appelle la fonction flag()
et affiche le contenu du fichier "flag.txt"
.
Démo
Voici ce que ça donne pendant le challenge:
$ nc challenges.france-cybersecurity-challenge.fr 2050
-9223372036854775808
-1
FCSC{0366ff5c59934da7301c0fc6cf7d617c99ad6f758831b1dc70378e59d1e060bf}
Le flag est donc FCSC{0366ff5c59934da7301c0fc6cf7d617c99ad6f758831b1dc70378e59d1e060bf}