The challenge
Value: 300 points
File Name for the challenge: chall4.elf
Message:
- find the correct password for the crackme to display the “Correct Password” message.
- your goal is not to make the app display “Correct Password” but to find the correct password which does that for you.
- brute-forcing won’t help but you can do whatever you want.
- don’t expose this challenge to a real work environment.
- flag format ritsCTF{<---flag-here--->}. Good Luck!
author - X3eRo0
Having a look at the decompiled binary, we see a lot of complexity has been introduced:
undefined8 realMain(void)
{
byte bVar1;
byte bVar2;
int iVar3;
size_t preDecodedFlag;
long in_FS_OFFSET;
uint local_ac;
char password [64];
byte encodedFlagData [88];
long local_10;
local_10 = *(long *)(in_FS_OFFSET + 0x28);
puts("--=[ Very Hard Crackme ]=--");
printf("passwd > ");
fgets(password,0x40,stdin);
encodedFlagData[0] = 0x6e;
encodedFlagData[1] = 0xf0;
encodedFlagData[2] = 0xec;
encodedFlagData[3] = 2;
encodedFlagData[4] = 0x6b;
encodedFlagData[5] = 0x24;
encodedFlagData[6] = 0xb1;
encodedFlagData[7] = 0x31;
encodedFlagData[8] = 6;
encodedFlagData[9] = 0xeb;
encodedFlagData[10] = 0x57;
encodedFlagData[11] = 0xf6;
encodedFlagData[12] = 0x5f;
encodedFlagData[13] = 0x90;
encodedFlagData[14] = 0x15;
encodedFlagData[15] = 0xe6;
encodedFlagData[16] = 0x1a;
encodedFlagData[17] = 0x15;
encodedFlagData[18] = 0x93;
encodedFlagData[19] = 0x1f;
encodedFlagData[20] = 0xba;
encodedFlagData[21] = 0x43;
encodedFlagData[22] = 0xd7;
encodedFlagData[23] = 0x3a;
encodedFlagData[24] = 0xd2;
encodedFlagData[25] = 0x80;
encodedFlagData[26] = 0x7a;
encodedFlagData[27] = 0xc;
encodedFlagData[28] = 0xab;
encodedFlagData[29] = 0xcc;
encodedFlagData[30] = 10;
encodedFlagData[31] = 0xfa;
encodedFlagData[32] = 0x93;
encodedFlagData[33] = 0xdd;
encodedFlagData[34] = 0xe8;
encodedFlagData[35] = 0x96;
encodedFlagData[36] = 0xa7;
encodedFlagData[37] = 0x75;
encodedFlagData[38] = 0xe9;
encodedFlagData[39] = 0x1d;
encodedFlagData[40] = 0xfc;
encodedFlagData[41] = 0x68;
encodedFlagData[42] = 0x7d;
encodedFlagData[43] = 1;
encodedFlagData[44] = 0x54;
encodedFlagData[45] = 0x96;
encodedFlagData[46] = 0x5a;
encodedFlagData[47] = 0x85;
encodedFlagData[48] = 0xcf;
encodedFlagData[49] = 0xd2;
encodedFlagData[50] = 200;
encodedFlagData[51] = 0x9f;
encodedFlagData[52] = 0xed;
encodedFlagData[53] = 0x1c;
encodedFlagData[54] = 0x6a;
encodedFlagData[55] = 0x29;
encodedFlagData[56] = 0xda;
encodedFlagData[57] = 0xd1;
encodedFlagData[58] = 0x30;
encodedFlagData[59] = 0x82;
encodedFlagData[60] = 0x7d;
encodedFlagData[61] = 0x82;
encodedFlagData[62] = 0xf1;
encodedFlagData[63] = 0xeb;
encodedFlagData[64] = 0xe0;
encodedFlagData[65] = 0x24;
encodedFlagData[66] = 0x15;
encodedFlagData[67] = 0x6d;
encodedFlagData[68] = 0x32;
encodedFlagData[69] = 0x30;
encodedFlagData[70] = 0x87;
encodedFlagData[71] = 0x28;
encodedFlagData[72] = 0x5c;
encodedFlagData[73] = 0x85;
local_ac = 0;
while (local_ac < 0x4a) {
bVar2 = (byte)local_ac;
bVar1 = ~-~((encodedFlagData[local_ac] << 5 | encodedFlagData[local_ac] >> 3) + 99 ^ bVar2);
bVar1 = (~((-bVar2 - ((-~(bVar1 << 7 | bVar1 >> 1) ^ 0x39) - 0x40 ^ 0x48) ^ bVar2) + 0x98 ^ 0xf3
) ^ bVar2) - bVar2 ^ bVar2;
bVar1 = ~(~((bVar1 << 6 | bVar1 >> 2) - bVar2) ^ 0x42);
bVar1 = ~-((((bVar1 * '\x02' | bVar1 >> 7) - 0x12 ^ 0x26) - bVar2 ^ bVar2) + 0x1f);
bVar1 = (~(((-bVar2 - ((-((bVar1 << 7 | bVar1 >> 1) ^ 0x99) ^ bVar2) + bVar2 ^ bVar2) ^ 0x83) +
bVar2 ^ 0x2b) + bVar2) ^ 0x62) - 0x25;
encodedFlagData[local_ac] = bVar1 * -0x80 | bVar1 >> 1;
local_ac = local_ac + 1;
}
preDecodedFlag = strlen((char *)encodedFlagData);
iVar3 = decrypter(password,encodedFlagData,preDecodedFlag & 0xffffffff,encodedFlagData);
if (iVar3 == 0) {
puts("Correct Password...");
}
else {
puts("Wrong Password...");
}
if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
And here the updated decrpyter function:
ulong decrypter(long password,long encodedFlag,int size)
{
uint i;
int j;
i = 0;
j = 0;
while (((j < size && (*(char *)(password + j) != '\0')) && (*(char *)(encodedFlag + j) != '\0')))
{
i = i | (int)(char)(*(byte *)(encodedFlag + j) ^ *(byte *)(password + j));
j = j + 1;
}
return (ulong)i;
}
We can see that the key has been omitted here and the expression only evaluates to 0, if the flag is xor-ed with itself. That’s an interesting fact. According to the code, the flag is stored in clear text.
I then decided to have a look at the value of rsi during the xor instruction. So I copied the relative address from BinaryNinja. Next, I fired up gdb-peda, set a relative breakpoint at the xor instruction, let the program run, entered a dummy password and analysed the rsi register once the breakpoint was hit:
starti
brva 0x11b4
c
<some pass>
x/s $rsi
Flag: riftCTF{---=[Did_Y0U_kN0W_YoU_CoU1D_ActuallY_FinD_ThE_FlaG_1n_m3M0rY]=--}