In part 01 of this blog post, I covered how you can utilize a function overwrite with shellcode to obfuscate code to make reverse engineering more difficult and to hide capabilities from anti-virus applications. In this addition, part 02, I will be covering additional capabilities utilizing network communications to send polymorphic shellcode over the network to control an “infected” client, making the reversing process a step more difficult, as now no functionality is hidden within the piece of “malware” and only can be retrieved via network communications. Lastly, I will be covering how static based signatures can be problematic for detecting this form of malicious activity.
Starting were we left off before, we are now going to modify our code to not have the shellcode within the portable executable’s read only section of memory, but to receive it from a remote server. Below we can see a very basic setup for this using local host and the typical TCP port 1337. The reason for local host being utilized in this example is just because we run our “malware” on the same system just for demonstration purposes. Essentially what our code below is doing is performing a very basic form of obfuscation by changing the first byte in the shellcode. If the shellcode’s first byte is greater than 32, we set it to be 32, else we increment it by one. After performing this change to our shellcode value, we then return it and send it to the client that makes a TCP connection over port 1337 to this C2 server.
C2 Server Code
For our “malware” we can see our main function below. What this code does is simple:
- Create a char array named “remoteShellcode” to store our retrieved shellcode from the C2 server.
- We create a boolean named “triggered” to indicate if the shellcode has been retrieved from the C2 server.
- We enter a loop, while the shellcode has not been received, we sleep for 5 seconds then reach back out to the C2 server. If the C2 server successfully responds, we copy the shellcode into the “remoteShellcode” char array, and set the value of triggered to true which will break us out of this while loop.
- Now we can call “swapFunctionWithShellcode” and perform a function overwrite with the shellcode we received from the C2 server, just like in part one of this post.
- This overwrite triggers our shellcode just before the function returns and executes our new functions instruction set.
Malware Main Function
To show a working example of this, below we can show our client’s “malware” running before the C2 server is started.
Clients “malware” before the C2 server is online
Now when we start our C2 server, we can see the behavior change and we print out a greeting to my university, Dakota State University.
Earlier we saw the shellcode slightly modified by changing the first byte if it was greater than the value 32, so how did this work exactly? Don’t we need the shellcode to stay the same to properly execute? The answer is yes, to better understand this lets take a look at the code on the client side that handles this. In the example below, we can see we check about halfway through this loop if the first byte of our “remoteShellcode” char array is equal to 0x32, if so we subtract it by 1, and if it equals 0x33, we subtract it by 2. This allows us to retrieve “polymorphic” shellcode and make the corrections necessary to run this shellcode successfully when we perform our function overwrite and call the overwritten function.
Shellcode Correction Client Side
So we went into a bit of detail and put emphasis on making this shellcode “polymorphic” but why does this matter? To better understand why this matters, lets take a look at the shellcode captured in wireshark. In the example below we can see our data section of the packet, with a length of 114 bytes coming from our C2 server using TCP port 1337.
Shellcode starting with 0x33 in a packet capture
In this next image, we can see the slightly modified version of the shellcode as well
Shellcode starting with 0x32 in a packet capture
What happens if we start using static signatures to detect this attack? Imagine what this shellcode would look like if we really tried to obfuscate it using symmetric encryption instead of our toy example here where we can visually make a connection between the two request, now things will become much more problematic as the cipher text representation of this shellcode would render static rule sets useless. Thus essentially creating the same issue commonly crossed when cyber security professionals try to detect polymorphic malware in general, as it constantly changes its static signature, it makes it incredibly difficult to detect until it is decrypted in memory and by this point it may be too late.
This example above as always, should not be taken as the end all to these techniques and is utilized as an example for educational purposes. The goal as always in these blog posts is to education the reader and to hopefully inform them of some techniques that they may come across in the real world and inspire some to have some fun tinkering within this field. I hope this was enjoyable, I always look forward to feedback. If you have any questions please leave a comment.