deayzl's blog

[DiceCTF 2024] pain-plus-plus 본문

CTF writeup/Others

[DiceCTF 2024] pain-plus-plus

deayzl 2024. 2. 5. 18:28

 

pain-plus-plus

 

The binary consists of two functions; main and main[cold].

 

main:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rax
  char *v4; // rdi
  char *v5; // rdx
  char *v6; // rax
  __int64 v7; // rsi
  __int64 v8; // rcx
  signed __int64 v9; // rbx
  signed __int64 v10; // rax
  size_t v11; // rdx
  char *v12; // rsi
  __int64 v13; // rbx
  int v14; // ebp
  char v15; // cl
  int *v16; // rdi
  __int64 v17; // rdx
  __int64 v18; // rax
  __int64 v19; // rsi
  __int64 v20; // rax
  signed __int64 v21; // rcx
  _QWORD *v23; // [rsp+8h] [rbp-240h]
  char v24; // [rsp+2Fh] [rbp-219h] BYREF
  __int64 v25[4]; // [rsp+30h] [rbp-218h] BYREF
  char *v26; // [rsp+50h] [rbp-1F8h] BYREF
  __int64 length; // [rsp+58h] [rbp-1F0h]
  char v28; // [rsp+60h] [rbp-1E8h] BYREF
  char v29[8]; // [rsp+70h] [rbp-1D8h] BYREF
  char v30[16]; // [rsp+78h] [rbp-1D0h] BYREF
  __int64 v31; // [rsp+88h] [rbp-1C0h]
  char v32[8]; // [rsp+A0h] [rbp-1A8h] BYREF
  int v33; // [rsp+A8h] [rbp-1A0h] BYREF
  __int64 v34; // [rsp+B0h] [rbp-198h]
  int v35[2]; // [rsp+B8h] [rbp-190h]
  int v36[2]; // [rsp+C0h] [rbp-188h]
  __int64 v37; // [rsp+C8h] [rbp-180h]
  int v38[94]; // [rsp+D0h] [rbp-178h] BYREF

  std::__ostream_insert<char,std::char_traits<char>>(&std::cout, "Enter the flag: ", 16LL);
  std::ostream::flush((std::ostream *)&std::cout);
  v28 = 0;
  v26 = &v28;
  length = 0LL;
  std::operator>><char>((__int64)&std::cin, (__int64)&v26);
  v3 = length;
  v4 = v26;
  v5 = &v26[length];
  if ( length >> 2 > 0 )
  {
    while ( 1 )
    {
      if ( *v4 == 10 )
        goto continue;
      if ( v4[1] == 10 )
      {
        ++v4;
        goto continue;
      }
      if ( v4[2] == 10 )
      {
        v4 += 2;
        goto continue;
      }
      if ( v4[3] == 10 )
        break;
      v4 += 4;
      if ( &v26[4 * (length >> 2)] == v4 )
        goto break;
    }
    v4 += 3;
continue:
    if ( v4 != v5 )
    {
      v6 = v4 + 1;
      if ( v5 == v4 + 1 )
      {
        v5 = v4;
        v3 = v4 - v26;
      }
      else
      {
        do
        {
          if ( *v6 != 10 )
            *v4++ = *v6;
          ++v6;
        }
        while ( v5 != v6 );
        v7 = (__int64)v26;
        v8 = length;
        v3 = v4 - v26;
        if ( v5 != &v26[length] )
        {
          v9 = v5 - v4;
          v10 = v5 - v26;
          v11 = length - (v5 - v26);
          if ( length != v10 && v9 )
          {
            v12 = &v26[v10];
            if ( v11 == 1 )
              *v4 = *v12;
            else
              memmove(v4, v12, v11);
            v8 = length;
            v7 = (__int64)v26;
          }
          length = v8 - v9;
          *(_BYTE *)(v7 + v8 - v9) = 0;
LABEL_20:
          qmemcpy(v38, &unk_50A0, 0x108uLL);
          v23 = (_QWORD *)operator new(0x108uLL);
          *v23 = *(_QWORD *)v38;
          v23[32] = *(_QWORD *)&v38[64];
          qmemcpy(
            (void *)((unsigned __int64)(v23 + 1) & 0xFFFFFFFFFFFFFFF8LL),
            (const void *)((char *)v38 - ((char *)v23 - ((unsigned __int64)(v23 + 1) & 0xFFFFFFFFFFFFFFF8LL))),
            8LL * (((unsigned int)v23 - (((_DWORD)v23 + 8) & 0xFFFFFFF8) + 264) >> 3));
          memset(v38, 0, 0x138uLL);
          *(_QWORD *)v38 = 0x620000001DLL;
          *(_QWORD *)&v38[2] = 0x640000001CLL;
          *(_QWORD *)&v38[4] = 0x6D00000010LL;
          *(_QWORD *)&v38[6] = 0x7D0000000ALL;
          *(_QWORD *)&v38[8] = 0x360000000DLL;
          *(_QWORD *)&v38[10] = 0x790000001FLL;
          *(_QWORD *)&v38[12] = 0x7B0000001ELL;
          *(_QWORD *)&v38[14] = 0x6900000007LL;
          *(_QWORD *)&v38[16] = 0x6100000002LL;
          *(_QWORD *)&v38[18] = 0x6600000001LL;
          *(_QWORD *)&v38[20] = 0x3100000016LL;
          *(_QWORD *)&v38[22] = 0x740000001ALL;
          *(_QWORD *)&v38[24] = 0x3400000013LL;
          *(_QWORD *)&v38[26] = 0x7100000025LL;
          *(_QWORD *)&v38[28] = 0x3200000009LL;
          *(_QWORD *)&v38[30] = 0x6800000003LL;
          *(_QWORD *)&v38[32] = 0x6A00000024LL;
          *(_QWORD *)&v38[34] = 0x7500000021LL;
          *(_QWORD *)&v38[36] = 0x3300000008LL;
          *(_QWORD *)&v38[38] = 0x3000000015LL;
          *(_QWORD *)&v38[40] = 0x7A00000020LL;
          *(_QWORD *)&v38[42] = 0x3800000026LL;
          *(_QWORD *)&v38[44] = 0x7600000018LL;
          *(_QWORD *)&v38[46] = 0x650000000CLL;
          *(_QWORD *)&v38[48] = 0x7800000023LL;
          *(_QWORD *)&v38[50] = 0x7200000006LL;
          *(_QWORD *)&v38[52] = 0x350000000ELL;
          *(_QWORD *)&v38[54] = 0x6B00000019LL;
          *(_QWORD *)&v38[56] = 0x5F0000000FLL;
          *(_QWORD *)&v38[58] = 0x7700000014LL;
          *(_QWORD *)&v38[60] = 0x6F00000027LL;
          v38[62] = 17;
          LOBYTE(v38[63]) = 55;
          v38[64] = 4;
          LOBYTE(v38[65]) = 108;
          v38[66] = 5;
          LOBYTE(v38[67]) = 115;
          v38[68] = 18;
          LOBYTE(v38[69]) = 110;
          v38[70] = 27;
          LOBYTE(v38[71]) = 99;
          v38[72] = 23;
          LOBYTE(v38[73]) = 103;
          v38[74] = 11;
          LOBYTE(v38[75]) = 112;
          v38[76] = 34;
          LOBYTE(v38[77]) = 57;
          std::map<int,char>::map(v29, v38, 39LL, v25, v32);
          v13 = v31;
          v33 = 0;
          v34 = 0LL;
          *(_QWORD *)v35 = &v33;
          *(_QWORD *)v36 = &v33;
          v37 = 0LL;
          if ( (char *)v31 != v30 )
          {
            v14 = *(_DWORD *)(v31 + 32);
LABEL_34:
            v19 = (__int64)&v33;
LABEL_31:
            v25[0] = v13 + 36;
            v19 = std::_Rb_tree<char,std::pair<char const,int>,std::_Select1st<std::pair<char const,int>>,std::less<char>,std::allocator<std::pair<char const,int>>>::_M_emplace_hint_unique<std::piecewise_construct_t const&,std::tuple<char const&>,std::tuple<>>(
                    v32,
                    v19,
                    &std::piecewise_construct,
                    v25,
                    &v24);
            while ( 1 )
            {
              *(_DWORD *)(v19 + 36) = v14;
              v20 = std::_Rb_tree_increment(v13);
              v13 = v20;
              if ( (char *)v20 == v30 )
                break;
              v19 = v34;
              v14 = *(_DWORD *)(v20 + 32);
              if ( !v34 )
                goto LABEL_34;
              v15 = *(_BYTE *)(v20 + 36);
              v16 = &v33;
              while ( 1 )
              {
                v17 = *(_QWORD *)(v19 + 16);
                v18 = *(_QWORD *)(v19 + 24);
                if ( v15 > *(char *)(v19 + 32) )
                  break;
LABEL_26:
                if ( !v17 )
                  goto LABEL_29;
                v16 = (int *)v19;
                v19 = v17;
              }
              while ( v18 )
              {
                v19 = v18;
                v17 = *(_QWORD *)(v18 + 16);
                v18 = *(_QWORD *)(v18 + 24);
                if ( v15 <= *(char *)(v19 + 32) )
                  goto LABEL_26;
              }
              v19 = (__int64)v16;
LABEL_29:
              if ( (int *)v19 == &v33 || v15 < *(char *)(v19 + 32) )
                goto LABEL_31;
            }
          }
          main_cold();
        }
        v5 = v4;
      }
    }
LABEL_36:
    length = v3;
    *v5 = 0;
    goto LABEL_20;
  }
