Jaeseo's Information Security Story

Codeengn Basic RCE 14번 문제 본문

Write UP/Codeengn - basic

Codeengn Basic RCE 14번 문제

Jaeseokim 2019. 12. 1. 00:48

Basic RCE L14

Name이 CodeEngn 일때 Serial을 구하시오

(이 문제는 정답이 여러개 나올 수 있는 문제이며 5개의 숫자로 되어있는 정답을 찾아야함, bruteforce 필요) Ex) 11111

 

일단 문제를 풀기 전에 PE 분석 프로그램을 사용합니다.

upx 패킹이 되어 있는 모습을 볼 수 있는데 upx -d 명령어를 통해 언패킹 후 분석을 합니다!

일단 최근 공부를 하면서 IDA에 대해 알게 되었는데 분석하는데 진짜 도움이 많이 되서 한번 IDA로 정적분석을 해봅니다.

일단 main 함수 입니다.

void __stdcall __noreturn start(int a1)
{
  hInstance = GetModuleHandleA(0); //응용 프로그램의 인스턴스 핸들을 받아서 hInstance에 저장
  sub_401025(hInstance); //hInstance를 인자로 sub_401025() 실행
  ExitProcess(0); //종료
}

일단 sub_401025()함수를 실행 하는 모습을 볼 수 있는데 분석을 합니다.

WPARAM __stdcall sub_401025(HINSTANCE hInstance)
{
  HWND v1; // eax
  HWND v2; // eax
  HWND v3; // eax
  HWND v4; // eax
  HWND hDlg; // [esp+0h] [ebp-50h]
  struct tagMSG Msg; // [esp+4h] [ebp-4Ch]
  WNDCLASSEXA v8; // [esp+20h] [ebp-30h] // 파워빌더? 구조체 선언

  LoadIconA(::hInstance, (LPCSTR)0x64); 
  v8.cbSize = 48;
  v8.style = 3;
  v8.lpfnWndProc = sub_4011E2; //메세지 처리 함수 지정
  v8.cbClsExtra = 0;
  v8.cbWndExtra = 30;
  v8.hInstance = hInstance;
  v8.hbrBackground = (HBRUSH)5;
  v8.lpszMenuName = 0;
  v8.lpszClassName = aBengaly;
  v8.hIcon = LoadIconA(::hInstance, (LPCSTR)0x64);
  v8.hIconSm = v8.hIcon;
  v8.hCursor = LoadCursorA(0, (LPCSTR)0x7F00);
  RegisterClassExA(&v8);
  hDlg = CreateDialogParamA(::hInstance, TemplateName, 0, 0, 0);
  ho = (LPARAM)LoadBitmapA(hInstance, (LPCSTR)0xC8);
  v1 = GetDlgItem(hDlg, 108);
  SendMessageA(v1, 0xF7u, 0, ho);
  ho = (LPARAM)LoadBitmapA(hInstance, (LPCSTR)0x12C);
  v2 = GetDlgItem(hDlg, 112);
  SendMessageA(v2, 0xF7u, 0, ho);
  ho = (LPARAM)LoadBitmapA(hInstance, (LPCSTR)0x190);
  v3 = GetDlgItem(hDlg, 109);
  SendMessageA(v3, 0xF7u, 0, ho);
  ho = (LPARAM)LoadBitmapA(hInstance, (LPCSTR)0x1F4);
  v4 = GetDlgItem(hDlg, 113);
  SendMessageA(v4, 0xF7u, 0, ho);
  SetWindowTextA(hDlg, String);
  UpdateWindow(hDlg);
  while ( GetMessageA(&Msg, 0, 0, 0) )
  {
    if ( !IsDialogMessageA(hDlg, &Msg) )
    {
      TranslateMessage(&Msg);
      DispatchMessageA(&Msg);
      DeleteObject((HGDIOBJ)ho);
      DeleteObject((HGDIOBJ)ho);
    }
  }
  return Msg.wParam;
}

소스를 보면 일단 파워빌더를 통해 모양을 잡고 윈도우를 생성하는 것으로 추측이 됩니다. 이때 lpfnWndProcsub_4011E2 함수를넣는 모습을 볼 수 있는데 이때는 메세지 처리 함수를 지정하는 것으로 아래와 같이 버튼을 누르고 등의 처리를 담당하는 것으로 예상이 됩니다.

이제 처리를 담당하는 sub_4011E2에 들어가 분석을 합니다.

