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

[CodeEngine] Basic RCE L20

728x90

1. 문제


이 프로그램은 Key파일을 필요로 하는 프로그램이다.
'Cracked by: CodeEngn!' 문구가 출력 되도록 하려면 crackme3.key 파일안의 데이터는 무엇이 되어야 하는가
Ex) 41424344454647
(정답이 여러개 있는 문제로 인증시 맞지 않다고 나올 경우 Contact로 연락주시면 확인 해드리겠습니다)

그냥 실해하면 아무것도 뜨지 않는다

2. 접근방법


HWND start()
{
  HANDLE v0; // eax
  HWND result; // eax
  bool v2; // [esp-4h] [ebp-4h]

  hInstance = GetModuleHandleA(0);
  dword_4020F9 = 0;
  v0 = CreateFileA("CRACKME3.KEY", 0xC0000000, 3u, 0, 3u, 0x80u, 0);
  if ( v0 != (HANDLE)-1
    && (hFile = v0, ReadFile(v0, buf, 0x12u, &NumberOfBytesRead, 0), NumberOfBytesRead == 18)
    && (sub_401311(buf), dword_4020F9 ^= 0x12345678u, v2 = sub_40133C(buf) == dword_4020F9) )
  {
    sub_401346((int)WindowName); // crack ! 열로 와야함
  }
  else
  {
    sub_4012F5((int)WindowName);
  }
  result = FindWindowA(ClassName, 0);
		....
    if ( v2 )
      sub_401362(buf, (int)Text, aNowTryTheNextC);
    while ( GetMessageA(&Msg, 0, 0, 0) )
    {
      TranslateMessage(&Msg);
      DispatchMessageA(&Msg);
    }
    ExitProcess(Msg.wParam);
  }
  return result;
}
  • CRACKME3.KEY 이름의 파일의 핸들을 가져온다. 따라서 키 파일을 하나 생성해야 한다
  • 키 내용은 18바이트여야 한다
  • 키를 읽은 다음 특정 연산과 조건을 거친다
    1. sub_401311() - 키 파일 내용 한바이트씩 xor 연산
    1. dword_4020f9 ^= 0x12345678
    1. sub_40133c(buf) == dword_4020f9

따라서 위 3가지의 조건을 다 만족시켜야 한다. 첫번째 함수부터 살펴보자

char __cdecl sub_401311(_BYTE *a1)
{
  int v1; // ecx
  int v2; // eax
  char v4; // bl

  v1 = 0;
  v2 = 0;
  v4 = 0x41;
  do
  {
    LOBYTE(v2) = v4 ^ *a1;
    *a1++ = v2;
    ++v4;
    dword_4020F9 += v2;
    if ( !(_BYTE)v2 )
      break;
    LOBYTE(v1) = v1 + 1;
  }
  while ( v4 != 0x4F );
  dword_402149 = v1;
  return v2;
}

위 함수는 결국 키 파일을 한바이트식 0x41부터 xor 연산을 진행하고, 결과값이 0이 아닐때까지 반복한다. 만약 xor 키가 0x4f가 되면 종료된다. 연산 결과는 기존의 키값으로 덮히고, dword_4020f9에도 복사된다

dword_4020f9 ^= 0x12345678

그다음 dword_4020f9와 0x12345678과 xor연산을 진행한다. sub_401311() 함수가 호출이 끝나면 저 곳에는 xor 연산된 특정 값이 들어가 있을 것이다. 그값과 0x12345678과 다시 xor 을 진행한다

int __cdecl sub_40133C(char *a1)
{
  return *(_DWORD *)(a1 + 14);
}

================================================
sub_40133c(buf) == dword_4020f9

buf의 값 즉, xor 연산되었던 값 + 14 offset과 dword_4020f9와 비교한다. 이 두 값이 일치해야 한다

3. 풀이


메시지 박스에 어떤 값이 출력되는지 임의로 키 값을 넣고 eip를 강제로 crack ! 쪽으로 가게끔 해보자. 우선 123456789123456789 이렇게 18바이트를 입력했고 sub_401311() 함수의 호출 결과는 다음과 같다

입력한 18바이트의 14바이트만 인코딩이 됬고 하위 4바이트는 그대로 들어가있다

0x4020f9는 0x1234502f 가 되었다. 0x1234502f 이 값과 0x12345678과 xor은 진행하고, 그 결과를 다시 0x4020f9에 저장한다

마지막으로 그 결과와 키 파일의 하위 4바이트랑 비교한다. 이 두 개가 동일해야 한다

임의로 동일하게 맞춰주고 진행시키면

다음과 같이 msg가 뜬다. 내용물은 키 파일에서 인코딩된 문자열이다. 즉 저기에 CodeEngn이 나오게 역연산을 진행해야 한다. 역연산 코드는 다음과 같다

input_ = ""
result = "CodeEngnIJKLMNOPQR"
key = 0x41
compare = 0
tmp = ""

for i in range(8):
    input_ += chr(ord(result[i]) ^ key)
    print(hex(ord(result[i]) ^ key))
    compare += key ^ ord(result[i])
    # print(hex(compare))
    key = key + 1

-------------------------------------------------------------------
> 
0x2 
0x2d
0x27
0x21
0x0
0x28
0x20
0x26

따라서 저 8바이트를 키 파일에 넣으면 된다. 키 파일은 18바이트를 맞춰야한다. 현재 xor 키가 0x41부터 시작하므로 9바이트에 0x41+8 = 0x49 를 넣으면 0x49 ^ 0x49 가 되어 0이 계산되고 인코딩 루틴이 종료가 된다.

  • 8바이트 역연산 값 + 1바이트 0x49 + 5바이트 더미 + 4바이트??

이제 키파일에서 마지막 4바이트만 맞춰주면 된다. 이는 앞에 값들만 맞춰주고, 디버깅으로 0x12345678과 xor 연산된 값을 그대로 넣으면 된다

  • 0 ~ 0x7 ⇒ 역연산 시킨 값. xor 연산 후 CodeEngn으로 바뀐다
  • 0x8 ~ 0x9 ⇒ 인코딩 시 0x49 ^ 0x49 에 의해서 0이 된다
  • 0xA ~ 0xD ⇒ 더미 값
  • 0xE ~ 0x11 ⇒ 앞 8바이트 xor 연산 + 그 결과값과 0x12345678 xor 연산 결과

4. 몰랐던 개념


  • none
728x90

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

[CodeEngine] Malware Analysis L02  (0) 2021.01.20
[CodeEngine] Malware Analysis L1  (0) 2021.01.19
[CodeEngine] Basic RCE L19  (0) 2021.01.13
[CodeEngine] Basic RCE L18  (0) 2021.01.12
[CodeEngine] Basic RCE L17  (0) 2021.01.11