CSAPP bomblab
This is the writeup for CSAPP bomblab
Tool: IDA/Ghidra, pwndbg
Phase 1
Phase 1 compare input with a string originally inside the program.
1 | Border relations with Canada have never been better. |
So input the same string can defuse the bomb
Phase 2
Phase 2 read in 6 numbers in sequence. There is a while loop checking that the number in the back should be twice the one in the front.
1 | 1 2 4 8 16 32 |
Phase 3
Phase 3 read in 2 numbers. The first number used as a variable in a switch statement of total 8 choices. From the disassembler, we can know different value that will be compared with our second number. So find the right case you want to choose and input them as pairs
1 | 0 207 |
Phase 4
Phase 4 also read in 2 numbers. The first number should be less than or equal to 0xE = 14
. There is a function called func4
that is a recursive function, the input number should make its return value equal to 0. After some test, input 0
can return 0
, so just simply solved it.
Then the program just simply compare the second value with 0. If it is, you will pass the test, otherwise the bomb will explode.
1 | 0 0 |
Phase 5
Phase 6 read in string with length 6, encrypt/decrypt it in some way and compare the result of the encryption/decryption with flyers
.
It take an AND
operation to the input string byte, which result only the half of the byte. Ex. f = 0x66; 0x66 AND 0x0f = 0x06
. The program use the last half byte as the index to get the characters in the array. If the output of those character become flyers
, you defuse the bomb.
The encryption/decryption array:
1 | unsigned char array_3449[] = |
1 | ionefg |
Phase 6
Phase 6 read in 6 numbers. First, there are two nested loop to make sure every input number is less or equal to 6, and there are no number that next to each other are equal. Ex:
1 | 1 3 4 6 9 2 (X) because 9 > 6 |
Then there is a second loop use 7 minus each input number and store the value in the same position as the original input.
The third loop initialize the “node” for the next loop. There are 6 nodes in total (also 6 input).
The fourth loop set up the pointer for each “node” by the sequence of the input. Similar to an object, the “node” here have 8 byte to store their own value and another 8 byte point to another node.
The last loop examine the “node chain” to make sure it is in decreasing or same order.
After debugging, the pointing direction should be:
1 | node3 -> node4 -> node5 -> node6 -> node1 -> node2 |
The solution should be (remember, the second loop reverse the inputs if we choose not to have repeated number):
1 | 4 3 2 1 6 5 |
Secret Phase
If we take a specific look at the phase_defused
function, we can see that if the num_input_strings
, which counting the number of inputs, equal to 6, another branch will open up.
After dynamic analysis, the new branch in the phase_defused
function redo the sscanf
function on the input of phase 4:
1 | sscanf(PHASE_4_str, "%d %d %s", rdx, rcx, r8); |
Then compare the contents of the last string with DrEvil
. If equal, the checks passed, successfully into the secrete phase. But as it said: But finding it and solving it are quite different...
The secret phase read in string and convert it to long int. The value after convert should less than 1000. Then call the fun7
, another recursive function, with the parameter of char *a1, input_val
. a1
is an array in the program .data
section, which we are able to access by disassembler. The return value of fun7
should equal to 2, then we defuse the secret phase.
There are two recursive branches for fun7
. The whole fun7
looks like this:
1 | // error case |
So in order to make the the return value to be 2, we may go in the first branch for first call, go in the second branch for second call, and terminate the recursion for third call.
After examine the array, we can easily find the solution for the secret phase follow the procedure above.
1 | 2 |