Polymorphism examples with Linux Intel x86 shellcodes
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
Student ID: SLAE-975
Assignment number: #6
Github repository: https://github.com/amonsec/SLAE/tree/master/assignment-6
This post is part of my SLAE series.
You can find the previous post at this address: https://amonsec.net/training/linux-assembly-x86/2018/metasploit-linux-x86-payloads-analysis
In this post we will discuss about polymorphism, what is it and why it’s useful nowadays.For that, we are going to take three different shellcode from the very well known Shell Storm website and we will change them in order to have structurally different shellcodes, but with same functionality
- Linux distribution, in my case Ubuntu 10.04 LTS (x86)
- A keyboard and
- maybe some caffeine ?!
What is polymorphism?
In our case, polymorphism is a technique used to bypass anti virus and intrusion detection systems (IDS) by defeating patterns matching algorithms. So, our shellcode must be structurally different but functionally identical.
To do so, we can:
- Add garbage instructions that don’t interfere with the shellcode execution;
- Use different registers, instructions and
- Use weird instruction
Note, in this post I will create three different polymorphic shellcode but they certainly can be more polymorphic and they are certainly not fully undetectable (FUD) with modern AV and IDS.
First example – execve /bin/sh
For the first example, I have chosen a basic shellcode that will spawn a shell. The original shellcode can be found here: http://shell-storm.org/shellcode/files/shellcode-811.php
Instead of pushing /bin/sh into the stack I decided to use a JMP CALL POP techniques, where, after the call of the subroutine, I will POP the next instruction into a register.
The skeleton should be like that:
In order to find something that will after being pushed into the stack represent /bin//sh I used this awesome website that provide both x86 and x64 instructions assemble/disassemble.
First we need to convert our desired string into hexadecimal:
Then, we can use the website to find a sequence of instructions that will, when pushed into the stack, represent /bin//sh:
Our polymorphic shellcode will look like this.
We extract the final shellcode.
Finally, we can create a C program with your shellcode.
We compile and we try our polymorphic shellcode.
Note, our polymorphic version have 8 bytes less then the original version.
Second example – chmod 777 /etc/shadow
For the second shellcode we will use this one: http://shell-storm.org/shellcode/files/shellcode-828.php who change the permission of the /etc/shadow file where all Linux accounts’s passwords are stored.
In our case, instead of pushing directly the file name we will randomly store pieces of the file name in registers and then push them in the good order.
Then, we use the SUB instruction instead of moving into EAX the chmod syscall id and the desired mode into ECX.
Same as before, we extract the shellcode from our shared object, then we create and compile a basic C program that will be used to execute our fresh polymorphic shellcode.
Third example – setuid(0) + setgid(0) + add new user
For our last example let’s try with a more complex one. You can find the original code here: http://shell-storm.org/shellcode/files/shellcode-798.php
First, we can set the uid to 0 and the gid to 0:
Then, we use a JMP CALL POP technique in order to don’t directly push the name of the file name that we want to open.
One more time, we can use both the previous website and python to find a sequence of instruction:
Now, we open our file, in our case /etc/passwd
The file is open, so we push into the stack the wanted new user, then we add the new user into the opened file.
Finally, we close the file and we exit the program.
Our final shellcode will look like this
Due to the fact the original shellcode use an ADD 10 encoder, we can add 10 to each hexadecimal characters.
With our encoded shellcode we create a C program that will decode our shellcode and execute it.
Finally, we compile and run this program.
So, in this quick post we saw three examples of polymorphism. It’s a really good exercise to pick some written shellcodes and try to modify them in order to find new obscure instructions or methods. Moreover, polymorphism is a good way to bypass some AV or IDS, so, understanding the concept and be able to customise a shellcode is always a plus.