1nzag

[reversing.kr]Position Writeup

wargame/reversing.kr

문제의 Readme.txt 를 읽어보면 다음과 같다.

ReversingKr KeygenMe



Find the Name when the Serial is 76876-77776

This problem has several answers.


Password is ***p



바이너리는 name과 serial 두가지 값을 받는데, serial 값이 위와 같을 때의 name 값을 알아내는 것이 목표이다.


처음에 바이너리를 보면 코드가 은근히 많은데, 바이너리에 나와있는 문자열 검색을 통해 핵심 루틴이 어디있는지 파악했다.

('Wrong' 이라는 문자열을 검색해보았다.)



다음과 같이 함수의 결과값에 따라 나오는 문자열이 다른것을 보아, sub_401740 부분이 시리얼과 이름값을 비교하는 것을 알 수 있다.

위의 함수 내용은 다음과 같다.

signed int __stdcall sub_401740(int a1)

{

  int index; // edi

  int alpha_loc; // esi

  int sub_index; // esi

  __int16 sub_char; // bx

  unsigned __int8 name_char_0; // al

  unsigned __int8 num_0_0; // ST2C_1

  unsigned __int8 name_char_1; // al

  unsigned __int8 num_0_1; // bl

  wchar_t *CS_buffer; // eax

  __int16 buffer_char_0; // di

  wchar_t *v12; // eax

  __int16 serial_char_1; // di

  wchar_t *v14; // eax

  __int16 v15; // di

  wchar_t *v16; // eax

  __int16 v17; // di

  wchar_t *v18; // eax

  __int16 v19; // di

  unsigned __int8 name_string_2; // al

  unsigned __int8 num_6_0; // ST2C_1

  unsigned __int8 name_string_3; // al

  unsigned __int8 num_6_1; // bl

  wchar_t *v24; // eax

  __int16 v25; // di

  wchar_t *v26; // eax

  __int16 v27; // di

  wchar_t *v28; // eax

  __int16 v29; // di

  wchar_t *v30; // eax

  __int16 v31; // di

  wchar_t *v32; // eax

  __int16 v33; // si

  unsigned __int8 num_3_1; // [esp+10h] [ebp-28h]

  unsigned __int8 num_9_1; // [esp+10h] [ebp-28h]

  unsigned __int8 num_4_1; // [esp+11h] [ebp-27h]

  unsigned __int8 num_10_1; // [esp+11h] [ebp-27h]

  unsigned __int8 num_1_1; // [esp+13h] [ebp-25h]

  unsigned __int8 num_7_1; // [esp+13h] [ebp-25h]

  unsigned __int8 num_2_1; // [esp+14h] [ebp-24h]

  unsigned __int8 num_8_1; // [esp+14h] [ebp-24h]

  unsigned __int8 num_2_0; // [esp+19h] [ebp-1Fh]

  unsigned __int8 num_8_0; // [esp+19h] [ebp-1Fh]

  unsigned __int8 num_3_0; // [esp+1Ah] [ebp-1Eh]

  unsigned __int8 num_9_0; // [esp+1Ah] [ebp-1Eh]

  unsigned __int8 num_1_0; // [esp+1Bh] [ebp-1Dh]

  unsigned __int8 num_7_0; // [esp+1Bh] [ebp-1Dh]

  unsigned __int8 num_4_0; // [esp+1Ch] [ebp-1Ch]

  unsigned __int8 num_10_0; // [esp+1Ch] [ebp-1Ch]

  int name_string; // [esp+20h] [ebp-18h]

  int serial_string; // [esp+24h] [ebp-14h]

  char buffer; // [esp+28h] [ebp-10h]

  int v53; // [esp+34h] [ebp-4h]


  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&name_string);

  index = 0;

  v53 = 0;

  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&serial_string);

  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&buffer);

  LOBYTE(v53) = 2;

  CWnd::GetWindowTextW(a1 + 304, &name_string);

  if ( *(_DWORD *)(name_string - 12) == 4 )

  {

    alpha_loc = 0;

    while ( (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&name_string, alpha_loc) >= 'a'

         && (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&name_string, alpha_loc) <= 'z' )

    {

      if ( ++alpha_loc >= 4 )

      {

LABEL_7:

        sub_index = 0;

        while ( 1 )

        {

          if ( index != sub_index )

          {

            sub_char = ATL::CSimpleStringT<wchar_t,1>::GetAt(&name_string, sub_index);

            if ( (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&name_string, index) == sub_char )

              goto LABEL_2;

          }

          if ( ++sub_index >= 4 )

          {

            if ( ++index < 4 )

              goto LABEL_7;

            CWnd::GetWindowTextW(a1 + 420, &serial_string);

            if ( *(_DWORD *)(serial_string - 12) == 11// serial string's length is 11

              && (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 5) == '-' )

            {

              name_char_0 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&name_string, 0);

              num_0_0 = (name_char_0 & 1) + 5;

              num_4_0 = ((name_char_0 >> 4) & 1) + 5;

              num_2_0 = ((name_char_0 >> 1) & 1) + 5;

              num_3_0 = ((name_char_0 >> 2) & 1) + 5;

              num_1_0 = ((name_char_0 >> 3) & 1) + 5;

              name_char_1 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&name_string, 1);

              num_3_1 = (name_char_1 & 1) + 1;

              num_2_1 = ((name_char_1 >> 4) & 1) + 1;

              num_4_1 = ((name_char_1 >> 1) & 1) + 1;

              num_0_1 = ((name_char_1 >> 2) & 1) + 1;

              num_1_1 = ((name_char_1 >> 3) & 1) + 1;

              CS_buffer = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

              itow_s(num_0_0 + num_0_1, CS_buffer, 0xAu, 10);// get integer 2

              buffer_char_0 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0);

              if ( (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 0) == buffer_char_0 )

              {

                ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                v12 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                itow_s(num_1_0 + num_1_1, v12, 0xAu, 10);

                serial_char_1 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 1);

                if ( serial_char_1 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                {

                  ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                  v14 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                  itow_s(num_2_0 + num_2_1, v14, 0xAu, 10);

                  v15 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 2);

                  if ( v15 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                  {

                    ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                    v16 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                    itow_s(num_3_0 + num_3_1, v16, 0xAu, 10);

                    v17 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 3);

                    if ( v17 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                    {

                      ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                      v18 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                      itow_s(num_4_0 + num_4_1, v18, 0xAu, 10);

                      v19 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 4);

                      if ( v19 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                      {

                        ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                        name_string_2 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&name_string, 2);

                        num_6_0 = (name_string_2 & 1) + 5;

                        num_10_0 = ((name_string_2 >> 4) & 1) + 5;

                        num_8_0 = ((name_string_2 >> 1) & 1) + 5;

                        num_9_0 = ((name_string_2 >> 2) & 1) + 5;

                        num_7_0 = ((name_string_2 >> 3) & 1) + 5;

                        name_string_3 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&name_string, 3);

                        num_9_1 = (name_string_3 & 1) + 1;

                        num_8_1 = ((name_string_3 >> 4) & 1) + 1;

                        num_10_1 = ((name_string_3 >> 1) & 1) + 1;

                        num_6_1 = ((name_string_3 >> 2) & 1) + 1;

                        num_7_1 = ((name_string_3 >> 3) & 1) + 1;

                        v24 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                        itow_s(num_6_0 + num_6_1, v24, 0xAu, 10);

                        v25 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 6);

                        if ( v25 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                        {

                          ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                          v26 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                          itow_s(num_7_0 + num_7_1, v26, 0xAu, 10);

                          v27 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 7);

                          if ( v27 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                          {

                            ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                            v28 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                            itow_s(num_8_0 + num_8_1, v28, 0xAu, 10);

                            v29 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 8);

                            if ( v29 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                            {

                              ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                              v30 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                              itow_s(num_9_0 + num_9_1, v30, 0xAu, 10);

                              v31 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 9);

                              if ( v31 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                              {

                                ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                                v32 = (wchar_t *)ATL::CSimpleStringT<wchar_t,1>::GetBuffer(&buffer);

                                itow_s(num_10_0 + num_10_1, v32, 0xAu, 10);

                                v33 = ATL::CSimpleStringT<wchar_t,1>::GetAt(&serial_string, 10);

                                if ( v33 == (unsigned __int16)ATL::CSimpleStringT<wchar_t,1>::GetAt(&buffer, 0) )

                                {

                                  ATL::CSimpleStringT<wchar_t,1>::ReleaseBuffer(&buffer, -1);

                                  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&buffer);

                                  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&serial_string);

                                  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&name_string);

                                  return 1;

                                }

                              }

                            }

                          }

                        }

                      }

                    }

                  }

                }

              }

            }

            goto LABEL_2;

          }

        }

      }

    }

  }

