IOLI CrackMe

IOLI CrackMe

Level 0x00

The first level is quite simple, i used Ghidra to decompile the executable file, then find the main function

int __cdecl _main(int _Argc,char **_Argv,char **_Env)

{
  int iVar1;
  size_t in_stack_ffffffc0;
  char local_1c [24];
  
  __alloca(in_stack_ffffffc0);
  ___main();
  _printf("IOLI Crackme Level 0x00\n");
  _printf("Password: ");
  _scanf("%s",local_1c);
  iVar1 = _strcmp(local_1c,"250382");
  if (iVar1 == 0) {
    _printf("Password OK :)\n");
  }
  else {
    _printf("Invalid Password!\n");
  }
  return 0;
}

We can clearly see that the string that the program take in input (local_1c) is being compared to “250382” so if we input that number the crackme is solved

Level 0x01

The second level is very similar to the first, this is the script:

int __cdecl _main(int _Argc,char **_Argv,char **_Env)

{
  size_t in_stack_ffffffe0;
  int local_8;
  
  __alloca(in_stack_ffffffe0);
  ___main();
  _printf("IOLI Crackme Level 0x01\n");
  _printf("Password: ");
  _scanf("%d",&local_8);
  if (local_8 == 0x149a) {
    _printf("Password OK :)\n");
  }
  else {
    _printf("Invalid Password!\n");
  }
  return 0;
}

So we can see that it does the same exact compare on the inputted value, but this time is not comparing with a string but with a hex value, we can simply convert it in decimal

0x149a = 5274

And that’s the solution

Level 0x02

The third level is equivalent to the second, only with a different control value

int __cdecl _main(int _Argc,char **_Argv,char **_Env)

{
  size_t in_stack_ffffffe0;
  int local_8;
  
  __alloca(in_stack_ffffffe0);
  ___main();
  _printf("IOLI Crackme Level 0x02\n");
  _printf("Password: ");
  _scanf("%d",&local_8);
  if (local_8 == 0x52b24) {
    _printf("Password OK :)\n");
  }
  else {
    _printf("Invalid Password!\n");
  }
  return 0;
}

With the same procedure we can convert 0x52b25 in decimal 338724 and thats the solution right here

Level 0x03

The fourth level use a different function to confront the input, let’s take a look:

main function

int __cdecl _main(int _Argc,char **_Argv,char **_Env)

{
  size_t in_stack_ffffffe0;
  int local_8;
  
  __alloca(in_stack_ffffffe0);
  ___main();
  _printf("IOLI Crackme Level 0x03\n");
  _printf("Password: ");
  _scanf("%d",&local_8);
  _test(local_8,0x52b24);
  return 0;
}

We see the test function which take the input, and a string; this is test:

void __cdecl _test(int param_1,int param_2)

{
  if (param_1 == param_2) {
    _shift("Sdvvzrug#RN$$$#=,");
  }
  else {
    _shift("Lqydolg#Sdvvzrug$");
  }
  return;
}

Also this function uses another procedure called shift, this is it:

void __cdecl _shift(char *param_1)

{
  size_t sVar1;
  uint local_80;
  char local_7c [120];
  
  local_80 = 0;
  while( true ) {
    sVar1 = _strlen(param_1);
    if (sVar1 <= local_80) break;
    local_7c[local_80] = param_1[local_80] + -3;
    local_80 = local_80 + 1;
  }
  local_7c[local_80] = '\0';
  _printf("%s\n",local_7c);
  return;
}

If we take a closer look, we can see that in the main function the confronted value is the same as the Level 0x02, the differnce here is how the check is done in the _test function. So we can put the same passowrd as Level 0x02 and the solution is founded!

Level 0x04

In the fifth level we can see an actual change of the check function, let’s take a look on the main to see this:

int __cdecl _main(int _Argc,char **_Argv,char **_Env)

{
  size_t in_stack_ffffff60;
  char local_7c [120];
  
  __alloca(in_stack_ffffff60);
  ___main();
  _printf("IOLI Crackme Level 0x04\n");
  _printf("Password: ");
  _scanf("%s",local_7c);
  _check(local_7c);
  return 0;
}

You can clearly see the _check function before the return that control the inputted value, let’s look the control function:

void __cdecl _check(char *param_1)

