tcache_dup2

oogu ㅣ 2023. 2. 1. 15:00

#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;
		}
	}
}

[그림 1-1]

이전 문제와 보호 기법이 같다. 따라서 Got Overwrite 할 수 있고, 코드, 데이터 섹션이 고정되어 있다.

get_shell 함수도 존재해 특정 함수 got 값을 get_shell 주소로 덮어보자

삽질하기 전에 리모트 환경에 보호 기법이 존재하는지 확인해 보자

[그림 1-2]

0번 index에 힙 영역을 할당받고 두 번 해제하는 코드이다.

[그림 1-3]

프로그램이 종료되었다. 원래 double free 어쩌고 문구가 떠야 하지만 일부러 안 보이게 한 것 같다. 째든 tcache에 보호 기법이 적용되었다. 이를 우회해야지 tcache duplication이 가능하다.

 

코드를 보면 modify라는 함수에서 index와 size를 입력받고 size만큼 해당 index에 data를 넣을 수 있다.

이를 통해 free 된 청크 key 값을 바꾼 다음 free를 하면 보호 기법을 우회할 수 있다.

[그림 1-4]

e -> key 위치는 fd 다음에 존재한다. 따라서 "A"*8 개로 fd 값을 덮고 "A"를 통해 e -> key 값 1byte를 "A"로 바뀌기 때문에 검증을 우회하여 free를 할 수 있게 된다. -> tcache duplication 발생!

 

[그림 1-5]

프로그램이 종료되지 않는 걸 보면 보호 기법이 우회되었고 tcache duplication이 발생한 걸 알 수 있다.

이제 got를 get_shell 주소로 덮어보자

[그림 1-6]

scanf got 값을 get_shell 주소로 덮었다.

[그림 1-7]

로컬에서는 익스가 성공했다. 리모트 ㄱㄱ

[그림 1-8]

[그림 1-8]은 리모트에서 시도한 것이다. 익스가 안된다....흠... 찾아보니까 일단 리모트 환경(20.04)이랑 로컬 환경(18.04) 우분투 버전이 다르다. 따라서 tc_idx라는 걸 신경 써야 된다고 한다. tc_idx는 bin에 들어간 청크의 개수 인다. 이 값이 0이면 tcache_bin에 청크가 없다는 뜻으로 tcache_bin을 참조하지 않는다고 한다.

 

직접 확인해보자

[그림 1-9]

[그림 1-9]는 0번 index를 해제한 상태이다.

tcache_bin에 0번 index 청크가 들어가 있고 아래 숫자 1이 들어가 있다. 아래 숫자 1이 tcache_bin의 개수이다.

[그림 2-1]

[그림 2-1]은 tcache duplication이 발생한 상태이다. tc_idx 개수는 2개이다.

하지만 got 값을 덮기 위해 총 세 번의 malloc을 시도하는데 tc_idx 개수는 2개이므로 마지막 create(0x40,p64(get_shell)) 코드는 우리가 원하는 데도 실행이 안되어 shell이 실행되지 않는다.

따라서 tc_idx를 3개로 맞춰줘야 한다. tc_idx 개수는 해제된 청크의 개수이므로 3번의 create를 하기 전 보호 기법을 우회해서  tc_idx 3으로 맞추자

[그림 2-2]

c-d m-d m-d을 통해 tc_idx를 3개로 맞추었다.

[그림 2-3]

성공!

 

알게 된 점

1. 라이브러리 버전이 다르다 -> 라이브러리 함수 동작이 바뀐다.

따라서 18.04에 없던 tc_idx 검증이 20.4에는 추가되어있다

2. 동일한 버전이라도 상세 버전에 따라 또 달라질 수 있다.

 

https://dreamhack.io/forum/qna/1305

 

modify_heap??

Double free를 쓰지 않고 청크를 1번만 Free한 다음에 Modify_heap으로 next 포인터를 got 주소로 변경후 익스플로잇을 시도해봤습니다. 그런데 우분투 18.…

dreamhack.io

 

https://dreamhack.io/forum/qna/3086

 

원격에서 tcache dfb 검증 없는 이유?

로컬 환경과 원격 모두 glibc 2.27 버전인데 로컬에선 막히고 원격에선 되네요.. glibc 버전이 같아도 환경에 따라 dfb가 막히기도 하나요? 원격 환경이 dfb 검증을…

dreamhack.io

 

'Dreamhack - pwnable' 카테고리의 다른 글

cmd_center  (0) 2023.02.03
sint  (0) 2023.02.03
tcache_dup  (0) 2023.02.01
tcache_poison  (0) 2023.01.30
uaf_overwrite  (0) 2023.01.28