LABEL_2:

  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&buffer);

  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&serial_string);

  ATL::CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>::~CStringT<wchar_t,StrTraitMFC_DLL<wchar_t,ATL::ChTraitsCRT<wchar_t>>>(&name_string);

  return 0;

위에서 처음 보는 함수나 자료형이 많았는데 주로 많이 나오는 CSimpleStringT 객체의 두 가지 함수는 GetAt 함수와 GetBuffer 함수는 다음과 같은 함수였다.


GetAt: https://msdn.microsoft.com/ko-kr/library/sddk80xf.aspx

GetBuffer: https://msdn.microsoft.com/ko-kr/library/sddk80xf.aspx


즉, GetAt 함수는 CSimpleStringT 객체의 문자열에서 특정 인덱스의 문자를 가져오는 함수이고 (일반 문자열에서의 [] 연산과 같다.)

GetBuffer 함수는 CSimple StringT 객체의 문자열과 일반 문자열 배열을 동기화(??) 시켜주는 함수였다.


이에 따라 위의 루틴을 분석해보면 각 name 캐릭터의 문자들을 비트연산해서 각각의 값이 시리얼과 일치하는지 확인하는 루틴이다.

근데 이 연산이 무조건 일대일 대응도 아니고 일일이 계산하는 것이 매우 머리아픈 짓이므로 z3 Solver 를 사용하기로 했다.


