In part 13, we analyzed and adapted the RESOLVER for 64 bits. In this part, we’ll discuss how to analyze the difficulty of creating a rop depending on the scenario.
As we have seen in the previous tutorials, the difficulty of creating a rop can vary depending on the circumstances and characteristics of the vulnerability. The four questions we have previously introduced also helps us discover the level of difficulty.
To review those four questions, let’s go over the example from the previous part:
1) Do you have modules that do not have ASLR? - YES
2) Do you have any module with VirtualAlloc or VirtualProtect function imported that does not have ASLR? -YES
3) Has the data already been placed on the stack to start ROPING? -YES
4) Can you pass any character (i.e., no invalid characters)? NO, BUT EFFECT IS MINOR
We can put a score from 0 to 4: initially adding one for each NO.
Of course, a level 0 ROP would be easier at first glance and a level 4 may be very difficult or impossible depending on the case. However, further investigation may be needed to definitively determine the difficulty level.
For example, in the previous part we found that using this scale, the rop would be level 1. But in practice the invalid characters affected so little. So in the case, after completing the rop, we would likely alter the “NO” from question four from a score of 1 to one of only 0.5, which would ultimately give us a difficulty score of 0.5, according to the “ricnar scale.”
What follows are two 32-bit and 64-bit examples. These are supposedly a little more difficult than the previous ones we have completed. Before starting to build the ROP, let’s assess the potential difficulty.
The exercises are available here:
Analyzing The Difficulty Of The Second 32 Bit Exercise
Let's start by answering the questions:
1) Does the program have modules that do not have ASLR?
To answer this, we’ll run it and when the MessageBoxA comes out, we’ll look at Process Explorer to see if there are modules without ASLR.
Let’s click and drag to the MessageBoxA window, where it will show us which process it corresponds to.
It has DEP enabled. So, in order to solve it, we will need to build a ROP and it has a module without ASLR (the same executable). This means the answer to the first question is YES, so we do not add anything. (Remember, we we only add one for each NO.)
PARTIAL SCORE = 0
2) Do you have VirtualAlloc or VirtualProtect function imported in any module that does not have ASLR?
First, let’s use RADARE to open the 32-bit example.
We’ll open a cmd in the folder where the executable is located and type in:
Then we’ll load the symbols with the idp command.
Next, we’ll analyze the program, using the aaa command.
We can list all the functions using the afl command.
If we want to filter the results, radare has a built-in grep. We just need to include the ~ symbol.
Let's go to main with the s pdb._main command.
We’ll create a project with the Ps ROP32 command.
This is more visible with echo bright.
We can see the call to VirtualAlloc that we had in the previous part is not there. Since this function was imported, let's make a list of the imported functions using the li command and filtering with ~ to see if we have them.
Since it’s not imported, the answer is No, so we’ll add to our score.
PARTIAL SCORE = 1
3) Has the data already been placed on the stack to start ROPING?
To see that, we must analyze the vulnerability of the main function. We can’t see anything, so perhaps it is inside the function called f.
We could see the function f using s pdb._f. However, it’s better to use the visual mode. We’ll enter it using v, pressing c to control the cursor. Using the direction arrows, we can go down to the CALL to the function f and press ENTER.
In visual mode, we can press c to remove the cursor. Pressing the space bar, we can see the blocks. We can then press the space bar again to return.
We can see three variables: there is considerable space between the upper variable and the middle variable. Consequently, the upper variable should be a buffer on the stack.
In respect to ebp, it is located at -0x408, and the next variable is -0x8. As there are 0x400 bytes difference between them, this is the length of the buffer.
We can also press the key “:” in visual mode and rename it using the command afvn buffer var_408h.
We then can use the v again to refresh it.
As we saw above, pressing the c key gives us the ability to control the cursor using the direction arrows. We can exit by pressing c once more.
Using the arrows, we can navigate to the line that we want to comment on and press the key “;” to add the comment. To submit the comment, we’ll just hit ENTER.
In visual mode, there is no need to refresh for the changes to go through.
We can see that it saves the pointer to buffer in var_8h, which we’ll rename pbuffer.
We’ll press the key “:” and the command afvn pbuffer var_8h. We’ll then refresh using v.
We see that there is a loop. Each time, it reads a character with the function getchar(), then saves it in the variable var_1h, which is a single byte variable. It then compares them first with 0x40 and then with 0x10. If it is equal to either of them, it will exit the loop.
If it is not one of those values, it saves it in the buffer since it reads ECX to pbuffer and then saves it in its content, that is, in the buffer, which increases pbuffer. From there it repeats the loop once more.
Since there are no protection cookies, we can fill the buffer and step on the return address, until the program finds a 0x40 or 0x10 byte.
This means that the answer to the question is YES since our data will be on the stack and we will be able to ROP directly from there. No additional points are added.
PARTIAL SCORE = 1
4) Can I pass any character? (i.e., no invalid characters)
As we found earlier, we can only pass characters that are 0x40 or 0x10 if we want to finish copying. Therefore, the data copied to the stack that formed the ROP cannot have 0x40 or 0x10, the same as the SHELLCODE. This means the answer is NO.
We will see if it affects it significantly later on. But for now, we add one to the score.
FINAL SCORE = 2
This partial analysis tells us that this ROP with score of 2 could be more complicated than the example we went through earlier, which had a score of 0.5.
We’ll rename the variable to temp_char, correct the type using afvn temp_char var_1h, and hit v to refresh.
To change the type of char we will use the command afvt temp_char char and refresh with v.
We will save the project with ps ROP32 to continue into the next part so you can try to solve it. Don't forget that a failure teaches us more than something solved easily.
Tip: Be careful when copying to the stack with pbuffer, which is below the buffer. It may crash the program and not allow you to get to the return address.
Join us for part 15, where we’ll continue with more complex ROPs.
Explore the Rest of the Reversing & Exploiting Series
Head to the main series page so you can check out past and future installments of the Reversing & Exploiting Using Free Tools.