Flash CTF – Spider’s Curse

Overview

This Reverse Engineering challenge gives us an executable program, a Linux ELF binary. Let’s try running it and giving it some input.

$ chmod +x tomb
$ ./tomb
As you step into the ancient, cobweb-laden chamber, a chill runs down your spine.  
The air is thick with the whispers of those who came before, trapped in the web of the spider's curse.  
A dusty tome lies open on a pedestal, its pages filled with cryptic symbols and warnings.  
  
To break the curse and escape the web, you must speak the secret word, hidden in the language of the ancients.  
Only then will the spider's grip loosen, and freedom be yours.  
  
Gather your courage, adventurer, and prepare to face the challenge.  
The fate of your freedom rests upon your words.  

Say the magic word to reverse the spider's curse: 12345  
The web tightens around you. The curse remains.  
Say the magic word to reverse the spider's curse: abcde  
The web tightens around you. The curse remains.

Following some delightful flavor text, it looks like the challenge is pretty simple – we have to enter a particular magic word (which is probably, but not necessarily, the flag) to get a successful result and “escape” the spider’s curse.

As always, let’s start with strings; this is a program that will show us all of the printable ASCII strings present within this binary file. Run strings tomb.

The output’s not too long. We see a lot of the standard boilerplate – function names, glibc identifiers, ELF section names. Near the top, we’ll recognize the intro text, the “Say the magic word” prompt, some format strings, and what looks like the winning result (The curse is lifted! You are free from the web.). The experienced might also spot a hexadecimal string:

4d6574614354467b68337833645f3572316e67735f3472655f6e305f6d347463685f6630725f6d337d

… and may immediately recognize that it’s out of place, decode it using a tool like Cyberchef, and realize that it’s in flag format, and is indeed the “magic word” as well as the flag for this challenge (MetaCTF{h3x3d_5r1ngs_4re_n0_m4tch_f0r_m3}).

We’ll also demonstrate how one could figure out the answer without intuition (say, if you didn’t spot the hexadecimal string or thought it was a part of the ELF “background noise”).

Ghidra

Let’s pop this binary into Ghidra, a popular decompilation tool. Any tool would probably do, though – IDA free, Binary Ninja, etc.

If we open up Functions in the symbol tree, go to main, and look at the decompilation, we’ll see:

undefined8 main(void)

{
  int iVar1;
  long in_FS_OFFSET;
  undefined local_318 [256];
  char local_218 [520];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  puts(
      "As you step into the ancient, cobweb-laden chamber, a chill runs down your spine.\nThe air is  thick with the whispers of those who came before, trapped in the web of the spider\'s curse.\ nA dusty tome lies open on a pedestal, its pages filled with cryptic symbols and warnings.\n\n To break the curse and escape the web, you must speak the secret word, hidden in the language of the ancients.\nOnly then will the spider\'s grip loosen, and freedom be yours.\n\nGather yo ur courage, adventurer, and prepare to face the challenge.\nThe fate of your freedom rests upo n your words.\n"
      );
  while( true ) {
    printf("Say the magic word to reverse the spider\'s curse: ");
    __isoc99_scanf("%255s",local_318);
    stringToHex(local_318,local_218);
    iVar1 = strcmp(local_218,
                   "4d6574614354467b68337833645f3572316e67735f3472655f6e305f6d347463685f6630725f6d33 7d"
                  );
    if (iVar1 == 0) break;
    puts("The web tightens around you. The curse remains.");
  }
  puts("The curse is lifted! You are free from the web.");
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

This is already pretty straightforward thanks to some function names (stringToHex) not having been stripped from the binary, but to make things clearer, I’ll rename some variables in Ghidra and then crop it to just the main loop.

while( true ) {
  printf("Say the magic word to reverse the spider\'s curse: ");
  __isoc99_scanf("%255s",users_input);
  stringToHex(users_input,hex_buffer);
  strcmp_result =
       strcmp(hex_buffer,
              "4d6574614354467b68337833645f3572316e67735f3472655f6e305f6d347463685f6630725f6d337d"
             );
  if (strcmp_result == 0) break;
  puts("The web tightens around you. The curse remains.");
}
puts("The curse is lifted! You are free from the web.");

Now the flow should be fairly apparent: The program gets our input via scanf, runs a function that (at least according to its name, and we’ll trust it) converts our input to hexadecimal, and then compares it against a hexadecimal string constant, the one we saw in strings earlier. If it matches, we win and break the spider’s curse.

Just like with strings, pulling that hex string out of Ghidra and decoding it yields MetaCTF{h3x3d_5r1ngs_4re_n0_m4tch_f0r_m3}.