from z3 import *


#76876-77776

a0 = BitVec('a0', 8)

a1 = BitVec('a1', 8)

a2 = BitVec('a2', 8)

a3 = BitVec('a3', 8)

s = Solver()


s.add(a0 >= 0x60)

s.add(a1 >= 0x60)

s.add(((a0 & 1) + 5) + (((a1 >> 2) & 1) + 1) == 7)

s.add((((a0 >> 3) & 1) + 5) + (((a1 >> 3) & 1) + 1) == 6)

s.add((((a0 >> 1) & 1) + 5) + (((a1 >> 4) & 1) + 1) == 8)

s.add((((a0 >> 2) & 1) + 5) + ((a1 & 1) + 1) == 7)

s.add((((a0 >> 4) & 1) + 5) + (((a1 >> 1) & 1) + 1) == 6)

#other


s.add(a3 == 112) #letter 'p'

s.add(a2 >= 0x60)

s.add(((a2 & 1) + 5) + (((a3 >> 2) & 1) + 1) == 7)

s.add((((a2 >> 3) & 1) + 5) + (((a3 >> 3) & 1) + 1) == 7)

s.add((((a2 >> 1) & 1) + 5) + (((a3 >> 4) & 1) + 1) == 7)

s.add((((a2 >> 2) & 1) + 5) + ((a3 & 1) + 1) == 7)

s.add((((a2 >> 4) & 1) + 5) + (((a3 >> 1) & 1) + 1) == 6)


print(s.check())

print(s.model())



갓 z3 ;;


따라서 나온 결과값이 name 값이 되고, 플래그가 된다.

