@Raaquilla

Crack Me V2

0
0m read

Challenge description

EvilCorp has upgraded their license validator! It’s now AI powered quantum resistant dark blockchain infused!


Approach

Decompiling the binary gives us a few key pieces of information.

  1. An array of bytes that is likely the encrypted flag.
_BYTE byte_2500[66] = {
    84, 107, 83, 119, 11, 30,
    -126, -45, 19, -29, 90, -95,
    -4, -52, 3, 40, -24, 109, 1,
    -66, 72, 101, -30, -66, 87,
    -96, -120, -2, 116, 48, -124,
    -119, 5, -8, -41, -6, -20, 2,
    120, -98, -2, 31, -115, -124,
    -76, -3, -110, 92, 45, -58,
    -126, -89, 106, -22, -18, 77,
    -68, 118, -51, -43, -89, 35,
    -43, 34, 107, 69
};
  1. The main function.
__int64 __fastcall main(int a1, char **a2, char **a3) {
  char s[136];
  unsigned __int64 v5;

  v5 = __readfsqword(0x28u);
  setvbuf(stdout, 0, 2, 0);
  setvbuf(stderr, 0, 2, 0);
  printf("serial: ");
  if (!fgets(s, 128, stdin))
    return 0;
  s[strcspn(s, "\n")] = 0;
  if (sub_13D2(s, 66)) {
    if (sub_1526(s))
      puts("you got it!");
    else
      puts("nope");
    return 0;
  } else {
    puts("nope");
    return 0;
  }
}
  1. The password checks.
_BOOL8 __fastcall sub_1526(const char *a1)
{
  if ( strlen(a1) != 66 )
    return 0;
  if ( sub_1457((__int64)a1) )
    return (unsigned int)sub_13FE((__int64)a1) == 1301556880;
  return 0;
}

_BOOL8 __fastcall sub_1457(__int64 a1)
{
  char v2;
  unsigned __int8 v3;
  unsigned __int64 i;

  v2 = 90;
  for ( i = 0; i <= 0x41; ++i )
  {
    v3 = sub_13B8((unsigned __int8)(29 * i + *(_BYTE *)(a1 + i)) ^ (unsigned __int8)(7 * v2 + 61));
    if ( (unsigned __int8)sub_1383(v3, (i + 3) & 7) != byte_2500[i] )
      return 0;
    v2 = *(_BYTE *)(a1 + i);
  }
  return *(_BYTE *)(a1 + 66) == 0;
}

__int64 __fastcall sub_1383(unsigned __int8 a1, char a2)
{
  return (a1 << (a2 & 7)) | (unsigned int)((int)a1 >> (8 - (a2 & 7)));
}

__int64 __fastcall sub_13B8(unsigned __int8 a1)
{
  return -59 * (unsigned int)a1 + 101;
}

_BOOL8 __fastcall sub_13D2(const char *a1, __int64 a2)
{
  return a2 == strlen(a1);
}

__int64 __fastcall sub_13FE(__int64 a1)
{
  unsigned int v2;
  __int64 i;

  v2 = -2128831035;
  for ( i = 0; *(_BYTE *)(a1 + i); ++i )
    v2 = 16777619 * (*(unsigned __int8 *)(a1 + i) ^ v2);
  return v2;
}

Lets clean up the logic. Here is a not quite equivelant (due to unsigned ints), but more understandable version.

bool sub_1457(int *a1) {
  int v3;
  char v2 = 90;

  for (int i = 0; i <= 65; ++i) {
    v3 = sub_13B8(29 * i + a1[i]) ^ (7 * v2 + 61);
    if (sub_1383(v3, (i + 3) & 7) != byte_2500[i])
      return 0;
    v2 = a1[i];
  }
  return a1[66] == 0;
}

int sub_1383(int a1, char a2) { // rol8 from Crack Me V1
  return (a1 << (a2 & 7)) | (a1 >> (8 - (a2 & 7)));
}

int sub_13B8(int a1) { return -59 * a1 + 101; }

int sub_13FE(int a1) {
  int v2 = -2128831035;
  for (int i = 0; a1[i]; ++i)
    v2 = 16777619 * a1[i] ^ v2);
  return v2;
}

int main() {
  char s[136]; // our input

  if (strlen(s) == 66 && sub_1457(a1) && sub_13FE(a1) == 1301556880)
    puts("you got it!");
  else
    puts("nope");
}

We can reverse the logic in python to get the flag.

Python implementation

flag_bytes = [
    84,  107, 83,  119, 11,  30,  130,
    211, 19,  227, 90,  161, 252, 204,
    3,   40,  232, 109, 1,   190, 72,
    101, 226, 190, 87,  160, 136, 254,
    116, 48,  132, 137, 5,   248, 215,
    250, 236, 2,   120, 158, 254, 31,
    141, 132, 180, 253, 146, 92,  45,
    198, 130, 167, 106, 234, 238, 77,
    188, 118, 205, 213, 167, 35,  213,
    34,  107, 69
]

def sub_1383_inv(a1, a2):
    # (a1 << (a2 & 7)) | (a1 >> (8 - (a2 & 7)))
    return ((a1 >> a2) | (a1 << (8 - a2))) & 0xFF

def sub_13B8_inv(y):
    for x in range(256):
        # sub_13B8
        if (-59 * x + 101) & 0xFF == y:
            return x

flag = []
v2 = 90
# sub_1457
for i in range(66):
    # sub_1383(v3, (i + 3) & 7) != byte_2500[i])
    b = sub_1383_inv(flag_bytes[i], (i + 3) & 7)

    # sub_13B8(...) ^ ...;
    x = sub_13B8_inv(b)

    # ... ^ (7 * v2 + 61);
    input_byte = x ^ ((7 * v2 + 61) & 0xFF)

    # sub_13B8(29 * i + a1[i]) ^ ...;
    input_byte = (input_byte - 29 * i) & 0xFF
    flag.append(input_byte)
    v2 = input_byte

print(bytes(flag).decode("ascii", errors="ignore"))

Running the script gives us our flag:

RISC{nextgen_totally_unbreakable_7a3211c840e8d7a4e9bd76f5539bd29d}
View Source