user@sys

[m3rcurylake.mov]

[wired]

[user@sys]#>>

cat "Crackme100.r3m"_

@Crackme100

/August 7, 2025/

link to the binary

Started off with the usual recon — readelf revealed the presence of a main function. Dumped it into Ghidra and ran into a weird-looking keygen algorithm.

After cleaning things up, the code looked like this:

#include <stdio.h>
#include <string.h>

int main(void)

{
  int addened;
  int adder;
  char input[51];
  char output[51];
  int random2;
  int random1;
  char fix;
  int secret3;
  int secret2;
  int secret1;
  
  strncpy(output,"mpknnphjngbhgzydttvkahppevhkmpwgdzxsykkokriepfnrdm",51);
  printf("Enter the secret password: ");
  scanf("%s", &input[51]);
  int len = strlen(output);
  for (int i = 0; i < 3; i++) {
    for (int j = 0; j < len; j++) {
      adder = (j % 0xff >> 1 & 0x55) + (j % 0xff & 0x55);
      adder = (adder >> 2 & 0x33U) + (adder & 0x33);
      int key = (adder >> 4) + (adder & 0xf);
      addened = (input[j] - 97) + key; //(convert alphabets to a index based position like 'a' -> 0, and adds the key )
      input[j] = addened + (addened / 26) * -26; //Basically iVar2 % 26, even for negative values
      input[j] = input[j] + 'a';	//Converts the 0–25 range back to alphabets
    }
  }

  printf("%s\n", input);
  int chk = memcmp(input,output,(long)(int)len);
  if (chk == 0) {
    printf("SUCCESS! Here is your flag: %s\n","picoCTF{sample_flag}");
  }
  else {
    puts("FAILED!");
  }
  return 0;
}

At first, I was completely lost — couldn’t find any static flag, just a 51-character long hash stored in output, and some obscure shifting logic applied on user input.

Finally I began to draw some logic after an hour of thinking, the binary takes an input, salts it with its own algorithm, then compares it with the hardcoded 51 charecter long hash. I noticed the key generation was  based on the iterator. So the “key” remains constant (and predictable, as the only value modifying arugment is the iterater variable). So if i negate the key from the addened, I would get another charecter. if I did this three times, I would get the final charecter which should be the password.

If I could subtract the key used at each position from the scrambled character, I could get back the original password — and since this mutation loop runs 3 times, I just had to reverse it 3 times.

So I wrote a Python script to reverse the entire transformation:

def get_shift_amount(iter):  //the "key"
    adder = ((iter >> 1) & 0x55) + (iter & 0x55)
    adder = ((adder >> 2) & 0x33) + (adder & 0x33)
    key = (adder >> 4) + (adder & 0xF)
    return key

def solver(letter, x):  //negating key operation
    key = get_shift_amount(x)
    dec = chr((((ord(letter) - 97) - key + 26) % 26) + 97)   #(97 = ordinal of 'a')
    return dec

def decode_password(text):  //the loop runner
    letters = list(text)
    for _ in range(3):
        for i in range(len(letters)):
            letters[i] = solver(letters[i], i)
    return ''.join(letters)

scrambled = "mpknnphjngbhgzydttvkahppevhkmpwgdzxsykkokriepfnrdm"
print(decode_password(scrambled))

Wellll….completing this challenge did take up more than 5 hours, but im happy I managed to solve this shit.

[~Ankit Mukherjee]