{
  size_t sVar1;
  char local_11;
  uint local_10;
  int local_c;
  int local_8;
  
  local_c = 0;
  local_10 = 0;
  while( true ) {
    sVar1 = _strlen(param_1);
    if (sVar1 <= local_10) {
      _printf("Password Incorrect!\n");
      return;
    }
    local_11 = param_1[local_10];
    _sscanf(&local_11,"%d",&local_8);
    local_c = local_c + local_8;
    if (local_c == 0xf) break;
    local_10 = local_10 + 1;
  }
  _printf("Password OK!\n");
                    /* WARNING: Subroutine does not return */
  _exit(0);
}

Let’s analyse this, you can see the first if control if the password length is less than the local_10 param, that param is updated by 1 on every cicle of the while, the next part controll a specified value, 0xf, so the sum of the value in the provided password must be less that 15.

Level 0x05

This level is the same as before, this is the check function:

void __cdecl _check(char *param_1)

{
  size_t sVar1;
  char local_11;
  uint local_10;
  int local_c;
  int local_8;
  
  local_c = 0;
  local_10 = 0;
  while( true ) {
    sVar1 = _strlen(param_1);
    if (sVar1 <= local_10) break;
    local_11 = param_1[local_10];
    _sscanf(&local_11,"%d",&local_8);
    local_c = local_c + local_8;
    if (local_c == 0x10) {
      _parell(param_1);
    }
    local_10 = local_10 + 1;
  }
  _printf("Password Incorrect!\n");
  return;
}

This is basically a copy of the function in the previous level, the only difference is the sum value, this time it is 0x10 which is 16, so every password with a sum of it’s value equal to 16 is correct.

Level 0x06

This level is a modified version of the previous 2 levels, this is the main function:

int __cdecl _main(int _Argc,char **_Argv,char **_Env)

{
  size_t in_stack_ffffff60;
  char local_7c [120];
  
  __alloca(in_stack_ffffff60);
  ___main();
  _printf("IOLI Crackme Level 0x06\n");
  _printf("Password: ");
  _scanf("%s",local_7c);
  _check(local_7c,(int)_Env);
  return 0;
}

You can see that the _check function takes the value inputted by the user, and another value, an environment variable.

void __cdecl _check(char *param_1,int param_2)

{
  size_t sVar1;
  char local_11;
  uint local_10;
  int local_c;
  int local_8;
  
  local_c = 0;
  local_10 = 0;
  while( true ) {
    sVar1 = _strlen(param_1);
    if (sVar1 <= local_10) break;
    local_11 = param_1[local_10];
    _sscanf(&local_11,"%d",&local_8);
    local_c = local_c + local_8;
    if (local_c == 0x10) {
      _parell(param_1,param_2);
    }
    local_10 = local_10 + 1;
  }
  _printf("Password Incorrect!\n");
  return;
}

This function is basically the same as before, but this time we can see another function _parell, that takes the password and the environment variable, let’s see:

void __cdecl _parell(char *param_1,int param_2)

{
  int iVar1;
  int local_c;
  uint local_8;
  
  _sscanf(param_1,"%d",&local_8);
  iVar1 = _dummy(local_8,param_2);
  if (iVar1 != 0) {
    for (local_c = 0; local_c < 10; local_c = local_c + 1) {
      if ((local_8 & 1) == 0) {
        _printf("Password OK!\n");
                    /* WARNING: Subroutine does not return */
        _exit(0);
      }
    }
  }
  return;
}
undefined4 __cdecl _dummy(undefined4 param_1,int param_2)

{
  int iVar1;
  int local_8;
  
  local_8 = 0;
  do {
    if (*(int *)(local_8 * 4 + param_2) == 0) {
      return 0;
    }
    iVar1 = local_8 * 4;
    local_8 = local_8 + 1;
    iVar1 = _strncmp(*(char **)(iVar1 + param_2),"LOLO",3);
  } while (iVar1 != 0);
  return 1;
}

Before the numeric checks, _parell call the _dummy funtion which check if an Environment Variable named “LOLO” exists (Actually it controls only te first three characters, so any variable startin with “LOL” is valid), if so then parell check if the value inserted is odd or even. So, if you set an environment variable named “LOL*” and input an even value.