vr-school picoMini by redpwn Solution

Published: April 2, 2026

Description

Welcome to VR school.

Connect to the challenge server with netcat.

Download the binary and libc if provided for local testing.

bash
nc <challenge_host> <PORT_FROM_INSTANCE>
bash
wget <challenge_url>/vr-school  # binary for local analysis
  1. Step 1Analyze binary protections and heap behavior
    Use checksec to enumerate binary mitigations, then run the binary locally under GDB with pwndbg/peda to understand the heap allocation pattern and identify the vulnerability.
    bash
    checksec --file=vr-school
    bash
    gdb -q ./vr-school
    bash
    # Inside GDB with pwndbg:
    bash
    # run, heap, bins, vis_heap_chunks
    Learn more

    checksec reports which binary hardening features are enabled: NX (non-executable stack), PIE (position-independent executable), stack canaries, RELRO (relocation read-only), and FORTIFY. These determine which exploitation techniques are viable. For heap challenges, PIE and full RELRO are the most impactful - they prevent leaking code addresses and overwriting the GOT, respectively.

    pwndbg is a GDB plug-in that adds heap-aware commands: heap lists all allocated chunks, bins shows the tcache/fastbin/unsorted bin contents, and vis_heap_chunks renders the heap layout as a color-coded diagram. These tools make it easy to visualize the heap state at any point in execution.

  2. Step 2Identify and trigger the heap vulnerability
    Interact with the program&apos;s menu to understand what heap operations are exposed. Look for a use-after-free (UAF) or tcache poisoning opportunity - allocate chunks, free them, and observe whether freed pointers can still be accessed.
    Learn more

    Tcache poisoning is an attack against glibc's per-thread caching mechanism introduced in glibc 2.26. Each tcache bin stores a singly-linked list of freed chunks of the same size. The forward pointer (fd) of each freed chunk points to the next chunk in the bin. By overwriting a freed chunk's fd pointer with an arbitrary address, the attacker causes malloc() to return that address on a subsequent allocation - achieving arbitrary write.

    Use-After-Free (UAF) occurs when a program continues to use a pointer after the memory it points to has been freed. In glibc, freed heap chunks' first 8 bytes become the fd pointer. If the program lets you write to a freed chunk, you can overwrite this pointer. The next malloc() of the same size returns the poisoned address.

  3. Step 3Leak libc base and overwrite a function pointer
    Use the heap vulnerability to leak a libc address (by reading the fd of an unsorted bin chunk), calculate the libc base, then overwrite __free_hook or a GOT entry with the address of system. Pass /bin/sh as the next freed chunk to pop a shell.
    bash
    # pwntools skeleton
    python
    from pwn import *
    bash
    p = process('./vr-school')
    bash
    # ... exploit steps ...
    bash
    p.interactive()
    Learn more

    ASLR randomizes the base address of libc each run, so hard-coding addresses fails. To defeat ASLR, leak a runtime libc pointer first. Chunks freed into the unsorted bin (size > 0x408) have their fd and bk pointers set to an address inside main_arena in libc - reading these leaks the libc base.

    __free_hook is a glibc function pointer that, if non-NULL, is called by free() instead of the real deallocation logic. Overwriting it with system and then calling free(ptr) where *ptr = "/bin/sh" effectively calls system("/bin/sh"). Note: in glibc ≥ 2.34, __free_hook was removed - alternative targets include the _IO_FILE vtable or the tcache struct itself.

Flag

picoCTF{...}

Heap exploitation via tcache poisoning or UAF - leak a libc address from the unsorted bin, calculate offsets, overwrite a hook with system, and trigger it with a /bin/sh pointer.

Want more picoMini by redpwn writeups?

Tools used in this challenge

Related reading

What to try next