Introduction

Hey everyone! We’re going to attack the sixth exam component for the SecurityTube Linux Assembly Expert course. This one is fun because we get to modify existing shellcode samples to make them polymorphic. Unlike the analysis component where we split up each individual analysis, I think this one is short enough that we can easily cover it in a single article.

Requirements

Take 3 shellcode samples from Shell-Storm and create polymorphic versions of them to beat pattern matching

The polymorphic versions cannot be more than 150% larger than the original shellcode

Bonus points for making it shorter in length than the original

Sample 1

So lets dig into our first shellcode sample. I chose to start on sample 477 from Shell-Storm, which will use execve to issue the command /sbin/reboot.

Because we don’t want to rely on Shell-Storm being available for this article to work, the original shellcode is also in the github repository linked at the top of this article and is shown below.

So this is the C harness that was published on Shell-Storm, with the corresponding assembly instructions. It weighs in at 28 bytes. Since we have commonly been working with Intel syntax, not AT&T, I have rewritten and commented the original in a .nasm file:

This is a bit easier for us to read. Essentially, the command sets up the execve call, loads the command string onto the stack, and then executes the command.

From this, we’ve rewritten this like so:

So what did we change? Honestly, most of it stays the same due to the size limits, but we change how we handle loading the string, hoping that by making this less clear, we can avoid very basic detections. Instead of pushing it onto the stack via push, we instead move the value into the memory locations on top of the stack, and then subtract 12 (the offset we’ve filled with data) from ESP (since the stack grows from high to low memory, we subtract to add more data) which re-aligns the stack.

We started with 28 bytes, 150% of the size is 42 bytes. After our changes, the shellcode has grown to 41 bytes, keeping us under the 150% size increase limit.

Sample 2

So lets dig into our second shellcode sample. I chose to start on sample 813 from Shell-Storm, which will use the write system call to change the value in //proc/sys/kernel/randomize_va_space to disable ASLR

Because we don’t want to rely on Shell-Storm being available for this article to work, the original shellcode is also in the github repository linked at the top of this article and is shown below.

Similar to above, we want to rewrite this into Intel syntax with comments to make it easier to read, due to the size (this weighs in at 83 bytes).

Much better. Now we can see that we’re writing 0 to //proc/sys/kernel/randomize_va_space, disabling ASLR in the process.

After making our changes, we end up with:

Here, we make a number of subtle changes. We change the first XOR eax, eax to SUB eax, eax causing the same result using a less common operation. We then change the MOV ebx, eax into xchg ebx, eax. We then replace the EDX xor opertion with another SUB operation. And conclude by replacing the MOV al, 0x4 with a push 0x3, pop eax, inc eax to bypass the pattern matching that we’re using system call 0x4.

By making these changes, we started with 83 bytes, 150% of that size would be 124 bytes, but we’ve only increased the size by one, making the total size 84 bytes.

Sample 3

So lets dig into our final shellcode sample. I chose to start on sample 564 from Shell-Storm, which will use the kill command to kill all processes

Because we don’t want to rely on Shell-Storm being available for this article to work, the original shellcode is also in the github repository linked at the top of this article and is shown below.

This is pretty straightforward and doesn’t require commenting due to it’s short size.

We can easily make some simple changes to avoid pattern matching:

The change here being that rather than using move, we instead push all of the value we want onto the stack, in reverse order, and pop them off again. This completely changes how our code looks.

This brings us from 9 bytes to 11 bytes. 150% of 9 bytes would be 13 bytes, so we stayed within the size requirements and have completed the polymorphic requirement for SLAE.

Author Kevin Kirsche

Kevin is a Principal Security Architect with Verizon. He holds the OSCP, OSWP, OSCE, and SLAE certifications. He is interested in learning more about building exploits and advanced penetration testing concepts.