In part three, we learned how to analyze the first two exercises (stacks), using the three interactive disassemblers, IDA FREE, RADARE, and GHIDRA.
In this next part, we will continue our analysis with stack three and stack four. However, before that we need to introduce the new concept of invalid or bad chars.
What is a Bad Char?
Bad chars, also called invalid characters, are characters received and filtered by the target program that serve as delimiters. Through the internal algorithms of the program, bad characters eliminate functional characters or replaces them with other values, making our shellcode useless.
The search for badchars is a crucial part in the methodology of developing exploits, since, if they are not located and avoided during the generation of the payload, they make it useless because it would not be interpreted correctly in the target system.
When we send data to a program, it processes it. For the duration of this operation, until it arrives at the point of exploitation, the program checks, filters, substitutes, or blocks certain characters. This prevents us from using them in our payload. Obviously, this complicates the exploitation as it will need to be determined quickly at the moment of creating the exploit.
There are many ways to check if in the exploitation of a program, it produces invalid characters. For example, you could analyze the program from where data is entered to the point of exploitation and see what it does with this data. This method is the most accurate but can be extremely tedious if the program performs multiple checks, copies, and manipulations of the entered data.
Another option is to pass a string containing all the possible characters and check what happens to it. After several attempts, we’ll generate a string that passes completely and is fully copied to the destination buffer by removing the invalid characters.
Normally, in a first attempt, we avoid passing the most common invalid characters: 0xA, 0xD, 0x0 and 0xFF. If the rest pass well and arrive at the zone of exploitation without problems, then these four can be added one by one to see if there are any problems with them.
In the case of a buffer overflow, we could pass the string of test characters and see if it is copied to the buffer and if all the ones sent.
The following section demonstrates a sample code in python 3 that sets up a payload to test the invalid characters.
BASIC SCRIPT TO TEST BAD CHARS
We can see that certain invalid characters are excluded. We can add or remove more depending on the tests we are doing.
There is a modification to the stack 1 script to test the invalid characters. Those that we find will be valid for the exercises since they all process data in the same way. They enter by means of the function gets, which then copies them to the destination buffer buf.
ADAPTING BASIC SCRIPT TO TEST BAD CHARS IN STACK 1
We’ll execute this script by attaching it to x64dbg when the message box pops up and going until the gets() function. After this execution, we’ll check if it copies all the characters to buf, which is the destination buffer that is going to be overflowed.
If we put a breakpoint in the gets()and accept the message box, it will stop there.
\ EDX contains the address of buf. Select the EDX register and choose FOLLOW IN DUMP to see in the dump the area where it will copy the bytes that we will send.
Press f8 to execute the gets().
The string is cut at 0x1a character, so let's add it to the list of invalid characters and try again.
Repeat the process: launch it, attach the program, and press f8 to retry the step above and execute the gets().
We can already see that it entered from 0x1 to 0xfe, which means that avoiding the five characters that we put in the list of invalids will pass our payload.
Now we can remove the initial invalid characters one by one. Let's start with 0x0.
Now, let’s try executing the gets () again.
We can see that 0x0 is not an invalid character since it entered and didn't cut the payload. Let's continue with the others.
We remove the 0xa.
We see that 0xa is an invalid character since it cuts the payload and does not enter complete. We should add it again and remove the 0xd.
Now we see that 0xd is not an invalid character as it did not cut the payload. Only 0xff is left to try.
To enter the character 0xFF in the loop, it must loop up to range(256) to ensure that 255 (0xFF) is included.
Since we have entered the whole string and individually tested all the characters, the invalids for the stacks are 0x1a and 0xa. The rest can be sent without problems.
That's why in stack 3, which has a zero in the cookie, one needs to investigate if the zero is an invalid character. Since it is not, we can send it in the payload. This means the script will be like stack 1 and 2, except for the value change so that it compares with the cookie.
STACK 3 SOLUTION
We must pass "05\x00\x02\x01" to compare with the cookie. There are no invalid characters so there is no problem.
Next, we can print the invalid characters. Using the same analysis that we did for stack 1 and 2, we can solve number 3 without problems.
Whenever we must perform an exploit, it helps to take the time to detect invalid characters, as it will ensure \that our payload will not be affected by them.
STACK 4 SOLUTION
The stack 4 case is different as there is an invalid character in the cookie.
We can see that we need to pass over this to step on the cookie.
payload = b"A" * 80 + b"\x00\x0a\x0d\x00"
Our payload contains an invalid character. This means we cannot pass the value we need to step on the cookie, which will complete the exercise and print “YOU WIN”.
We will look into the solutions in the upcoming part five.
If you want to try this exercise, you must continue to step below the cookie and try to step on the return address, forcefully returning to the address where the program calls the printf with the message “YOU WIN." Otherwise, look for some way to achieve the printout that we want.
Of course, it is quite possible the program will break when you do that, so there are only two valid solutions:
- Print “YOU WIN” and breaking the program, which is the simpler option.
- Print “YOU WIN” and to avoid breaking the program, which is obviously much more difficult.
Let's see who attempts to do it either way—or both!
Get the "Penetration Testing Toolkit"
This toolkit is designed to guide you through all the steps of managing an effective penetration testing program including: A detailed explanation of the six critical stages of pen testing, Maximizing defense tactics with purple teaming, Reinforcing broader security strategies using pen testing results and Deploying phishing simulations to prevent social engineering attacks.