Bomb 💣

Table of Contents

1. Bomb

The purpose of this exercise is to defuse the bomb, solving 6 (plus a secret one) puzzles that can be solved only using static analysis. In this page I’ll try to state how I solved each phase, of course, as always, this is not an official solution, nor the best one; I’m open for any correction or consideration on what I’m going to write (or You’re going to read).

One last thing: BE AWARE, SPOILERS

2. Initial steps

2.1. Strings

Before even opening IDA, I used PE Studio to extract some preliminary information that I will use later. The first thing that I checked was the strings section:

bomb-strings-1.png

bomb-strings-2.png

There are some interesting strings in here: some format strings maybe used for formatting input or output, the indication of a secret stage and some cryptic strings that sound a little bit strange like: isrveawhobpnutfggiants and Public speaking is very easy.. Also there are a couple of error messages regarding EOF and an too long input line.

2.2. The bomb has been planted.

I executed the binary to see how the program looked like. It is a simple CLI application that present itself with a nice text and then wait for an input.

bomb-execution-1.png

Let’s try to trigger the “input too long” error:

bomb-execution-2.png

After those trivial attempts let’s fire up IDA.

3. Prologue: input and company

When opening the executable with IDA, I see the main function that performs some initial steps before even start the real logic of the application itself. In fact the executable checks for the number of command line arguments: if the number of arguments is one the it jumps to the core of the application, else if the number of command line arguments is two it will treat the second as a file name and tries to open it in reading mode to read its content.

bomb-ida-1.png

At the end of the possible file reading part, the code jumps to the core part of the execution: the six (plus one) stages.

bomb-ida-2.png

Here I recognized some of the strings that I saw in the strings section of PE Studio, but not all of them. The first interesting function call is the one to sub_401870; at first sight seems that this function is handling the user input, this hypothesis is enforced by the presence of some error messagges like: “Erorr: premature EOF on stdin” and “Error: input line too long”, with respect to the second we can see that the error messagge is printed at this point of the code:

bomb-ida-3.png

Now I know that the input has to me at most 79 characters. Going back to the callee function I rename sub_401870 as input_handle, noticing how it is called at the beginning of each phase. The output of this function is assigned to the Buffer variable.

4. First Act: I’m about to blow up.

The second function call is to sub_401190; that takes one argument, the above mentioned Buffer. The function itself is quite simple, there is a string literal “Public speaking is very easy.” another function call (to sub_401740) and a test on the output of the function. If the value returned by the function is different from zero another function (sub_401960) is called. I inspected this last function first.

4.1. Boom.

sub_401960 is easy to understand, this is the function responsible for the exoplion of the bomb. Nothing more, nothing less. Let’s call this function BOOM.

4.2. Homemade bread strcmp and strlen

The first function call I saw in the first stage was to sub_401740. This sub is a little bit more complex that the BOOM one, but we can easily see that:

  • It takes two arguments
  • The same function is called twice at the beginning and the return values are compared
  • There is a loop
  • and just one path lead to a zero output, all the other return 1.

Before entering into the details of what the function that is called at the beginning does I considered the code that is executed is the compare is successful. First of all two local variables are initialized with the content of the two arguments provided here:

bomb-ida-4.png

Then the loop starts, at each iteration the loop takes the content of the first local variable and checks if its zero (string termination pheraps?) if not it checks if the content of the memory pointed by the two local variables is equal, increase by one the content of the local variables (iterates over a string?) and the loop restarts.

bomb-ida-5.png

Coming back to the two calls to the same function let’s inspect sub_401700. This sub is quite simple:

  • Takes one argument
  • Initializes a local variable with 0, let’s call it idx
  • Initializes a local variable with the content of the argument, let’s call it input
  • deferences the content of input
  • checks if the content is 0, and if not
    • increase the value of idx by one, and also increase the content of input by one (iterates over a string?).
  • the sub returns idx.

bomb-ida-6.png

