슬기로운 해커 생활

Reverse engineering

[Dreamhack] Easy Assembly

Tjdmin1 2024. 7. 3. 03:40

https://dreamhack.io/wargame/challenges/1095

 

Easy Assembly

어셈블리 언어에 대한 지식이 있으시다면 이 문제는 쉽게 해결할 수 있는 아주 간단한 문제입니다! 필요한 정보만을 찾아 플래그를 획득해 주세요 플래그의 형식은 DH{…} 입니다.

dreamhack.io

Analysis


prob - ELF Header

_start function

void __noreturn start()
{
      _BYTE *v0; // ecx
      _BYTE *v1; // ecx
      int v2; // eax
      int v3; // [esp-Ch] [ebp-Ch]
      _BYTE *v4; // [esp-4h] [ebp-4h]

      if ( v3 == 1 )
      {
            print(usage1);
            print(v0);
            print(usage2);
      }
      else
      {
            len = (int)strlen(v4);
            if ( check_password(len, 0, enc_flag, v1) )
              print(fail_msg);
            else
              print(correct_msg);
      }
      v2 = sys_exit(0);
}

 

보통은 _start 함수에서 _libc_start_main 함수를 통해 main 함수로 이동해 실행하는데 이 문제에선 _start에서 바로 코드가 실행되게 됩니다.

 

바이너리를 그냥 실행시켰을 때 'Usage : ./prob <key>'라는 문구로 봐서 v3는 ./prob에 들어가는 인자값의 개수 즉 v3 = argc라고 볼 수 있고 v0 = argv[0]의 값으로 볼 수 있습니다.

strlen - argument

_BYTE *__usercall strlen@<eax>(_BYTE *a1@<eax>)
{
      _BYTE *v1; // ebx

      v1 = a1;
      while ( *a1 )
            ++a1;
      return (_BYTE *)(a1 - v1);
}

 

strlen 함수로 진입할 때 인자 값으로 eax값이 들어가게 되는데, 이때 prob의 인자로 tjdmin1을 넣어줬을 때 eax에 tjdmin1이 담기는 걸로 봐선 _start 함수의 v4는 argv[1]이 되는 것을 알 수 있습니다.

 

int __usercall check_password@<eax>(int a1@<eax>, int a2@<ecx>, _BYTE *a3@<edi>, _BYTE *a4@<esi>)
{
      do
      {
            a2 |= (unsigned __int8)(*a3++ ^ len ^ *a4++);
            --a1;
      }
      while ( a1 );
      return a2;
}

check_password - argument

_start 함수의 v1 = argv[1]의 값이 담겨 있는 것을 볼 수 있습니다.

※ 결론적으론 v1 = v4로 볼 수 있습니다.

 

다시 한번 check_password 함수를 정리해 보면 아래와 같습니다.

int __usercall check_password@<eax>(int len@<eax>, int result@<ecx>, _BYTE *enc_flag@<edi>, _BYTE *key@<esi>)
{
      do
      {
            result |= (unsigned __int8)(*enc_flag++ ^ ::len ^ *key++);
            --len;
      }
      while ( len );
      return result;
}

check_password

암호화 과정 : enc_flag[i] ^ len ^ key[i]

 

xor의 특징 중 A = B ^ C는 B = A ^ C라는 특성이 있습니다.

이 특성을 이용하면 key를 구할 수 있게 됩니다.

 

result = enc_flag[i] ^ len ^ key[i]

0 = enc_flag[i] ^ len ^ key[i]

※ result가 0인 이유는 _start 함수에서 0이 떠야 print(correct_msg);를 출력해 주기 때문입니다.

 

즉 다시 한번 수식을 정리하자면 아래와 같습니다.

key[i] = enc_flag[i] ^ len

Exploit


key[i] = enc_flag[i] ^ len 수식을 활용해서 key 값을 구하는 Python 코드를 짜보겠습니다.

enc_flag = [0x74, 0x78, 0x4B, 0x65, 0x77, 0x48, 0x5C, 0x69, 0x68, 0x7E, 0x5C, 0x79, 0x77, 0x62, 0x46, 0x79, 0x77, 0x05, 0x46, 0x54, 0x73, 0x72, 0x59, 0x69, 0x68, 0x7E, 0x5C, 0x7E, 0x5A, 0x61, 0x57, 0x6A, 0x77, 0x66, 0x5A, 0x52, 0x02, 0x62, 0x5C, 0x79, 0x77, 0x5C, 0x00, 0x7C, 0x57, 0x0D, 0x0D, 0x4D]

for i in range(len(enc_flag)):
    print(chr(len(enc_flag) ^ enc_flag[i]), end='')

key

이렇게 key값이 잘 뜨는 것을 볼 수 있습니다.

 

저 값을 prob에 인자 값으로 넣어주게 되면 correct_msg가 출력이 됩니다.

correct_msg

여담으로 Flag의 값이 base64로 encoding한 값이길래 궁금해서 decoding 해봤더니 웃긴 값이 나왔습니다.

 

'Reverse engineering' 카테고리의 다른 글

[Dreamhack] simple-operation  (1) 2024.07.03
[Dreamhack] rev-basic-1  (1) 2024.07.03
[Dreamhack] rev-basic-0  (0) 2024.07.03