2018 ROOT CTF Reversing Writeup

ROOT_Process_1

int sub_EA1860()
{
  int v0; // edx
  int v1; // ecx
  int v2; // edx
  int v3; // ecx
  int v4; // edx
  int v5; // ecx
  int v6; // edx
  int v7; // ecx
  int v8; // edx
  int v9; // ecx
  int v10; // edx
  int v11; // ecx
  int v12; // edx
  int v13; // ecx
  int v14; // edx
  int v15; // ecx
  int v16; // edx
  int v17; // edx
  int v18; // ecx
  int v19; // ST08_4
  DWORD v21; // [esp+250h] [ebp-448h]
  HWND hWnd; // [esp+25Ch] [ebp-43Ch]
  int j; // [esp+268h] [ebp-430h]
  CHAR *v24; // [esp+274h] [ebp-424h]
  unsigned int i; // [esp+280h] [ebp-418h]
  BOOL v26; // [esp+28Ch] [ebp-40Ch]
  PROCESSENTRY32 pe; // [esp+298h] [ebp-400h]
  char Dst[280]; // [esp+3C8h] [ebp-2D0h]
  size_t v29; // [esp+4E0h] [ebp-1B8h]
  int v30; // [esp+4ECh] [ebp-1ACh]
  int v31; // [esp+4F8h] [ebp-1A0h]
  int v32; // [esp+504h] [ebp-194h]
  DWORD dwProcessId; // [esp+510h] [ebp-188h]
  HANDLE hSnapshot; // [esp+528h] [ebp-170h]
  int v35; // [esp+640h] [ebp-58h]
  int v36; // [esp+644h] [ebp-54h]
  int v37; // [esp+648h] [ebp-50h]
  int v38; // [esp+64Ch] [ebp-4Ch]
  int v39; // [esp+650h] [ebp-48h]
  int v40; // [esp+654h] [ebp-44h]
  int v41; // [esp+658h] [ebp-40h]
  int v42; // [esp+65Ch] [ebp-3Ch]
  int v43; // [esp+660h] [ebp-38h]
  int v44; // [esp+664h] [ebp-34h]
  int v45; // [esp+668h] [ebp-30h]
  int v46; // [esp+66Ch] [ebp-2Ch]
  int v47; // [esp+670h] [ebp-28h]
  int v48; // [esp+674h] [ebp-24h]
  int v49; // [esp+678h] [ebp-20h]
  int v50; // [esp+67Ch] [ebp-1Ch]
  int v51; // [esp+680h] [ebp-18h]
  int v52; // [esp+684h] [ebp-14h]
  int v53; // [esp+688h] [ebp-10h]
  int v54; // [esp+68Ch] [ebp-Ch]
  int v55; // [esp+694h] [ebp-4h]
  int savedregs; // [esp+698h] [ebp+0h]

  sub_EA1235(&unk_EAC017);
  system("title Very_easy_Reversing!");
  sub_EA123F(v1, v0);
  v35 = 31;
  v36 = 41;
  v37 = 66;
  v38 = 15;
  v39 = 58;
  v40 = 50;
  v41 = 40;
  v42 = 29;
  v43 = 23;
  v44 = 49;
  v45 = 19;
  v46 = 21;
  v47 = 71;
  v48 = 87;
  v49 = 65;
  v50 = 69;
  v51 = 71;
  v52 = 11;
  v53 = 31;
  v54 = 68;
  hSnapshot = j_CreateToolhelp32Snapshot(2u, 0);
  GetCurrentProcessId();
  dwProcessId = sub_EA123F(v3, v2);
  GetCurrentThread();
  v32 = sub_EA123F(v5, v4);
  OpenProcess(0x2000000u, 1, dwProcessId);
  v31 = sub_EA123F(v7, v6);
  GetCurrentProcessId();
  v30 = sub_EA123F(v9, v8);
  j_memset(Dst, 0, 0x104u);
  if ( hSnapshot )
  {
    pe.dwSize = 296;
    v26 = j_Process32First(hSnapshot, &pe);
    while ( v26 )
    {
      v26 = j_Process32Next(hSnapshot, &pe);
      v29 = j_strlen(pe.szExeFile);
      for ( i = 0; i < 0x14; ++i )
        pe.szExeFile[i] ^= *(&v35 + 4 * i);
      j_memset(Dst, 0, 4u);
      v24 = pe.szExeFile;
      for ( j = 0; j < 20; ++j )
        Dst[j] = v24[j];
      FindWindowA(0, Dst);
      hWnd = sub_EA123F(v11, v10);
      if ( hWnd )
      {
        GetWindowThreadProcessId(hWnd, &v21);
        sub_EA123F(v13, v12);
        if ( v21 == v30 )
        {
          sub_EA104B("Correct\n");
          system("pause");
          sub_EA123F(v15, v14);
          goto LABEL_15;
        }
      }
    }
  }
  system("pause");
  sub_EA123F(v18, v17);
LABEL_15:
  sub_EA1262(&savedregs, &dword_EA1B9C, 0, v16);
  return sub_EA123F(&savedregs ^ v55, v19);
}

