Encoded/Obfuscated shellcode — SecurityTube Linux Assembly Expert 32 bit — Exercise 4
In the last section/exercise we saw how to craft an egg-hunter and test it out for different payloads. In this section we will pivot a bit and talk about trying to make our shellcodes obfuscated so that its not easily/trivially detected. In all fairness, this is a daunting task and almost always not achievable consistently unless your obfuscation process is polymorphic. I know, I just dropped a word without explaining :) I will get to that in a moment.
So, lets go about it. Most of the time the payload/shellcode we create have meta-data(byte pattern, ascii bytes etc..) that is used by EDRs, Network monitoring tools or Anti-Virus to fix the signature of a particular payload. In fact there is only so much variation we could have with different payloads varying across different platforms and architectures that most of the AVs etc.. have a signature already fixed for them. The way we could encode our payload and then decode it during run-time is also limited by our imagination and the side-effects its going to produce in terms of the size of the final payload. The bigger the payload the more challenging/difficult it is to inject it successfully/safely in the target process/system. The first rule of any shellcode craft is to minimize the length of the shellcode as much as possible. Now coming to the obfuscation, we could deploy any algorithms like insertion sort, bit mapping, marking bytes and swapping at run-time - basically any schema that can seemingly randomize the payload and assemble it to the correct form at run-time and pass control to it. How super cool is that?
The limitation with this approach is, anyone with a decent knowledge of the code/assembly would be able to decode the algorithm and we are toast. We would need to encode the payload in a different way to evade the AVs or EDRs successfully. So basically its a one-hit wonder.
The other class of encoders are called polymorphic encoders that do a pretty good job at obfuscating the payload with multiple layers of obfuscations with randomization and other tricks like feedback loops as inputs to the next stages, to basically throw any AV or EDR off its scent. These kinds of encoders have sophisticated algorithms based on solid mathematics. One such famous encoder is the ‘shikata ga nai’.
In this exercise we will look into a simple encoder that takes any payload and randomizes/encodes the payload and XORs each byte such that with enough entropy the final payload is 60% more than the initial payload size. Its not efficient space wise but its simple enough to understand and to encode/decode. The randomization is based of a bit string of any given 4 bytes. For this example, I chose the hexadecimal 0x10D06E, but I padded it to 4 bytes with 00 and the final bit string is 0x0010D06E. This bit string is like this, for the impatient: 00000000000100000000110101101110.
So what this means is, wherever there is a zero(0) we will mark that position in the payload with an invalid byte(Ex: 0xAA) and not touch the bytes in the position that has a one(1). With the final result we will bit-wise XOR the payload with a pre-configured byte(Ex: 0xEE). The decoder will decode and assemble the payload during run-time and pass control for execution. Enough of talk, lets see the setup and the code. I have written a python script to help us to spit out the final shellcode we want to embed in another shellcode.
Note: Be cognizant of the fact that your choice of selecting a marker(invalid) byte will affect the payload and you might inadvertently remove it from the final payload. Also the XORing byte has to be selected such that it does not have similar bytes in the payload because then it zeroes it off and you have a null(bad) char in your payload. Long story short, in a hurry make educated guesses while choosing these two values. Ex: I had selected 0x99 as a marker(invalid) byte until I hit a snag while running the port binding shellcode, if you observe carefully it has a 0x99 in its normal payload, damn :)
The normal shellcodes for an execve(/bin/bash) shellcode and a port-binding shellcode.
After referring to the bit map or the bit stream the individual shellcodes are randomized and inflated in size.
After the payload has been bit-wise XORed with a pre-configured key, Ex: 0xEE
Now for the shellcode, that is a carrier or vector of sorts for the above payload. The following shellcode has the logic/stub to decode the above payload at run-time and execute it.
Observe that the final encoded shellcode is copied into the label declaration ‘_undev: db’ .
Now all we need to do is assemble our carrier shellcode and off we go to the races. I learned a lot and had a lot of fun while crafting this one. Hope you enjoy it as much as I did guys. Before I go, here is the output for both the payloads, execve shell shellcode and the port-binding shellcode. As usual, we will use our tried and tested test harness to execute the shellcode :)
The following is a port-binding shellcode listening on 31337 on localhost.
Please check the following github repo for the entire set of source files and helper scripts to generate and test the shellcode.
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification.
Student ID: SLAE -1100