FCSC 2023 - Zéro Pointé
Table of Contents
In this article, we will explore the step-by-step walkthrough of the ‘Zéro Pointé’ challenge presented at FCSC 2023.
In this challenge, we exploited a vulnerability related to an integer overflow in a C program to trigger a
SIGFPEsignal and execute theflag()function. The code reads two long values, performs a division, and handles a specific case of division by zero.
Challenge Overview
We have the source code zero-pointe.c available:
You can download the binary here: zero-pointe
Code Analysis
#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);
}
Code Overview
The source code provides an overview of the main parts of the program:
- The
flag()function reads and displays the contents of the"flag.txt"file. - The
read_long()function reads a long value from standard input. - The main function reads two long values (a and b) and performs the division
a / b.
The goal is to trigger a SIGFPE signal, which calls the flag() function and displays the contents of the "flag.txt" file. The SIGFPE signal is triggered when a calculation exception occurs, such as division by zero or integer overflow.
Exploitation Strategy
After examining the code, we determined that the best approach to trigger the SIGFPE signal was to cause an integer overflow during the division a / b.
To do this, we sent the following values:
a: -9223372036854775808
b: -1
These values were chosen for the following reasons:
- a is equal to
LONG_MIN, which is the smallest value that a long can store on a 64-bit system. - b is equal to
-1, which ensures that division by zero does not occur, as the code already handles this specific case.
When the code performs the division a / b, it results in an integer overflow.
Indeed, dividing LONG_MIN by -1 would give a result greater than LONG_MAX, which cannot be represented by a long.
This division with integer overflow triggers a SIGFPE signal, which calls the flag() function and displays the contents of the "flag.txt" file.
Result
Here is what it looks like during the challenge:
$ nc challenges.france-cybersecurity-challenge.fr 2050
-9223372036854775808
-1
FCSC{0366ff5c59934da7301c0fc6cf7d617c99ad6f758831b1dc70378e59d1e060bf}
Flag
The flag is therefore FCSC{0366ff5c59934da7301c0fc6cf7d617c99ad6f758831b1dc70378e59d1e060bf}