int __stdcall sub_4011E2(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
  int v5; // esi
  int v6; // ecx
  signed int v7; // eax

  if ( Msg == 2 ) //WM_DESTROY := 0x02
  {
    PostQuitMessage(0);
  }
  else if ( Msg == 513 ) //WM_LBUTTONDOWN := 0x201
  {
    ReleaseCapture();
    SendMessageA(hWnd, 0xA1u, 2u, 0);
  }
  //WM_COMMAND := 0x111 == 273 메뉴가 클릭 되었을 때 발생 wParam,lParam를 통해 어떤 메뉴인지에 대해 판단
  else if ( Msg == 273 && !(wParam >> 16) && wParam == 109 )
  //종료에 대한 메뉴
  {
    ExitProcess(0);
  }
  if ( Msg == 273 && !(wParam >> 16) && wParam == 113 )
  //최소화 처리에 대한 메뉴
    ShowWindow(hWnd, 6);
  if ( Msg != 273 )
  //윈도우에서 기본 처리
    return DefWindowProcA(hWnd, Msg, wParam, lParam);
  if ( !(wParam >> 16) && wParam == 112 )
  //ABOUT에 관한 메세지 출력
    MessageBoxA(0, Text, Caption, 0);
  /*
  Text = 
  +=================================+
  |Key/CrackMe - 2 Created on 21/9/2001|
  +=================================+
  ....이하생략....
  Caption = Key/CrackMe #2
  */
  if ( !(wParam >> 16) && wParam == 108 )
  //Check 버튼이 입력 될때 처리
  {
    if ( !GetDlgItemTextA(hWnd, 106, dword_403038, 64) || !GetDlgItemTextA(hWnd, 107, byte_403138, 64) )
      //둘중 하나라도 입력값이 없어 0이 리턴 되면 메세지 박스 출력
      return MessageBoxA(0, aPleaseFillIn1M, Caption, 0);
      /*
      aPleaseFillIn1M = Please Fill in 1 more Char!!
      Caption = Key/CrackMe #2
      */
    v5 = 0;
    v6 = lstrlenA(dword_403038);
    //id의 길이를 저장시킴
    v7 = 1;
    /*
    dword_403038 == id 변수
    byte_403138 == password 변수
    아래의 설명 이미지 참조!
    */
    do
    {
      v5 = ((signed int)(unsigned __int8)byte_403037[v7] >> 1)
         //id의 v7의 인덱스에 들어가 >> 1 연산 즉 1/2
         + (unsigned __int8)byte_403037[v7] * (unsigned __int8)byte_403037[v7]
         //위의 결과에 + id의 v7의 인덱스의 값을 제곱
         + v5
         //기존의 v5 추가
         - (unsigned __int8)byte_403037[v7];
        // - id의 v7의 인덱스의 값을 뺀다.
      ++v7;
      //인덱스를 위한 변수 1증가
      --v6;
      //반복 변수 1--
    }
    while ( v6 );
    //아이디의 길이를 반복할때 마다 v6-- 반복 
    if ( sub_401383(byte_403138) == v5 )
      //만약 password가 v5와 같으면 성공 메세지 출력
      MessageBoxA(0, aGoodJobIWishYo, Caption, 0);
    else
      //아니라면 실패 메세지 출력
      MessageBoxA(0, aYouHaveEnterAW, Caption, 0);
  }
  return 0;
}

참조 이미지

위와 같이 중간에 x64dbg로 동적으로 어디에 사용하는지에 대해 분석도 하면서 IDA를 주로 사용 하며 파악을 하였는데 이 프로그램은 ID를 기준으로 특수한 알고리즘 연산을 통해 Password를 뽑아내는 것으로 보입니다. 이점을 이용하여 CodeEngn일때의 Password를 알아내봅니다.

직접 X64dbg를 통해서 ID가 CodeEngn일때의 알고리즘 후 비교하는 레지스터값을 분석 하는 방법도 있지만 한번 IDA에서 가져온 소스를 조금 수정 하여 출력 해봤습니다.

#include <stdio.h>

int main(int argc, char const *argv[])
{    
    int v5 = 0;
    char *id = "CodeEngn";
    int v6 = 8;
    //id의 길이를 저장시킴
    signed int v7 = 0;
    /*
    dword_403038 == id 변수
    byte_403138 == password 변수
    아래의 설명 이미지 참조!
    */
    do
    {
        v5 = ((signed int)(unsigned __int8)id[v7] >> 1)
            //id의 v7의 인덱스에 들어가 >> 1 연산 즉 1/2
            + (unsigned __int8)id[v7] * (unsigned __int8)id[v7]
            //위의 결과에 + id의 v7의 인덱스의 값을 제곱
            + v5
            //기존의 v5 추가
            - (unsigned __int8)id[v7];
        // - id의 v7의 인덱스의 값을 뺀다.
        ++v7;
        //인덱스를 위한 변수 1증가
        --v6;
        //반복 변수 1--
    }
    while ( v6 );
    printf("%d",v5);
    return 0;
}

그 결과 아래의 결과가 나오는 것을 확인할 수 있는데 직접 테스트를 해봅니다.

[Running] cd "d:\OneDrive - JaeSeo\Codeengn\" && gcc 14.c -o 14 && "d:\OneDrive - JaeSeo\Codeengn\"14
76193
[Done] exited with code=0 in 0.335 seconds

 

AUTH:76193

 

13번까지 예전에 풀어두고 이제서야 블로그에 올린 뒤 14번을 풀어서 올리네요...

'Write UP > Codeengn - basic' 카테고리의 다른 글

Codeengn Basic RCE 16번 문제  (0) 2019.12.03
Codeengn Basic RCE 15번 문제  (0) 2019.12.01
Codeengn Basic RCE 13번 문제  (0) 2019.11.30
Codeengn Basic RCE 12번 문제  (0) 2019.11.30
Codeengn Basic RCE 11번 문제  (0) 2019.11.30
Comments