I’m quite confindent that this functions returns the number of ASCII characters in a string, so I called it homemade_strlen. Now everything seems more clear, first the function calls homemade_strlen on both its argument and compare the respective return values, then the sub compares the two arguments byte by byte and checks if they are equal, if just one byte mismatch or the two argumets have different lenght it returns 1: we have a freshly baked homemade_strcmp. Back to phase one.

4.3. Phase One solution.

With just a couple of renames and everything seem more clear.

bomb-ida-7.png

The phase one sub just compares the user input with the string literal “Public speaking is very easy.”. One phase’s defused, five more left.

bomb-ida-8.png

5. Second Act: Count with me.

I’ll ignore for now all the calls to sub_401990, and dive in phase 2. The second phase starts with the call to sub_4011B0. This sub takes the Buffer that contains our input and declares some local variables. Then a sub_4016B0 is called.

  • Var_1C is used to contain the six integers given in input
  • Var_4 is used as an index
  • Var_20, is not initialized anywhere, but is used inside the loop to index the input array.

5.1. Input handling

sub_4016B0 is responsible of the formatting of the content of the Buffer viariable:

bomb-ida-9.png

and fill an array of six integers

5.2. Inside the Loop

After the input handling phase, the sub checks if the first input is 1, and then it proceeds with a loop. The loop starts from 1 until 6, and inside its body a condition is checked, if the conditionis not verified th ebomb explodes.

It is clear that it checks that the content of the usrinput array respects some kind of property, but there is one dubt still left: what does Var_20 do? Var_20 is recognized as a local variable by IDA, but in reality it was put there by the compiler as an optimization trick. In fact, at each iteration the user_input array is acessed twice:

  1. at the current position pointed by the idx variable, and
  2. at offset Var_20, that is -20h (-32 in decimal). This means that we are accessing the array at position idx - 1.

bomb-ida-10.png

5.3. Phase Two solution.

We have to contstruct the array with respect to that condition, given that the first number should be 1.

bomb-ida-11.png

bomb-ida-12.png

6. Third Act: Sultans of Switch.

Phase three begins with the call to sub_401210, from the beginning we see that an integer, a character and another integer as I see here:

bomb-ida-13.png

Also the sub performs a check on the return value of the sscanf: it has to be greater or equal than 3 (why greater or equal and not just equal?). The sub stores the input values inside three local variables:

  • Var_C contains the first integer
  • Var_D contains the character
  • Var_8 contains the second integer

The fourth local variable Var_1 is used later in the sub.

After the check on the return value of sscanf there is a switch statement performed on the first integer. Following the jumptable, with the help of IDA is simple to see that there are 7 cases plus a default one, and that for case 3 Var_1 is initialized with character k and Var_8 is compared with 251. After the switch statement the content of Var_D is compared with the content of Var_1, if the two variables are equal the phase is solved.

6.1. Phase Three solution.

This phase was quite simple: 3 k 251 can be used as an input to solve it.

bomb-ida-14.png

7. Fourth Act: fiftyfive rabbits.

The fourth phase starts in sub_401390, the functions parses Buffer to obtain one integer, and as always, checks that the valure returned by sscanf is equal to one; then it checks if the input is a non zero positive integer, and then calls sub_401350 with the input as argument.

7.1. Down the rabbit hole

sub_401350 starts with a compare between the argument, and one, if the argument is less or equal than one it just returns one, else if the argument is greater than one the function:

  • subtracts one from the argument
  • pass the new value to another call of itself, stores the return value in register esi, then
  • subtracts two from the argument and pass the new value to another call of itself, the return value is in eax, finally
  • adds the two obtained values and then returns the just computed sum.

Yes, this sub computes the fibonacci from the second element up two an nth element given in input.

7.2. Phase Four solution.

The return value of sub_401350 is compared with 37h, 55 in decimal. So our solution is the position of the number 55 in the fibonacci sequence, starting from the second element of the sequence itself.

bomb-ida-16.png

8. Fifth Act: Encoding on a Star.

