블로그 이전했습니다. https://jeongzero.oopy.io/
[CodeEngine] Basic RCE L17
본문 바로가기
워게임/CodeEngn

[CodeEngine] Basic RCE L17

728x90

1. 문제


Key 값이 BEDA-2F56-BC4F4368-8A71-870B 일때 Name은 무엇인가
힌트 : Name은 한자리인데.. 알파벳일수도 있고 숫자일수도 있고..
정답인증은 Name의 MD5 해쉬값(대문자)

이번엔 반대로 키에 해당하는 Name을 구해야한다

패킹은 따로 안돼있고 델파이로 만들어진 바이너리이다

int __usercall TForm1_Button1Click@<eax>(int a1@<eax>, int a2@<ebx>)
{
  ...

  Controls::TControl::GetText(*(Controls::TControl **)(a1 + 872));
  v15 = v16;                                    // v16=&Name
  v3 = v16;
  if ( v16 )                                    // Name 값이 참이면
    v3 = *(_DWORD *)(v16 - 4);
  if ( v3 >= 3 )                                // Name 사이즈가 3보다 크거나 같으면
  {
    Controls::TControl::GetText(*(Controls::TControl **)(a1 + 872));
    v15 = v14;
    v4 = v14;
    if ( v14 )
      v4 = *(_DWORD *)(v14 - 4);
    if ( v4 <= 0x1E )                           // len(Name) <= 0x1E
    {
      Controls::TControl::GetText(*(Controls::TControl **)(a1 + 0x374));
      v7 = v13;                                 // v13=serial
      Controls::TControl::GetText(*(Controls::TControl **)(a1 + 0x368));
      sub_45B850(v11, (int)&v12);               // v11=Name
      System::__linkproc__ LStrCmp(v7, v12);
      if ( v5 )
        Forms::TApplication::MessageBox(*(Forms::TApplication **)off_45E9C0[0], "Well done!", "Good Boy!!!", 0x40u);
      else
        Sleep_0(0x14Du);
....
}

Name에 입력한 사이즈를 검사한다. 문제에서는 한글자라고 했지만 여기서는 3바이트보다 크거나 같아야지만 된다. 저기는 1로 패치해서 디버깅하면 될듯

그리고 0x1E보다는 작아야한다. 힌트에서 한글자라고 했으니 여기는 걍 통과될 것이다.

키젠을 어디서 하는지를 찾는게 관건이다

2. 접근방법


디버깅 결과

sub_45B850(v11, (int)&v12); // v11=Name

여기서 키젠을 하는것을 확인할 수 있다. v11에는 입력한 Name이 들어가고 v12에는 생서된 키 값이 들어간다

// bad sp value at call has been detected, the output may be wrong!
int __fastcall sub_45B850(void *a1, int a2)
{
  ...

  v34 = a2;
  v36 = a1;
  System::__linkproc__ LStrAddRef(a1);
  v22 = &savedregs;
  v21 = &loc_45BAAA;
  v20 = NtCurrentTeb()->NtTib.ExceptionList;
  __writefsdword(0, (unsigned int)&v20);
  v2 = 0;
  v3 = 0;
  v33 = 0;
  v4 = (char *)v36;
  if ( v36 )
    v4 = (char *)*((_DWORD *)v36 - 1);
  if ( (int)v4 > 0 )
  {
    v5 = 1;
    do
    {
      v6 = 0x772 * (v2 + (unsigned __int8)*((char *)v36 + v5 - 1));
      v3 = 0x8E8 * (v6 * v6 + v6);
      v2 = v3;
      ++v5;
      --v4;
    }
    while ( v4 );
  }
  v7 = (int)v36;
  if ( v36 )
    v7 = *((_DWORD *)v36 - 1);
  if ( v7 >= 1 )
  {
    do
      v33 += 0x93C84 * ((unsigned __int8)*((char *)v36 + --v7) + 12);
    while ( v7 );
  }
  System::__linkproc__ LStrCat3((int)&v31, v36, &str_w09__720_______[1]);
  sub_45B54C(v31, (int)&v35);
  v8 = 0;
  v9 = 0;
  v10 = (int)v36;
  if ( v36 )
    v10 = *((_DWORD *)v36 - 1);
  if ( v10 >= 1 )
  {
    do
    {
      v8 = 33682 * (v9 + (unsigned __int8)*((char *)v36 + v10 - 1) + v8 + 4240);
      v9 = v8 + v8 * (v8 - 51);
      --v10;
    }
    while ( v10 );
  }
  v11 = 0;
  v12 = (char *)v36;
  if ( v36 )
    v12 = (char *)*((_DWORD *)v36 - 1);
  if ( (int)v12 > 0 )
  {
    v32 = 1;
    do
    {
      v13 = 2 * ((unsigned __int8)*((char *)v36 + v32 - 1) + v11);
      v14 = (unsigned __int8)*((char *)v36 + v32 - 1) + 883 * ((v13 * v13 * v13) ^ 0x10 | 0x44) + 1091;
      v11 = v14 * v14;
      ++v32;
      --v12;
    }
    while ( v12 );
  }
  Sysutils::IntToHex(v3, 4);
  System::__linkproc__ LStrCopy((int)&v30, (int)v20);
  v20 = (_EXCEPTION_REGISTRATION_RECORD *)v30;
  Sysutils::IntToHex(v33, 0);
  System::__linkproc__ LStrCopy((int)&v29, (int)&str___13[1]);
  v19 = v29;
  System::__linkproc__ LStrCopy((int)&v28, (int)&str___13[1]);
  v18 = v28;
  Sysutils::IntToHex(v8, 0);
  System::__linkproc__ LStrCopy((int)&v27, (int)&str___13[1]);
  v17 = v27;
  Sysutils::IntToHex(v11, 0);
  System::__linkproc__ LStrCopy((int)&v26, (int)&str___13[1]);
  System::__linkproc__ LStrCatN(v34, 9, v15, v23, v22, v21, v20, v19, v18, v17, v26);
  __writefsdword(0, v24);
  v26 = (int)&loc_45BAB1;
  System::__linkproc__ LStrArrayClr(&v25, 10);
  return System::__linkproc__ LStrArrayClr(&v35, 2);
}

맨 마지막 StrCat 을 통해 생성한 키 값을 이어붙인다. 중간중간 하이픈으로 연결한다.

키 사이즈는 Name에 상관없이 고정 사이즈이다

ex) 0000-0000-00000000-0000-0000

초반 부분을 보면

v3=0
v5=0 
if ( (int)v4 > 0 )
  {
    v5 = 1;
    do
    {
      v6 = 0x772 * (v3 + (unsigned __int8)*((char *)v36 + v5 - 1));
      v3 = 0x474 * (v6 * v6 + v6);
      v3 = v3+v3;
      ++v5;
      --v4;
    }
    while ( v4 );
  }

다음과 같이 계산되는데 Name의 한바이트를 가지고 연산을 진행한다. 연산결과는 4바이트 이상의 값이 나오지만 4바이트까지만 저장된다. 생성되는 키 값을 확인해보면 위 연산 결과의 상위 2바이트 인것을 알 수 있다

따라서 위 로직을 파이썬 코드로 포팅하여 BEDA-2F56-BC4F4368-8A71-870B 키 값중 첫 4바이트가 나오는 것을 Name으로 하면 된다

3. 풀이


import hashlib

v6 = 0
v2 = 0
for i in range(0x21, 0x7F):
    v6 = (v2 + i) * 0x772
    tmp = (v6 * v6) & 0xFFFFFFFF
    tmp = ((tmp + v6) * 0x474) & 0xFFFFFFFF
    tmp = (tmp + tmp) & 0xFFFFFFFF
    if (tmp >> (8 * 2)) == 0xBEDA:
        print("Name is " + chr(i))
        enc = hashlib.md5()
        enc.update(chr(i).encode("utf-8"))
        encText = enc.hexdigest()
        print("md5 hash is " + str(encText))
        break

============================================================
Name is F
md5 hash is 800618943025315f869e4e1f09471012

4. 몰랐던 개념


728x90

'워게임 > CodeEngn' 카테고리의 다른 글

[CodeEngine] Basic RCE L19  (0) 2021.01.13
[CodeEngine] Basic RCE L18  (0) 2021.01.12
[CodeEngine] Basic RCE L16  (0) 2021.01.04
[CodeEngine] Basic RCE L14  (0) 2021.01.02
[CodeEngine] Basic RCE L15  (0) 2021.01.02