이 문제를 2년전에 풀어보고 이번에 포스팅 하면서 다시 풀어봤는데
예전과 달리 넘 쉽게 풀려서 어이가 없었다.
예전에 풀때는 ltrace를 이용해서 노가다해서 풀었는데
다시보니 사실 그럴 필요가 없는 문제였다.
문제 설명을 읽어보면 산타할아버지가 박스를 주는데
비밀번호를 맞춰야 사탕을 주고, 나머지 사람들은 석탄을 준다고 한다...
문제 파일로 SantaBox라는 이름의 바이너리 파일이 주어진다.
칼리 리눅스에서 file 명령어를 통해 해당 바이너리의 정보를 확인해보면
ELF 파일임을 알 수 있다.
ELF 파일은 리눅스에서 실행 가능한 EXE 파일같은거라고 생각하면 된다.
시험삼아 바이너리 파일을 실행시켜 본다.
실행시키면 저렇게 Enter code here: 라고 뜨면서 입력을 받는데
giveme 라고 입력해봤더니 석탄을 줬다..
좀더 자세히 보기위해 디스어셈블러인 IDA 프로그램을 이용했다.
main 함수 외에 특별한 함수는 보이지 않는다.
IDA를 이용하면 오른쪽 화면과 같이 수도코드로 함수들을 디스어셈블 해준다.
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char *v3; // rax
int v4; // ecx
unsigned int v5; // edx
unsigned __int64 v6; // rax
unsigned __int64 v7; // rdx
__m128i si128; // xmm0
int v9; // edx
unsigned __int64 v10; // rcx
unsigned __int64 v11; // rcx
unsigned __int64 v12; // rcx
unsigned __int64 v13; // rcx
unsigned __int64 v14; // rcx
unsigned __int64 v15; // rcx
unsigned __int64 v16; // rcx
unsigned __int64 v17; // rcx
unsigned __int64 v18; // rcx
unsigned __int64 v19; // rcx
unsigned __int64 v20; // rcx
unsigned __int64 v21; // rcx
unsigned __int64 v22; // rcx
unsigned __int64 v23; // rdx
__int64 result; // rax
char v25[32]; // [rsp+0h] [rbp-78h] BYREF
char s1[16]; // [rsp+20h] [rbp-58h] BYREF
__m128i v27; // [rsp+30h] [rbp-48h] BYREF
__m128i v28; // [rsp+40h] [rbp-38h] BYREF
__m128i v29; // [rsp+50h] [rbp-28h]
unsigned __int64 v30; // [rsp+68h] [rbp-10h]
v30 = __readfsqword(0x28u);
strcpy(v25, "PH?>OA(vN/io/Zb<q.Zt+pZKm.io0x");
puts("-----------------------------------------");
puts("| |");
puts("| |");
puts("| |");
puts("| Santa's Mysterious |");
puts("| Box of Treats |");
puts("| |");
puts("| |");
puts("| |");
puts("| |");
puts("| |");
puts("-----------------------------------------");
__printf_chk(1LL, "Enter code here: ");
__isoc99_scanf("%s", s1);
v3 = s1;
do
{
v4 = *v3;
v3 += 4;
v5 = ~v4 & (v4 - 16843009) & 0x80808080;
}
while ( !v5 );
if ( (~v4 & (v4 - 16843009) & 0x8080) == 0 )
v5 >>= 16;
if ( (~v4 & (v4 - 16843009) & 0x8080) == 0 )
v3 += 2;
v6 = &v3[-__CFADD__(v5, v5) - 3] - s1;
if ( v6 )
{
if ( v6 - 1 <= 0xE )
{
v9 = 0;
LABEL_14:
s1[v9] -= 5;
v10 = v9 + 1;
if ( v6 > v10 )
{
s1[v10] -= 5;
v11 = v9 + 2;
if ( v6 > v11 )
{
s1[v11] -= 5;
v12 = v9 + 3;
if ( v6 > v12 )
{
s1[v12] -= 5;
v13 = v9 + 4;
if ( v6 > v13 )
{
s1[v13] -= 5;
v14 = v9 + 5;
if ( v6 > v14 )
{
s1[v14] -= 5;
v15 = v9 + 6;
if ( v6 > v15 )
{
s1[v15] -= 5;
v16 = v9 + 7;
if ( v6 > v16 )
{
s1[v16] -= 5;
v17 = v9 + 8;
if ( v6 > v17 )
{
s1[v17] -= 5;
v18 = v9 + 9;
if ( v6 > v18 )
{
s1[v18] -= 5;
v19 = v9 + 10;
if ( v6 > v19 )
{
s1[v19] -= 5;
v20 = v9 + 11;
if ( v6 > v20 )
{
s1[v20] -= 5;
v21 = v9 + 12;
if ( v6 > v21 )
{
s1[v21] -= 5;
v22 = v9 + 13;
if ( v6 > v22 )
{
s1[v22] -= 5;
v23 = v9 + 14;
if ( v6 > v23 )
s1[v23] -= 5;
}
}
}
}
}
}
}
}
}
}
}
}
}
goto LABEL_29;
}
v7 = v6 >> 4;
si128 = _mm_load_si128(&xmmword_C90);
*s1 = _mm_add_epi8(_mm_load_si128(s1), si128);
if ( v6 >> 4 != 1 )
{
v27 = _mm_add_epi8(_mm_load_si128(&v27), si128);
if ( v7 != 2 )
{
v28 = _mm_add_epi8(_mm_load_si128(&v28), si128);
if ( v7 != 3 )
v29 = _mm_add_epi8(si128, v29);
}
}
v9 = v6 & 0xFFFFFFF0;
if ( v6 != (v6 & 0xFFFFFFFFFFFFFFF0LL) )
goto LABEL_14;
}
LABEL_29:
if ( !strcmp(s1, v25) )
puts("Candy candy and more candy! Just what I like!");
else
puts("You received coal!");
result = 0LL;
if ( __readfsqword(0x28u) != v30 )
start();
return result;
}
이것이 main 함수의 전체 코드이다.
길고 어려워 보이는데 몇가지 포인트만 확인하면 된다.
strcpy(v25, "PH?>OA(vN/io/Zb<q.Zt+pZKm.io0x");
가장 먼저 이부분
PH?>OA(vN/io/Zb<q.Zt+pZKm.io0x 라는 값을 v25 라는 변수에 넣고 있다.
__printf_chk(1LL, "Enter code here: ");
__isoc99_scanf("%s", s1);
그다음 여기
사용자 입력을 받아서 s1 변수에 저장하고 있다.
그다음 여기 LABEL_14 부분을 보면
s1 변수를 이래저래 조작하는게 보인다.
(내용이 긴데 다 똑같은 계산이라 앞부분만 가져왔다.)
v9 변수에는 젤 처음 0이 저장되는데
s1[v9] -= 5; 는
s1[0] -= 5; 와 같은 의미이다.
s1변수에 첫번째 자리 글자에서 5를 빼라는 것이다.
그리고 v10 변수에 v9 + 1 을 해서 저장한다. (그럼 v10에는 1이 저장된다.)
그다음 s1[v10] -=5; 를 하는데
마찬가지로 s1[1] -= 5; 와 같은 의미이고, s1변수의 두번째 자리 글자에서 5를 빼준다.
그냥 LABEL_14 부분은 사용자가 입력한 값에서 모든 자리 글자를 -5 하는 계산이다.
if ( !strcmp(s1, v25) )
puts("Candy candy and more candy! Just what I like!");
else
puts("You received coal!");
마지막으로 s1과 v25 변수를 비교해서
똑같으면 캔디를 주고, 틀리면 석탄을 준다.
그럼 아주 간단하게 v25(PH?>OA(vN/io/Zb<q.Zt+pZKm.io0x)에다가 +5 를 해주면?
CyberChef(https://gchq.github.io/CyberChef)에서 바로 해볼 수 있다.
+5를 해주면 플래그를 찾을 수 있다.
플래그를 주면 사탕도 준다.
'CTF > 리버싱' 카테고리의 다른 글
[HouseplantCTF] Fragile - 리버싱 / Java (54) | 2022.06.06 |
---|---|
[UMDCTF] Twilight Zone - 리버싱 / dnSpy (65) | 2022.05.11 |
[UMDCTF] Easy Money - 리버싱 / Strings (72) | 2022.05.02 |
[DCTF] Seek - 리버싱 / C / fseek (40) | 2022.04.19 |
[Space Heroes CTF] Cape Kennedy - 리버싱 / Python (41) | 2022.04.10 |