Sunday, April 24, 2016

PCDC 2016 RE Challenge Solutions - Part 1

When we sit down and plan the next year's version of PCDC, the goal is to challenge competitors with new and specific learning objectives. This year, our goal was to push more reverse engineering and incident response type challenges for the competitors to complete as part of their injects. For this, I created 4 different reverse engineering challenges. The rules were simple: Use the tools provided to you or freely available for download to analyze the challenges and get the flag. Flags were in the format 'FLAG{}' with a custom string for each challenge in-between the '{}' characters. In order to get the points for the challenge, teams must submit the input to the challenge and the correct flag.

Since PCDC is run for high schools, colleges, and professionals, the challenges were created with increasing levels of difficulty. Additionally, the competition only runs for about 8 hours and these challenges are not the only tasks the Blue Teams must complete. With that in mind, here is Part 1 of the RE solutions.

Challenge 1 - strungout


The first challenge is called 'strungout'. Let's run it and see what it does.


Ok. Looks like we need to figure out the password. Let's just guess something and see what happens.



So password wasn't the password.

In an attempt to provide some insight to the challenges, each challenge had a name that hinted at its solution. This first challenge was meant to be a gentle introduction to reverse engineering. In order to reverse engineer any target, the goal is to gather as much information about that target as you can. The defacto go to is to extract some form of human readable text, aka, strings. As it happens, the Microsoft SysInternals suite has a tool called strings that does exactly what we want. If we run strings on the challenge, we can see the human readable strings embedded in the executable.



And if we scroll down...



We see the 2 strings we saw before, "Sorry. Can haz password??" and "Ouch. Try again!". Interestingly, we see a different string that looks suspicious! "I_Love_PCDC_RE_Challenges!!!". Let's see what happens if we try this for a password.



So the answer to the first RE challenge would have been:
Password: I_Love_PCDC_RE_Challenges!!!
Flag: FLAG{playing_with_strings_iz_easy!!!}


Challenge 2 - maths

This next challenge increases the difficulty a little bit. The goal was to introduce new students to debugging and x86 assembly. For the competition, we made sure that there were copies of Immunity Debugger installed on Blue Team computers already. Additionally, the free version of IDA Pro was installed. For this write-up I'm going to be using IDA to just show off the control flow graph of a program. This helps layout a program into a diagram that makes understanding how different parts of the program get executed. The remaining details will use Immunity debugger just because it is more easily available for people to use. 

So opening up maths in IDA will produce a diagram similar to this:

What we see here is it looks like a number is being read into the program, some math is happening on it, and if it's right, we fall a specific part of the program, otherwise we get the message "Knew you couldn't guess it!!". Let's see if this is true.


Ok. Let's open this program up in Immunity debugger and figure out what's going on.


So things look a little different in Immunity, but that's OK. What we want to do first is look at the executable modules of this program. This can be done by clicking the 'e' in the top bar of the previous picture or by pressing <Alt + e>. This should bring up the following screen:


From here, right click on the list and choose the option "Analyze all modules" to invoke some of Immunity's built in analysis tools. Once the analysis completes, double click on the line with maths to get taken to the code we want to look at. You should see a picture like the one below.


At first this doesn't look too interesting, but scroll down and we will see some details that should look familiar.


Now. Before we go any further, let's talk about what we are actually looking at. The large window with the multicolored text is showing us the output of a program called a disassembler. What a disassembler does is take raw bytes in a program and print them out as an easier to understand and analyze form called assembly. Modern x86 assembly is incredibly complicated and is comprised of over 1,000 instructions! For these challenges, though, we won't need to know anywhere near that many. The understand this assembly, the left most column is the address in the program that the assembly sits at, the next column shows the raw bytes of the assembly, the third column shows the human readable mnemonics of the assembly, and for some lines, there is a 4th column generated by Immunity that shows some comment that may provide you with more information. The window to the right of the disassembly shows the register values of the CPU while you're debugging the program. This is helpful to keep track of values as they move from the CPU to memory and back again. Below the disassembly and register windows are the listing for the program's .data section and stack. We don't need to go into too much detail about each of them, but the .data section is on the left and it shows data built into the program such as the strings. The bottom right window is the stack that is used by the program for various memory operations.

OK. We can see the messages that we saw in IDA and our tests before in our disassembly window. Let's review what we know so far:
  1. The program wants us to guess its secret number
  2. The name of the program is called maths
  3. If we get it wrong, the program will print out "Knew you couldn't guess it!!"
Now let's look at the assembly. We know that we need to guess a magic number. In x86 assembly, values are compared with the CMP instruction. If we look at the assembly at address 0x00401365, we see the instruction CMP DWORD PTR SS:[EBP-4],DEADBEEF. Now, this looks a little scary, but the CMP instruction performs comparison checks. This is just comparing some variable with the hexadecimal value 0xDEADBEEF. Since we see the string "Knew you couldn't guess it!!" right below the CMP at address 0x00401375, this seems like it may be be a good candidate for our password. Let's try!