break:
  v21 = v5 - v4;
  if ( v5 - v4 != 2 )
  {
    if ( v21 != 3 )
    {
      if ( v21 != 1 )
        goto LABEL_36;
      goto LABEL_40;
    }
    if ( *v4 == 10 )
      goto continue;
    ++v4;
  }
  if ( *v4 == 10 )
    goto continue;
  ++v4;
LABEL_40:
  if ( *v4 != 10 )
    goto LABEL_36;
  goto continue;
}

 

main[cold]:

// positive sp value has been detected, the output may be wrong!
void __fastcall __noreturn main_cold()
{
  int v0; // r15d
  void *exception; // rbp
  _QWORD *v2; // rbx
  int v3; // r8d
  int v4; // r9d
  __int64 v5; // rax
  __int64 v6; // rdx
  int *v7; // rcx
  __int64 v8; // rdx
  int *v9; // rcx
  char *v10; // [rsp-240h] [rbp-240h]
  __int64 v11; // [rsp-230h] [rbp-230h]
  __int64 v12; // [rsp-198h] [rbp-198h]
  __int64 v13; // [rsp-180h] [rbp-180h]
  int v14; // [rsp-170h] [rbp-170h] BYREF
  __int64 v15; // [rsp-168h] [rbp-168h]
  int *v16; // [rsp-160h] [rbp-160h]
  int *v17; // [rsp-158h] [rbp-158h]
  __int64 v18; // [rsp-150h] [rbp-150h]

  exception = __cxa_allocate_exception(0x58uLL);
  v15 = 0LL;
  v14 = 0;
  v16 = &v14;
  v17 = &v14;
  v18 = 0LL;
  if ( v12 )
  {
    v5 = std::_Rb_tree<char,std::pair<char const,int>,std::_Select1st<std::pair<char const,int>>,std::less<char>,std::allocator<std::pair<char const,int>>>::_M_copy<false,std::_Rb_tree<char,std::pair<char const,int>,std::_Select1st<std::pair<char const,int>>,std::less<char>,std::allocator<std::pair<char const,int>>>::_Alloc_node>();
    v6 = v5;
    do
    {
      v7 = (int *)v6;
      v6 = *(_QWORD *)(v6 + 16);
    }
    while ( v6 );
    v16 = v7;
    v8 = v5;
    do
    {
      v9 = (int *)v8;
      v8 = *(_QWORD *)(v8 + 24);
    }
    while ( v8 );
    v17 = v9;
    v15 = v5;
    v18 = v13;
  }
  v2 = (_QWORD *)operator new(0x108uLL);
  *v2 = *(_QWORD *)v10;
  v2[32] = *(_QWORD *)(v11 - 16);
  qmemcpy(
    (void *)((unsigned __int64)(v2 + 1) & 0xFFFFFFFFFFFFFFF8LL),
    (const void *)(v10 - ((char *)v2 - ((unsigned __int64)(v2 + 1) & 0xFFFFFFFFFFFFFFF8LL))),
    8LL * (((unsigned int)v2 - (((_DWORD)v2 + 8) & 0xFFFFFFF8) + 264) >> 3));
  ((void (__fastcall *)(_DWORD, int, _DWORD, int, int))exception_with_both::exception_with_both)(
    (_DWORD)exception,
    v0,
    0,
    v3,
    v4);
  operator delete(v2, 0x108uLL);
  ((void (__fastcall *)(__int64))std::_Rb_tree<char,std::pair<char const,int>,std::_Select1st<std::pair<char const,int>>,std::less<char>,std::allocator<std::pair<char const,int>>>::_M_erase)(v15);
  __cxa_throw(
    exception,
    (struct type_info *)&`typeinfo for'exception_with_both,
    exception_with_both::~exception_with_both);
}

 

 

It uses rbtree and the implementations are following;

1. rbtree initialization

2. compare input character with something

3. If some conditions are satisfied, pass input character to _M_emplace_hint_unique function

4. cmp and je, print out "wrong"

 

I don't know what these codes do specifically but I just did print out operands on following two compares.

(they looked quite important while debugging)

 

1. 5555555563f3 (cmp dl, byte ptr [rax + 0x20])

2. 555555556709 (cmp dl, byte ptr [rsi + 0x20])

 

 

The character "i" of "di" (prefix of flag, "dice{") passed but "A" (which is not part of flag) crashed the process.

 

From this result, I could figure out that the brute-force attack may be possible.

Then I worked on making gdb script.

from random import choice
import string
import gdb

chset = string.ascii_letters + string.digits + string.punctuation.replace('\'', '').replace('}', '') + '}'

ge = gdb.execute

ge('file pain-plus-plus')
ge('b *0x5555555563f3') # cmp    dl, byte ptr [rax + 0x20]
ge('b *0x555555556709') # cmp    dl, byte ptr [rsi + 0x20]
ge('b *0x555555556611') # correct

flag = 'dice{'

bDone = False

while True:
    for ch in chset:
        print('try:', ch)
        input_str = flag + ch + '}'
        
        ge(f'r <<< \'{input_str}\'')
        
        dl = ''

        while True:
            try:
                rip = int(gdb.parse_and_eval('$rip'))
                if rip & 0xfff == 0x3f3:
                    dl = chr(gdb.parse_and_eval('$dl'))
                    rax = ge('x/c $rax + 0x20', to_string=True).split()[-1][1:-1]
                    print('cmp '+dl+' '+rax)
                elif rip & 0xfff == 0x709:
                    dl = chr(gdb.parse_and_eval('$dl'))
                    rsi = ge('x/c $rsi + 0x20', to_string=True).split()[-1][1:-1]
                    print('cmp '+dl+' '+rsi)
                elif rip  & 0xfff == 0x611:
                    bDone = True
                    print('flag:', input_str)
                    break
                ge('c')
            except:
                break
        if bDone:
            break
            
        print('dl:', dl)
        print('present str:',input_str)
        if dl != ch and dl == '}':
            flag += ch
            break
    if bDone:
        break

exit()

# dice{would_you_like_to_try_the_windows_abi_next_time_685dfc4bc1a0}

 

'CTF writeup > Others' 카테고리의 다른 글

[2023 UMDCTF] You Want Me To Run What?? writeup (unintended)  (0) 2023.05.01
[VishwaCTF] 0 | 1 writeup  (0) 2023.04.02
Comments