중간에 보면 0x14길이 만큼 테이블값과 xor해주는 연산이 있다.

프로세스 이름과 *(&v35 + 4 * i)이 xor해준다.

table=[0x1f,0x29,0x42,0xf,0x3a,0x32,0x28,0x1d,0x17,0x31,0x13,0x15,0x47,0x57,0x41,0x45,0x47,0xb,0x1f,0x44]
process = "Very_easy_Reversing!"
print ''.join(chr(table[i]^ord(process[i])) for i in range(20))

FLAG : IL0veWInnnAp1236.exe


ROOT_Process_2

위에 변수가 9195개 선언되어있는데 생략했다.

너무 커서 헥스레이가 안돌아가는데 hexrays.cfg를 고쳐서 10000넘게 고쳐주면 헥스레이로 볼 수 있다.

  memset(&Dst, 0, 0x44u);
  ProcessHandle = 0;
  ThreadHandle = 0;
  v11 = 0;
  v12 = 0;
  Context.ContextFlags = 65543;
  sub_401020("input : ", savedregs);
  v9199 = &v14;
  sub_401050("%[^\n]s", &v14);
  GetModuleFileNameA(0, &Filename, 0x104u);
  CreateProcessA(&Filename, 0, 0, 0, 0, 4u, 0, 0, &Dst, &ProcessHandle);
  lpAddress = VirtualAlloc(0, 0x240Au, 0x3000u, 4u);
  v9198 = &Src;
  v9197 = lpAddress;
  memcpy(lpAddress, &Src, 0x2400u);
  if ( *lpAddress == 23117 )
  {
    v6 = lpAddress + lpAddress[15];
    NtGetContextThread(ThreadHandle, &Context);
    NtReadVirtualMemory(ProcessHandle, (Context.Ebx + 8), &Buffer, 4u, 0);
    if ( Buffer == *(v6 + 52) )
      NtUnmapViewOfSection(ProcessHandle, Buffer);
    v9199 = 64;
    v9198 = 12288;
    v9197 = *(v6 + 80);
    v9196 = *(v6 + 52);
    BaseAddress = VirtualAllocEx(ProcessHandle, v9196, v9197, 0x3000u, 0x40u);
    if ( BaseAddress )
    {
      v9199 = 0;
      NtWriteVirtualMemory(ProcessHandle, BaseAddress, lpAddress, *(v6 + 84), 0);
      for ( i = 0; i < *(v6 + 6); ++i )
      {
        v2 = (&lpAddress[10 * i + 62] + lpAddress[15]);
        v9199 = 0;
        NtWriteVirtualMemory(ProcessHandle, &BaseAddress[v2[3]], lpAddress + v2[5], v2[4], 0);
      }
      Context.Eax = &BaseAddress[*(v6 + 40)];
      NtWriteVirtualMemory(ProcessHandle, (Context.Ebx + 8), (v6 + 52), 4u, 0);
      NtSetContextThread(ThreadHandle, &Context);
      NtResumeThread(ThreadHandle, 0);
      NtWaitForSingleObject(ProcessHandle, 0, 0);
      NtClose(ThreadHandle);
      NtClose(ProcessHandle);
      VirtualFree(lpAddress, 0, 0x8000u);
      result = 0;
    }
    else
    {
      NtTerminateProcess(ProcessHandle, 1);
      result = -1;
    }
  }
  else
  {
    NtTerminateProcess(ProcessHandle, 1);
    result = 1;
  }
  return result;
}

