1nzag

'ctf'에 해당되는 글 1건

  1. [CODEBLUE 2018] odin part 1 writeup

[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}이 된다.