'wargame > reversing.kr' 카테고리의 다른 글

[reversing.kr]Direct3D FPS Writeup  (0) 2018.08.02
[reversing.kr] ransomware writeup  (0) 2018.08.02

[reversing.kr] ransomware writeup

wargame/reversing.kr

문제를 받아보면 upx 패킹이 되어있다는 것을 알 수 있다. 


패킹을 푼 후 코드를 분석해보면 hexray를 막기 위해 의미없는 코드들을 중간에 끼워 함수의 길이를 크게 늘려놓은것을 확인할 수 있다. 

보는 바와 같이 레지스터 값을 push 하고 다시 pop 하는 과정만 반복한다.

실제 의미있는 코드부분에 함수의 프롤로그가 될 수 있도록 패치를 한뒤, 함수지정을 해서 hexray를 가능하게 했다.


int sub_44A765()

{

  char file_char; // al

  int v2; // [esp+Ch] [ebp-24h]

  char *key_1_; // [esp+14h] [ebp-1Ch]

  const char *__key_end_offset__; // [esp+18h] [ebp-18h]

  FILE *new_fp; // [esp+1Ch] [ebp-14h]

  unsigned int file_size; // [esp+20h] [ebp-10h]

  unsigned int key_length; // [esp+24h] [ebp-Ch]

  unsigned int index; // [esp+28h] [ebp-8h]

  FILE *fp; // [esp+2Ch] [ebp-4h]


  printf("Key : ");

  no_mean_401000();

  scanf("%s", Key_44D370);

  __key_end_offset__ = Key_44D370;

  key_1_ = &Key_44D370[1];

  __key_end_offset__ += strlen(__key_end_offset__);

  v2 = ++__key_end_offset__ - &Key_44D370[1];

  key_length = __key_end_offset__ - &Key_44D370[1];

  no_mean_401000();

  index = 0;

  fp = fopen("file", "rb");

  no_mean_401000();

  if ( !fp )

  {

    no_mean_401000();

    printf("\n\n\n파일을 찾을수 없다!\n");

    no_mean_401000();

    exit(0);

  }

  fseek(fp, 0, 2);                              // go to end

  no_mean_401000();

  file_size = ftell(fp);

  no_mean_401000();

  rewind(fp);

  no_mean_401000();

  while ( !feof(fp) )

  {

    no_mean_401000();

    file_char = fgetc(fp);

    file_stream_5415B8[index] = file_char;

    no_mean_401000();

    ++index;

    no_mean_401000();

  }

  no_mean_401000();

  for ( index = 0; index < file_size; ++index )

  {

    file_stream_5415B8[index] ^= Key_44D370[index % key_length];

    no_mean_401000();

    file_stream_5415B8[index] = ~file_stream_5415B8[index];

    no_mean_401000();

  }

  fclose(fp);

  no_mean_401000();

  new_fp = fopen("file", "wb");

  no_mean_401000();

  no_mean_401000();

  for ( index = 0; index < file_size; ++index )

  {

    fputc(file_stream_5415B8[index], new_fp);

    no_mean_401000();

  }

  printf(asc_44C1E8);

  no_mean_401000();

  return getch();

}

 보는 바와 같이 루틴은 단순히 키값을 받고, 파일의 값을 not 한뒤 키값과 xor 해서 파일을 암호화 한다. 

readme.txt 를 보면 파일의 형식은 exe 였다. exe 파일은 보통 앞의 magic 값인 MZ 뒤에 널바이트가 꽤 많이 붙어있기 때문에 앞의 MZ를 제외한 나머지부분의 원래 값이 0이라 가정하고 나올 수 있는 key 값을 나오게 해보았다.

다음과 같이 대강 key값으로 보이는 부분이 몇개 보인다. 특히 play 부분의 단어가 같은 간격으로 반복되는 것으로 보아 key의 길이는 12바이트라는 것을 알 수 있었고 이를 통해 키 값을 게싱하여 파일을 복호화하였다.


