Baby Crypt
2 minutes to read
We have a binary called baby_crypt:
$ file baby_crypt
baby_crypt: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=24af7e68eab982022ea63c1828813c3bfa671b51, for GNU/Linux 3.2.0, not stripped
If we open it in Ghidra, we will see this main function:
int main() {
  char *key;
  long in_FS_OFFSET;
  int i;
  undefined8 local_38;
  undefined8 local_30;
  undefined8 local_28;
  undefined2 local_20;
  long canary;
  
  canary = *(long *) (in_FS_OFFSET + 0x28);
  printf("Give me the key and I\'ll give you the flag: ");
  key = (char *) malloc(4);
  fgets(key, 4, stdin);
  local_38 = 0x6f0547480c35643f;
  local_30 = 0x28130304026f0446;
  local_28 = 0x5000f4358280e52;
  local_20 = 0x4d56;
  for (i = 0; i < 0x1a; i = i + 1) {
    *(byte *) ((long) &local_38 + (long) i) = *(byte *) ((long) &local_38 + (long) i) ^ key[i % 3];
  }
  printf("%.26s\n", &local_38);
  if (canary != *(long *) (in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}
Basically, it asks for a 3-byte key and then performs a XOR cipher. Since the expected output is the flag and we know the format (HTB{...}), we can reverse the XOR cipher and get the expected key.
Let 
Hence, we can take the first three bytes of the ciphertext (variable called local_38, in little-endian format) and XOR them with the plaintext bytes:
$ python3 -q
>>> chr(ord('H') ^ 0x3f)
'w'
>>> chr(ord('T') ^ 0x64)
'0'
>>> chr(ord('B') ^ 0x35)
'w'
And we found the key (w0w). Let’s get the flag:
$ ./baby_crypt
Give me the key and I'll give you the flag: w0w
HTB{x0r_1s_us3d_by_h4x0r!}