Codegate Open CTF에서도 나온 Process Hollowing 기법이다.

아래 처럼 동작한다. 나는 동적 디버깅해서 win32_remote.exe가 뜨는건데 그 밑에 ROOT_Process2.exe 보면 된다.

Process를 하나 생성해주니까 대충 생성되어 쓴 곳에서 브레이크 걸고 그때 프로세스 pid 가져와서 메모리 덤프 떠주면 된다.

그러면 그 덤프뜬 파일을 보면 제대로 덤프를 떠졌다.

signed int __cdecl sub_401000(int a1, int a2)
{
  signed int result; // eax
  int v3; // edi
  int v4; // esi
  char v5; // bl
  char v6; // al
  int v7; // esi
  int v8; // eax
  int v9; // edx
  char v10[260]; // [esp+4h] [ebp-11Ch]
  __int128 v11; // [esp+108h] [ebp-18h]
  int v12; // [esp+118h] [ebp-8h]
  char v13; // [esp+11Ch] [ebp-4h]
  const char **v14; // [esp+128h] [ebp+8h]

  srand(1u);
  v12 = 139343166;
  v13 = 123;
  v11 = xmmword_402130;
  if ( a1 == 1 )
  {
    MessageBoxA(0, "Incorrect", "ROOTCTF", 0);
    result = -1;
  }
  else
  {
    v3 = a1 - 1;
    v4 = 0;
    if ( a1 - 1 > 0 )
    {
      v14 = (a2 + 4);
      do
      {
        v5 = rand() % 127;
        v6 = atoi(*v14);
        ++v14;
        v10[v4++] = v5 ^ v6;
      }
      while ( v4 < v3 );
    }
    v7 = 0;
    v8 = 0;
    if ( v3 <= 0 )
      goto LABEL_15;
    do
    {
      v9 = v7 + 1;
      if ( v10[v8] != *(&v11 + v8) )
        v9 = v7;
      ++v8;
      v7 = v9;
    }
    while ( v8 < v3 );
    if ( v9 != 21 )
    {
LABEL_15:
      MessageBoxA(0, "Incorrect", "ROOTCTF", 0);
      result = 0;
    }
    else
    {
      MessageBoxA(0, "Correct", "ROOTCTF", 0);
      result = 0;
    }
  }
  return result;
}

srand(1)이고 rand() % 127 값과 테이블이 xor 연산을 하는 것을 알 수 있다. 쉽게 역연산해서 구할 수 있다.

from ctypes import *
xmmword_402130 = [0x6f,0x78,0x2e,0x13,0x0c,0x35,0x00,0x7a,0x72,0x0f,0x44,0x20,0x62,0x5a,0x54,0x2e,0x3e,0x35,0x4e,0x08,0x7b]

libc = CDLL('msvcrt')
libc.srand(1)
flag = []
for i in range(len(xmmword_402130)):
	flag.append(xmmword_402130[i]^(libc.rand()%127))
print ''.join(chr(flag[i]) for i in range(len(xmmword_402130)))

FLAG : FLAG{R0oT_1nJec@t1On}