https://dreamhack.io/wargame/challenges/1095
Analysis
_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]의 값으로 볼 수 있습니다.
_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;
}
_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;
}
암호화 과정 : 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값이 잘 뜨는 것을 볼 수 있습니다.
저 값을 prob에 인자 값으로 넣어주게 되면 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 |