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.
Bad Char Definition
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.