Cover Image

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 fonction flag(). 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}