Biscuits BITSCTF2025

naibu3 · February 11, 2025

En este último ctf sólo había dos ejercicios de PWN, además bastante sencillito, así que he decidido subir writeup de los dos.


Image

Overview

El enunciado dice lo siguiente:

Momma, can I have cookie..?

No....

Además incluye un binario main.

Reconocimiento

Lo primero será analizar el binario:

❯ file main
main: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=102ac972af333cb069ddd21a54364dbbe2a0d9ed, for GNU/Linux 3.2.0, not stripped
❯ checksec --file=main
[*] '/home/kali/Downloads/biscuits/main'
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        PIE enabled
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No

Vemos que es de 64 bits y que tiene todas las protecciones activas. Si lo descompilamos con ghidra veremos un código como el siguiente:

undefined8 main(void)
{
  int rand_n;
  time_t time;
  size_t end_of_input;
  FILE *__stream;
  long in_FS_OFFSET;
  int i;
  char input [112];
  char cookie [104];
  long canary;
  
  canary = *(long *)(in_FS_OFFSET + 0x28);
  time = ::time((time_t *)0x0);
  srand((uint)time);
  puts("Give me the cookie I want a 100 times in a row and I\'ll give you the flag!");
  fflush(stdout);
  for (i = 0; i < 100; i = i + 1) {
    rand_n = rand();
    strcpy(cookie,*(char **)(cookies + (long)(rand_n % 100) * 8));
    printf("Guess the cookie: ");
    fflush(stdout);
    fgets(input,100,stdin);
    end_of_input = strcspn(input,"\n");
    input[end_of_input] = '\0';
    rand_n = strcmp(input,cookie);
    if (rand_n != 0) {
      printf("Wrong. The cookie I wanted was: %s\n",cookie);
                    /* WARNING: Subroutine does not return */
      exit(0);
    }
    printf("Correct! The cookie was: %s\n",cookie);
    fflush(stdout);
  }
  printf("Congrats!\nFlag: ");
  fflush(stdout);
  __stream = fopen("flag.txt","r");
  if (__stream == (FILE *)0x0) {
    perror("Failed to open flag file");
                    /* WARNING: Subroutine does not return */
    exit(1);
  }
  while( true ) {
    rand_n = fgetc(__stream);
    if ((char)rand_n == -1) break;
    putchar((int)(char)rand_n);
  }
  putchar(10);
  fclose(__stream);
  fflush(stdout);
  if (canary != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

                            cookies                                         XREF[3]:     Entry Point(*), main:001023e9(*), 
                                                                                          main:001023f0(*)  
        00105020 08 30 10        undefine
                 00 00 00 
                 00 00 17 
           00105020 08              undefined108h                     [0]           ?  ->  00103008     XREF[3]:     Entry Point(*), main:001023e9(*), 
                                                                                                                     main:001023f0(*)  
           00105021 30              undefined130h                     [1]
           00105022 10              undefined110h                     [2]
           00105023 00              undefined100h                     [3]
           00105024 00              undefined100h                     [4]
           00105025 00              undefined100h                     [5]
           00105026 00              undefined100h                     [6]
           00105027 00              undefined100h                     [7]
           00105028 17              undefined117h                     [8]           ?  ->  00103017
           00105029 30              undefined130h                     [9]
           0010502a 10              undefined110h                     [10]
           0010502b 00              undefined100h                     [11]
           0010502c 00              undefined100h                     [12]
           0010502d 00              undefined100h                     [13]
           0010502e 00              undefined100h                     [14]
           0010502f 00              undefined100h                     [15]
           00105030 24              undefined124h                     [16]          ?  ->  00103024
           [...]
           00105330 e2              undefined1E2h                     [784]         ?  ->  001035e2
           00105331 35              undefined135h                     [785]
           00105332 10              undefined110h                     [786]
           00105333 00              undefined100h                     [787]
           00105334 00              undefined100h                     [788]
           00105335 00              undefined100h                     [789]
           00105336 00              undefined100h                     [790]
           00105337 00              undefined100h                     [791]
           00105338 fe              undefined1FEh                     [792]         ?  ->  001035fe
           00105339 35              undefined135h                     [793]
           0010533a 10              undefined110h                     [794]
           0010533b 00              undefined100h                     [795]
           0010533c 00              undefined100h                     [796]
           0010533d 00              undefined100h                     [797]
           0010533e 00              undefined100h                     [798]
           0010533f 00              undefined100h

Vemos que el programa pide que vayas introduciendo distintas cadenas que toma aleatroiamente de un array. Si vemos el contenido de las direcciones podemos sacar los valores del array:

        00103008 43 68 6f        ds         "Chocolate Chip"
                 63 6f 6c 
                 61 74 65 
        00103017 53 75 67        ds         "Sugar Cookie"
                 61 72 20 
                 43 6f 6f 
        00103024 4f 61 74        ds         "Oatmeal Raisin"
                 6d 65 61 
                 6c 20 52 
        00103033 50 65 61        ds         "Peanut Butter"
                 6e 75 74 
                 20 42 75 
        00103041 53 6e 69        ds         "Snickerdoodle"
                 63 6b 65 
                 72 64 6f 
        0010304f 53 68 6f        ds         "Shortbread"
                 72 74 62 
                 72 65 61 
        0010305a 47 69 6e        ds         "Gingerbread"
                 67 65 72 
                 62 72 65 
        00103066 4d 61 63        ds         "Macaron"
                 61 72 6f 
                 6e 00

La generación aleatoria se hace mediante la semilla srand((uint)time); y se generan números con rand_n = rand();.

Explotación

Dado que se utiliza como semilla la hora en la que se ejecuta el binario. Podríamos capturar ese istante y utilizarlo para generar números idénticos y así obtener la flag. Para ello lo haremos mediante un script en python3 y pwntools:

#!/usr/bin/env python3
from pwn import *
import ctypes

if len(sys.argv) == 1:
    p = remote('20.244.40.210', 6000)
else:
    p = process('./main')

libc = ctypes.CDLL("libc.so.6")

seed = libc.time(0)
libc.srand(seed)

data = p.recvline().decode().rstrip()
print(data)

table = [0x00103008, 0x00103017, 0x00103024, 0x00103033, 0x00103041,
    0x0010304f, 0x0010305a, 0x00103066, 0x0010306e, 0x00103077, 0x00103080,
    0x0010308e, 0x001030ac, 0x001030c2, 0x001030cd, 0x001030df, 0x001030ee,
    0x001030fc, 0x0010310e, 0x0010311d, 0x00103134, 0x00103144, 0x00103153,
    0x00103160, 0x00103170, 0x00103182, 0x00103192, 0x001031a1, 0x001031b3,
    0x001031c8, 0x001031dc, 0x001031ec, 0x00103204, 0x00103216, 0x0010322a,
    0x0010323f, 0x00103250, 0x00103260, 0x0010326e, 0x0010327c, 0x0010328a,
    0x0010329b, 0x001032a9, 0x001032bb, 0x001032c7, 0x001032d1, 0x001032db,
    0x001032e6, 0x001032f4, 0x001032fe, 0x00103307, 0x00103310, 0x0010331d,
    0x00103328, 0x00103334, 0x00103341, 0x0010334d, 0x00103356, 0x0010335f,
    0x00103368, 0x00103380, 0x0010338a, 0x00103392, 0x00103398, 0x001033a7,
    0x001033b7, 0x001033c6, 0x001033d4, 0x001033e6, 0x001033ed, 0x001033ff,
    0x0010340f, 0x0010341d, 0x00103428, 0x00103435, 0x0010343f, 0x0010344e,
    0x0010345f, 0x00103471, 0x0010347d, 0x00103489, 0x00103492, 0x001034a1,
    0x001034b1, 0x001034c5, 0x001034d4, 0x001034eb, 0x001034fb, 0x00103513,
    0x00103528, 0x0010353d, 0x00103557, 0x0010356d, 0x00103582, 0x00103591,
    0x001035a3, 0x001035b3, 0x001035ca, 0x001035e2, 0x001035fe]

with open('main', 'rb') as f:
    bin_data = f.read()

for i in range(100):
    r = libc.rand()
    offset = table[r % 100] - 0x00100000
    end = bin_data.find(b'\x00', offset)
    cookie = bin_data[offset:end].decode()

    data = p.recvuntil(b': ').decode()
    print(data + cookie)
    p.sendline(cookie.encode())
    data = p.recvline().decode().rstrip()
    print(data)

for _ in range(2):
    data = p.recvline().decode().rstrip()
    print(data)

Ejecutamos y tendríamos la flag! Si te ha gustado, échale un vistazo a mis otros writeups y no dudes en seguirme en redes sociales.

Reconocimiento al writeup original (en Japonés).


Twitter, Facebook