Hmmm...didn't work. Remember. The name of the program is actually a clue how to solve it. If we look up a couple of instruction from the CMP, we can see some instructions that sound like math operations operating on the same variable that's used in the CMP DWORD PTR SS:[EBP-4],DEADBEEF instruction. Those math instructions are SUB, ADD, and IMUL. Working backwards from the CMP instruction, we can see that there is math being performed on the variable before it gets compared to 0xDEADBEEF. Here is a summary of what those instructions are:
  1. Read 'magic number' from user
  2. Multiply (IMUL) 0x0a to the 'magic' number at 0x00401345
  3. Add (ADD) 0x0a to the 'magic number' at 0x0040134E
  4. Subtract (SUB) 0x01 from the 'magic number' at 0x0040135F
So if we do the opposite of this, we should be able to get our original input:

(((0xdeadbeef + 0x01) - 0x0a) / 0x0a) = 0x16449317

Let's try that!


Still not right! What did we miss! Well, we did miss something. There is another instruction that operates on the variable where our input is stored. This instruction is SHL at 0x00401357. This instruction does a binary shift left which effectively multiplies the number by 2 for the size of the shift. In this case, there is a shift left of 1, so the value is doubled. So to get our answer, we need another divide by 2. Here is the new equation:

((((0xdeadbeef + 0x01) / 2) - 0x0a) / 0x0a) = 0xB22498B

Ok. Let's try this one.


Again it didn't work! Why not?! Well, the answer is found by looking at how the number is actually read in. Look at 0x00401334, we can see in the comments section ASCII "%d" and the next line has a CALL to MSVCR90.scanf_s. This means that the routine reading in the user input is looking for the input to be in decimal form. So converting 0xB22498B to decimal is 186796427. Finally, let's try this.


There we go! So the answer to this one is:
Password: 186796427
Flag: FLAG{deadbeef_is_best_beef!!!}


Alternatives:
Now, this may seem a little complicated for people just entering the RE world. It would be much easier if we had the original source code for this program to actually figure out what's going on. Unfortunately, the process from going a binary to source code is incredibly difficult. A decompiler will never be able to give you the original source. Too much information is lost. There are decompilers out there that can give you something resembling the source, however. Let's look at a few.

RetDec
Found here, RetDec is an online trial of a decompiler. Here is the function we are interested in once decompiled by RetDec:

// Address range: 0x401310 - 0x40138a
int32_t function_401310(char * a1) {
int32_t v1;
g2 = &v1;
printf("I bet you can't guess my secret number...\n\n");
printf("Enter your guess --> ");
scanf_s();
if (20 * g3 == -0x21524124) {
// 0x40136e
function_401240();
// branch -> 0x401383
} else {
// 0x401375
printf("\nKnew you couldn't guess it!!\n");
// branch -> 0x401383
}
// 0x401383
exit(1);
// UNREACHABLE
}

IDA Pro - Hexrays
IDA Pro also has a decompiler plugin, but it is very expensive. Here is some of its output:

//----- (00401310) -------------------------------------------------------
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // ecx@0
  int v4; // [sp+0h] [bp-4h]@1

  v4 = v3;
  printf(aIBetYouCanTGue);
  printf(aEnterYourGuess);
  scanf_s(aD, &v4);
  v4 = 2 * (10 * v4 + 10) - 1;
  if ( v4 == -559038737 )
    sub_401240();
  else
    printf(aKnewYouCouldnT);
  exit(1);
}

Snowman
Another free decompiler is Snowman. It is integrated into the also free debugger, x64dbg. Here is the function decompiled by Snowman:


int32_t g4020a4 = 0x73cd20c1;
int32_t g402098 = 0x73cd2719;
void fun_401240();

int32_t g40209c = 0x73cc2455;

void fun_401311(int32_t ecx, int32_t a2) {
     g4020a4("I bet you can't guess my secret number...\n\n");
     g4020a4("Enter your guess --> ");
     g402098("%d", reinterpret_cast<int32_t>(__zero_stack_offset()) - 4);
     if ((ecx * 10 + 10 << 1) - 1 != 0xdeadbeef) {
          g4020a4("\nKnew you couldn't guess it!!\n");
     } else {
          fun_401240();
    }
    g40209c(1);
    goto a2;
}

As you can see, the outputs from these decompilers are pretty different and this is just for a relatively simple function. None of which, look like the original code:

// Main function
int main(int argc, char *argv[]) {
// Integer to store user input
unsigned int user_input;
// Print info and read input
printf("I bet you can't guess my secret number...\n\n");
printf("Enter your guess --> ");
scanf_s("%d", &user_input);

// Obfuscate, answer == 186796427
user_input *= 10;
user_input += 10;
user_input *= 2;
user_input -= 1;
// Check for valid input
if (user_input == 0xdeadbeef) {
get_flag();
}
else {
printf("\nKnew you couldn't guess it!!\n");
}

// Exit
exit(1);
}

But with these different outputs, it was easier to see the mathematical comparison and check for the magic number. One thing you'll notice is that each decompiler treated the final number, 0xDEADBEEF, differently. This is because type information is lost at this lower level and each decompiler uses different algorithms and heuristics to recover this information. The numbers produced by the decompilers are all technically correct, you just have to interpret it in the appropriate way.

Conclusion

These challenges were meant to get competitors thinking about different areas of cyber security. Hopefully we were successful. In my next post, I'll detail the answers for the remaining 2 RE challenges, exclusive and hashbrowns. If you have any questions or want more clarity on how to solve either of these two challenges, please don't hesitate to ask!

No comments:

Post a Comment