복호화 된 exe 파일은 upx 패킹이 되어있었고, 그 파일을 실행시키면 키값을 알려준다. (나는 실행이 안되길래 그냥 프로그램을 까서 확인했다.)

'wargame > reversing.kr' 카테고리의 다른 글

[reversing.kr]Direct3D FPS Writeup  (0) 2018.08.02
[reversing.kr]Position Writeup  (0) 2018.08.02

[CODEBLUE 2018] odin part 1 writeup

ctf/codeblue CTF 2018

문제의 설명을 보면 ROM 에 저장되어있는 것을 덤프한 바이너리를 준것으로 보인다. 


I’ve installed a smart lock device to the entrance door of my home a while ago. The smart lock can be controlled using a smartphone app over Bluetooth Low Energy. I noticed, a few times since the installation, that there're some traces left in my home like someone trespassed. I really suspect that there’s a hidden backdoor in the smart lock. Could you find out the backdoor command for unlocking the door?

The genuine smartphone app unlocks the door with 1 byte length command "91" (in hex) after authorization. Your task is to build another command bytes to unlock the door.

I’ve got a firmware dump from BLE SoC on the smart lock board (nRF52832) using SWD and captured BLE packets on genuine app’s unlocking operation. I also analyzed the smartphone app for smart lock and wrote a document of BLE communication protocol between the app and the smart lock. Here is it. Files

Firmware dump (Memory dump of 0x00000000-0x00080000): memdump_00000000_00080000.bin

Captured BLE packets of genuine app’s unlocking operation: genuine_app_unlock_operation.pcap 


그리고 해당 도어락의 연산장치는 nRF52832 라고 되어있다. 이 연산장치에 대해서 조사해보면

다음과 같이 ARM 아키텍쳐로 되어있는 것을 알 수 있다. 

어떤 바이너리인진 모르겠지만 밑도 끝도 없이 IDA 에 바이너리를 올려놓고, 대충 프로그램 언어가 ARM Thumb 모드로 되어있다는 걸 예측했다. 


이제 코드의 주요 지점이나 엔트리 포인트를 알아야 하는데, 찾을수 없어서 문을 여는 코드가 0x91 이라는 단서에서 착안하여 전체를 code로 바꾸고 거기서 #0x91 텍스트를 찾았다.

그랬더니 0x91과 다른 변수를 CMP 하는 코드의 영역이 있었고 대략 이부분이 코드의 주요부분이라고 짐작할 수 있었다. 


int __fastcall sub_31174(unsigned __int8 *a1, int a2, int a3, _DWORD *a4)

