Introducing Jigsaw: Shellcode Obfuscation

By Red Siege | March 18, 2024

by Mike Saunders, Principal Consultant

As offensive security professionals, we often find ourselves writing custom shellcode loaders for initial access and code execution. Unless we’re using a fully custom C2, there’s a good chance our shellcode will get detected and get us burned. As a result, we find ourselves having to come up with creative solutions to hide our code and avoid detection.

The days of using simple base64-encoding, or XOR’ing our code with a single-byte key are long since passed. I would venture to guess that using some kind of encryption is the most common approach to solving this problem. However, as with everything, our choices sometimes have unintended consequences.

Without inserting some superfluous code sequence in the middle of the routine, using XOR with a multibyte key is signatured and will lead to detection in some endpoint products like Microsoft Defender. Hint: if you want to use XOR with a multibyte key, try disabling compiler optimization. Using other encryption algorithms like RC4 or AES comes with a cost – higher entropy. High entropy can be a problem with some endpoint protection products. To a trained analyst, use of known encryption algorithms might send signals that you’re trying to hide something. I’ve even seen Defender fire alerts when I used a Rust loader and simply imported the RC4 or AES crates. I never even used them.

There are lots of methods to get around the entropy problem I referenced earlier. I even wrote a tool to help with this. Jargon uses a lookup table to substitute words for shellcode bytes. As a result, you have a “shellcode-less” loader that doesn’t result in increased entropy. On the down side, depending on how large the words in your source dictionary are, you end up with a pretty large executable.

 

Yet another shellcode obfuscation method

I was thinking of a way to obfuscate shellcode that didn’t involve encryption, and didn’t make the payload significantly larger. If I could shuffle all the bytes of a given shellcode payload and keep track of where all the bytes went, I’d have a way to avoid getting detected based on signatured byte sequences. This approach also wouldn’t result in increased entropy. To anyone analyzing the payload, the shellcode would look like a jigsaw puzzle dumped on the table and with no picture on the box as a reference.

Eventually, I came up with Jigsaw. Jigsaw is a Python script that reads a raw shellcode file and creates a randomized array sized equal to the number of bytes in the shellcode. Jigsaw then uses random.shuffle() to shuffle the array into random order. Each entry in this array represents the position of a byte in our shellcode. At that point, it’s a matter of iterating through our array of positions, grabbing the shellcode byte at that position, and storing it in a new array.

Here’s a contrived example to help illustrate the concept. Our shellcode consists of the following 10 bytes:

shellcode = [ 0xfc, 0xe8, 0x89, 0x00, 0x00, 0x00, 0x60, 0x89, 0xe5, 0x31 ]

To generate our array of randomized positions, we’d do the following:

>>> positions = list(range(0,10))
>>> random.shuffle(positions)
>>> positions
[1, 6, 8, 2, 5, 9, 7, 0, 4, 3]

If we iterate through positions, look up the byte at the corresponding position in the shellcode array, and store that value in a new array, our shuffled shellcode then becomes:

shuffled_shellcode = [ 0xe8, 0x60, 0xe5, 0x89, 0x00, 0x31, 0x89, 0xfc, 0x00, 0x00 ]

At this point, we just need to build the logic in whatever language we’re going to write our loader in. This example would work for a C or C++ loader:

unsigned int calc_len = 10;
char calc_payload[10] = { 0x00 };
int position;

// Reconstruct the payload
for (int idx = 0; idx < sizeof(positions) / sizeof(positions[0]); idx++) {
        position = positions[idx];
        calc_payload[position] = jigsaw[idx];
}

In theory, this should allow us to store our shellcode in a manner that won’t result in detection. Of course, there is the possibility that the right sequence of bytes could be stored in an order that results in detection. It’s even possible that random.shuffle could generate an array of positions that exactly matches our shellcode. It’s always a good idea to test your payload for detections before you send it out in the world.

 

Detection

I couldn’t think of an obvious way of detecting this method of obfuscation. At some point, the deobfuscated shellcode will exist in memory and could be detected there. The methods used to load and execute the shellcode would also present opportunities for detection.

 

Conclusion

I did some searching to find examples of prior art. I’m sure I’m not the first person to have this idea, but I didn’t immediately find any examples. If you know of one, please drop me a note on Discord, X, or open up a Github issue so I can give credit where it’s due.

 


 

About Principal Security Consultant Mike Saunders

Mike Saunders is Red Siege Information Security’s Principal Consultant. Mike has over 25 years of IT and security expertise, having worked in the ISP, banking, insurance, and agriculture businesses. Mike gained knowledge in a range of roles throughout his career, including system and network administration, development, and security architecture. Mike is a highly regarded and experienced international speaker with notable cybersecurity talks at conferences such as DerbyCon, Circle City Con, SANS Enterprise Summit, and NorthSec, in addition to having more than a decade of experience as a penetration tester. You can find Mike’s in-depth technical blogs and tool releases online and learn from his several offensive and defensive-focused SiegeCasts. He has been a member of the NCCCDC Red Team on several occasions and is the Lead Red Team Operator for Red Siege Information Security.

Certifications:
GCIH, GPEN, GWAPT, GMOB, CISSP, and OSCP

Connect on Twitter & LinkedIn

Using Microsoft Dev Tunnels for C2 Redirection

By Red Siege | April 9, 2024

by Justin Palk, Senior Security Consultant   As penetration testers, we’re always on the lookout for new ways to get our command-and-control (C2) traffic out of a client’s network, evading […]

Learn More
Using Microsoft Dev Tunnels for C2 Redirection

SSHishing – Abusing Shortcut Files and the Windows SSH Client for Initial Access

By Red Siege | April 1, 2024

By: Alex Reid, Current Red Siege Intern   In the April 2018 release of Windows 10 version 1803, Microsoft announced that the Windows OpenSSH client would ship and be enabled […]

Learn More
SSHishing – Abusing Shortcut Files and the Windows SSH Client for Initial Access

Navigating Active Directory Security with EDD

By Red Siege | March 21, 2024

Tool developed by: Chris Truncer   Leverage EDD for Advanced Offensive Strategies EDD serves as a critical tool for offensive security professionals, enhancing domain reconnaissance with .NET efficiency. It facilitates a […]

Learn More
Navigating Active Directory Security with EDD

Find Out What’s Next

Stay in the loop with our upcoming events.