sub_4013E0 is responsible for the phase five. the first thing I noted was a call to the afore mentioned homemadestrlen, the first check regards the lenght of the input: it must be a string of six characters. Also the function define four local variables:

  • an index: Var_10
  • an array: Var_C
  • Var_6 used as an offset to index the last position of the array in Var_C (compiler optimization)
  • and an integer that contains the return value of homemade_strlen

The sub checks if the lenght of the input is six, and then starts a loop that iterates over the input. At each iteration of the loop the code index a memory location that contains an array of characters, using the ASCII value of our input to access it.

bomb-ida-17.png

Then the constructed string is, after the loop, compared with the string giants via the handmandestrcmp. If they are equal the phase is cleared.

The memory is organized as so:

bomb-ida-18.png

I changed its view via the Edit menu of IDA to see it like that:

bomb-ida-19.png

So we need in order indexes: 15, 0, 5, 11, 13, 1.

8.1. Building a giant.

My goal is to find a string such that the result of bitwise AND between the ASCII value of the characters with 15 will produce those indexes in order. But the bitwise AND does nothing more than extracting the last byte and obtaining a number between 0 and 15.

8.2. Phase Five solution.

There are multiple solutions for this phase, I used ?05;=1 as passphrase to solve this phase, but other strings can be used.

bomb-ida-20.png

9. Sixth Act: Order matters.

The defeat the last phase I have to understand what the sub_401460 does. The sub declares a lot of local variables, for now not all of them seems to have a clear purpose.

  • Var_3C is initialized with a pointer to a memory location.
  • Var_8 that is used as an index
  • Var_20 that contains the user input
  • Var_40 that is used as a second index

9.1. Input handling

The first thing that the function does is to call sub_4016B0, this function takes the Buffer that contains the user input and uses sscanf to fill Var_20, then checks if the number of integers is greater than six.

9.2. Checking those numbers

After the input handling phase, a loop begins.

bomb-ida-21.png

The body of this loop looks a little bit intimidating, let’s break it in small parts. Firs of all the loop it’s iterating over the user input, in the first conditional statement checks if the element in the position currently indexed is greater or equal than one, the second conditional statement checks if the element currently indexed is les or equal than six:

bomb-ida-22.png

So all the input numbers must be integers between 1 and 6. After this preliminary check, an internal loop is started, this time the loop starts from the current index plus one and iterates until the end of the array. At each iteration this inner loop checks that the number pointed by the outer loop’s index is different for all the following numbers. In summary the inner loops checks that all the number in the array are different.

bomb-ida-23.png

So the first loop of the sixth phase checks that all the six numbers taken in input are integers between one and six and that are all different.

9.3. Cracking the code

After the first loop, a second one begins, let’s uderstand what it does. In this loop a second array is filled with some pointers, the order of the elements of the array is given by the numbers inside the user input array. At each iteration of the loop:

  • Var_4 is initialized with the value contained in Var_3C,
  • idx2 is initialized with one, and
  • if the value of idx2 is greater then the element currently indexed by idx in the user input array then:
    • the value of Var_4 is inserted in position idx of a newly constructed array.
  • else:
    • it adds 8 (moves up a byte) to Var_4 and increase idx2

So at the end of this first loop we have contructed this array. Let’s inspect the .data section:

bomb-ida-24.png

bomb-ida-25.png

Now I know what the addresses point to, going back to the code, I saw that, and the end of the sub_401460

bomb-ida-28.png

the code checks if the value pointed by the element currenlty considered in the previusly constructed array is greater than the value pointed by the next pointer in the array. Using this information and the values I saw inspecting the .data section It’s possible to deduce that the correct order is:

bomb-ida-27.png

9.4. Phase Six solution

bomb-ida-29.png

9.5. What I left behind

When solving the last phase I didn’t analyzed what the second loop considered in Cracking the code part did.

10. Epilogue: Secrets are not for everyone.

Author: Andrea Ercolino

Created: 2022-12-12 lun 12:09