Welcome to part 2. This will go through the phases of the Linux Bomb binary provided by Open Security Training. Check out their training if you’re a beginner looking to develop your assembly skills. You can also grab the binary from their.
If you need a help setting up, check Part 1 of this blog which should walk you through everything you need.
Before I begin, I just want to state that I have experience in reverse engineering malware only and I’m not claiming to be an expert in the subject by any means. I have not dabbled in the CTF type challenge realm prior to this, so I may not be as honed as some who do these types of challenges often and may get some things wrong but please be kind. I just want to share my thought process in the hope of helping others. I am open to constructive feedback or questions in the comments.
You should have a text editor open with the output of objdump to use to cross-reference the code with your debugger and a seperate terminal with your debugger running, paused at the first instruction within main. Also, the addresses may be different to mine so I’ll try avoid referring to direct addresses throughout.
Lets get to it then….
So if you take a look at the disassembly you’ll notice a lot of functions within the code. The main one we’re initially concerned about is the <main> function. Theres a bunch of instructions and function calls for the initial setup, followed by a call to a function called <phase_1> which is immediately followed by a function called <phase_defused>.
This looks like a good starting point. The phase 1 function is passed one input which I’m guessing will be the first user input which will be compared to the actual input somehow within the function. So lets jump into the debugger and set a breakpoint here and step though it.
Switch over to gdb and set a breakpoint on phase 1 using the following command:
This will pause the execution at the first instruction within the function.
Now resume the execution with the c command and you will be prompted for your first input. The idea here is to give it a dummy input which is easily recognisable so you can follow it to see how the code interacts with it, hopefully leading you to the real input.
Once you hit enter, you’ll be taken to the breakpoint within phase_1.
This looks to be passing our dummy input into the <strings_not_equal> function along with one other argument. The output of this function is then tested and if the result is not zero, the bomb will go off. Lets take a look at the things being passed to this function then to see if it gives us any clues using the x/s command:
That looks like it could be what we’re looking for. We could step into the function and see how its going to use the two strings but as this is the first stage, I’m going to assume we’re going to be eased in gently and jump back to the start and give the string a go.
First, set a breakpoint on phase 2 using b *phase_2 and delete the previous by using the i b command to view your breakpoint and d <n> with the number of the phase 1 bp. Then use the r command to restart the execution.
Success! Sometimes, educated guesses do pay off and save you some time.
Its looking for the next input, so rince and repeat the previous method and you should be taken to the first instuction within phase 2.
The first obstacle within the function is the comparison of the output of <read_six_numbers>. That looks to be a custom function so stepping through it is the only option. Use the si command to step through the code and monitor the inputs as you go.
Looking at read_six_numbers in the disassembled code it looks like its using the sscanf function which is not a custom function. OS research into this shows that it is used to take input from a string and match the input to set of template arguments, returning the amount of successful matches.
sscanf (input_string, “%as %as %as”, &str_arg1, &str_arg2, &str_arg3);
Applying this theory to the actions prior to this call looks like edx may be the arguments which are setup with the series of lea instructions. It looks like our input string is moved to ecx from the stack and the third push must be the formatting for the inputs. (I’m not an expert on C so I could be wrong).
It looks to be expecting 6 numbers as input and as the output of this function is compared and only avoids disaster if its greater than 5 (and the name of the function obv) I’m pretty confident on this. If the comparison passes it returns to the parent function so this looks like it might be checking input before doing some other function. To get past this stage, set a breakpoint on the add esp,0x10 instruction directly after the call to read_six_numbers in phase 2 then restart the execution and give the second output as 6 numbers, seperated with a space, eg. 1 2 3 4 5 6
That was successful and the execution is now back to the phase 2 function.
Immediatly upon returning, the value residing at the memory address of [ebp – 0x18] is compared to 0x1. If you look at the stack you can see that the six numbers entered as input are stored in sequence.
ebp is currently sat at 0xffffce88 so subtracting 0x18 from ebp takes you to 0xffff70 which currently stores the first number you entered as input. Luckily for me I have used 1 as the first number so I will pass this test. If you didn’t you’ll need to go back and do it again. Either way, we have our first number which is 1. (Top tip: if you’re using Windows as your physical machine, calc’s Programmer mode is a convenient tool to use for working with hex conversions)
Directly after the successful jump, there is an instruction to mov ebx, 0x1. Looking further down the code, there is an inc ebx and then cmp ebx, 0x5. If the compare fails it loops back. To me this looks like ebx is i and its being used as a counter to loop through what looks to be a multiplication of some values (from imul). As this is looking for 5 loops (due to it starting at 1) and we have 5 numbers left to find, I’m guessing this is where we’ll find them.
I’m not going to explain the whole loop as it would be far too long but basically what you need to do is set a breakpoint on the compare instruction.
The loop is going to do calculations based on your first input (esi) and the counter (ebx) and place the answer in eax. It will then compare this to your input so the answers you need are in eax at the point of the compare. Loop through each iteration and grab the numbers you need, restarting and adding them to the input each time until you eventually defuse phase 2.
On a roll! Set a breakpoint on phase 3 and start the process again and you should come to the following.
This looks familiar! 3 lea’s, a cmp of the output to 2 and a jump if greater than. Pretty confident its looking for 3 inputs this time.
Looks like it wants 2 numbers and a character this time. Go back and give it an input in this format to continue.
The next hurdle is the cmp after the the jg. Its comparing the first number input and jumping if its greater than 7. The jump takes you to the bottom of the function where the bomb goes off so the first number is going to be less than or equal to 7.
Looking deeper into the function shows that its taking the first input string and multiplying it by 4 then adding it to a set value to generate an address.
The rest of the function is filled with a similar recuring structure of comparing one of the inputs and either blowing or jumping depending on the result.
There are 8 in total of 8 branches which can be taken. One like junk code in so ruling that out takes it to 7. The first input can be between 0-7 so that makes sense. This looks like a menu type structure (or possibly case) where it asks for an input and produces a certain output based on the input.
When I’m faced with a huge chunk of code that I don’t yet understand, I find it best to start from the end and work my way backwards. This is where having the disassembly in a text editor really helps as you can start formating the text to make it easier to process.
Looking at the end you can see that there is a final compare you must pass to to exit the function. The compare is comparing a value on the stack to bl.
Looking back you can see that you are forwarded to one of the 7 branches based upon your first input. Every branch forwards to the final compare if sucessful. The value of bl is set based on the branch you are sent to. The second comparison happens within this branch.
I first confirmed that the first number you enter decides which branch you take which is true. I then chose one branch to crack.
As you can see, there is a compare to 0xa0 which is 160 in decimal. So going off the 3 inputs of which 2 are numbers, I now have 4 and 160 as inputs. Using a hex to ascii convertor, I found that 0x6f (which is placed into bl and compared later) results in the letter “o”. With this in mind, I restarted and attempted to give this as the input after setting a breakpoint on phase 4 just in case.
Pretty rewarding challenge so far. I’m going to end this part here as it will be too long if I do the full write-up in one. You should be into the swing of things by now so give the rest of the phases a try and stay tuned for the last part to help you through or just for cross-reference.
Follow me on twitter to get updates on new posts and other related info abiut the field. https://twitter.com/mcb0x2Eexe
Thanks for reading.