{

  unsigned __int8 *v4; // ST0C_4

  int v5; // ST08_4

  int v6; // r3

  _DWORD *v8; // [sp+0h] [bp-20h]

  int v9; // [sp+4h] [bp-1Ch]

  unsigned int v10; // [sp+8h] [bp-18h]

  unsigned __int8 *v11; // [sp+Ch] [bp-14h]

  unsigned __int8 v12; // [sp+17h] [bp-9h]


  v4 = a1;

  v5 = a2;

  v9 = a3;

  v8 = a4;

  *a4 = 0;

  sub_3108C((int)a1, a2);

  v12 = *v4;

  v11 = v4 + 1;

  v10 = v5 - 1;

  if ( (v12 & 0x80u) != 0 && MEMORY[0x20004BEC] ^ 1 )

    return 16;

  if ( v12 == 32 )

    return sub_31300((int)v11, v10);

  if ( (signed int)v12 > 0x20 )

  {

    if ( v12 == 0x81 )

    {

      if ( v10 == 7 )

        v6 = sub_31074(v11);

      else

        v6 = 1;

    }

    else if ( (signed int)v12 > 0x81 )

    {

      if ( v12 == 0x90 )

      {

        if ( v10 )

          v6 = 1;

        else


          v6 = sub_30F14();

      }

      else

      {

        if ( v12 != 0x91 )

          return 3;

        if ( v10 )

          v6 = 1;

        else

          v6 = LOCK_OPEN();

      }

    }

    else

    {

      if ( v12 != 0x80 )

        return 3;

      if ( v10 == 16 )

        v6 = sub_30ED4(v11);

      else

        v6 = 1;

    }

  }

  else if ( v12 == 1 )

  {

    if ( v10 == 16 )

      v6 = sub_30FB0(v11);

    else

      v6 = 1;

  }

  else if ( v12 == 16 )

  {

    if ( v10 == 1 )

      v6 = sub_31024(*v11, v9, v8);

    else

      v6 = 1;

  }

  else

  {

    if ( v12 )

      return 3;

    if ( v10 )

      v6 = 1;

    else

      v6 = sub_30F2C(v9, v8);

  }

  return v6;

전체적인 코드를 보면 값을 여러개 비교하는데, 비교하는 값들이 문제 설명에 나와있는 값들이랑 동일한 값이라는 걸 볼 수 있다. 


이때 설명에 나와있는 값이 아닌 0x20 값을 먼저 비교하는 부분이 있는데, 이부분이 백도어가 들어가 있는 코드라는 것을 알 수 있다.


0x91 코드가 있을 시에 문을 여는 코드를 보니, 특정 함수에 대한 콜을 볼 수 있는데, 아무래도 PIN CALL 을 하는 함수인것 같다. 첫번째 인자가 0x77 일때, 문을 열고 닫는 CALL 을 하고, 3번째 인자가 1일때 문을 열고 0일때 문을 닫는다. 


백도어 부분을 조사해보면 입력된 데이터와 특정 값을 비교하는 것을 볼 수 있다. 

signed int __fastcall backdoor_31300(int data, unsigned int a2)

{

  int v2; // r3

  unsigned int length; // [sp+8h] [bp-18h]

  int next_length; // [sp+8h] [bp-18h]

  int __data__; // [sp+Ch] [bp-14h]

  char next_data; // [sp+17h] [bp-9h]


  __data__ = data;

  length = a2;

  if ( a2 <= 7 )

    return 3;

  if ( (unsigned __int8)DATA_CMP_312B8(data) ^ 1 )// if data is same : pass / 8byte

    return 3;

  next_data = *(_BYTE *)(__data__ + 8);

  next_length = length - 9;

  if ( next_data )

  {

    if ( next_data == 1 )

    {

      if ( next_length )

        v2 = 1;

      else

        v2 = OPEN_312AC();

    }

    else

    {

      v2 = 2;

    }

  }

  else if ( next_length )

  {

    v2 = 1;

  }

  else

  {

    v2 = sub_312A0();

  }

  return v2;

}


비교하는 함수를 분석해보면

다음과 같이 데이터를 비교한다. 

앞의 4바이트부분은 0x100000A4 부분에서, 뒤의 4바이트 부분은 0x3ae68 부분에서 값이 같은지 비교한다.

0x3ae68 부분의 4바이트는 다음과 같이 되어있다.


따라서 플래그 값에 들어가는 부분은 앞의 0x20 와 마지막에 들어가야 하는 0x01 까지 포함해서 20 ?? ?? ?? ?? e5 bc 03 b7 01이 된다.

0x100000A4 부분은 데이터에 표현이 안돼있는데, 해당 주소를 구글에 검색해 보면 다음과 같은 정보가 나온다.


https://devzone.nordicsemi.com/f/nordic-q-a/13734/how-to-obtain-the-full-mac-address-in-nrfjprog


즉, 0x100000A4  부분에는 도어락의 MAC 주소가 little endian 형식으로 저장되어있다는 것을 알 수 있다.

MAC address 는 문제에 나온 패킷 파일을 통해 알 수 있다. 


따라서 MAC address 에서 little endian 으로 4바이트만 가져와서 빈자리에 넣으면 플래그가 나온다.

CBCTF{20afa8c15fe5bc03b701}이 된다.