This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification: http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert/
Student ID: SLAE-1294
A polymorphic version of shellcode uses different instructions while maintaining the original functionality, particularly in order to evade IDS or antivirus systems that may be searching for signatures. This sixth assignment is to take three samples of linux/x86 shellcode from the shell-storm.org database and manually create polymorphic versions of them. It is required that the new versions are no more than 50% larger than the originals.
The following shellcodes were analysed and recreated with different instructions/approaches.
These were chosen because I can reasonably easily test their effects on my Kali VM without destroying it, and the overall size of each is not too large.
nc bind shell
This shellcode is provided in nasm format already. Its purpose is to provide a bind shell, not by listening for clients itself but by invoking a local copy of netcat:
/bin/nc -lvve/bin/sh -vp13377. I checked it carefully, built it, and confirmed that it works.
I noticed something a little odd at the start. It clears some registers then pushes the string
-vp13377 onto the stack.
This parameter is missing a null terminator. This can cause the wrong port to be selected. Since
nc is parsing everything after
-p as a port number it will stop as soon as it reaches a non-numeric character. Most of the time this is fine. However if the next byte on the top of the stack happens to be a valid ASCII number it will be attached to the number 11337. In this case the port number will overflow:
# nc -lvp 113371 listening on [any] 47835 ...
I consider this a bug in the original source code and I will fix it in my own version by pushing a null to the stack first.
We don’t need to zero out
edx at this stage. The goal is to place 0 inside
eax - instead of using xor, subtract it from itself.
First a new
push eax is inserted to ensure this argument is properly terminated, as discussed above.
The dword pushes are converted to pushes of two words each. This requires additional opcodes but means those tell-tale constants are broken into smaller, separate chunks in memory.
The pointer is stored in
esi instead of
push eax provides a null terminator. Here we will use
push ax instead, which will still give us two zero bytes on the stack. Unfortunately this requires a two byte op-code where the original was one byte.
The constants are obfuscated the same way as before. This time the pointer is stored in
edi instead of
The last string is handled much the same, except we remove the middle push to save space. The original pushes the string “/bin//////nc”, probably an attempt to evade AV that is looking for “/bin//nc”. I am doing my own word-splitting evasion here so I’ll take it out.
This pointer is stored in
ecx instead of
ebx, using two steps because we can. The original code had a
mov ecx,esp in an earlier position so I may as well avoid it.
edx is deferred to this point. The stack is set up like before except we’re using a different combination and order of registers. Because we didn’t use
ebx already for the path to
nc it must be configured as an extra step.
ecx is pointed to the structure we just created using
add instead of a
The syscall number can be split into two steps. I decided to leave the
int 0x80 alone. This instruction is hopefully not too suspicious.
This builds and works fine. I test that I can execute it and use the created reverse shell.
iptables –flush shellcode
Since the source code was given in AT&T format I decided to take the assembled format and disassemble it with ndisasm. The corresponding disassembly is in the GitHub repository.
This one is relatively straightforward and not so different from the
nc shellcode above. The main job it is doing is setting up the parameters for
execve on the stack.
This time I will try to use a different technique—PUSHAD. This single op-code causes
edi to be pushed onto the stack all at once in that order. Instead of pushing dword constants onto the stack I can load them carefully into registers, then place them all on the stack all at once.
In the first place we still need a zeroed register. I simply choose a different register.
push esi. The path to iptables is going to span four registers—
ebx. The next register in the pushad operation is
esp and I can’t fiddle with that. I still need a null terminator, so I push it in advance.
-F parameter is formed using half of the last register to be pushed,
edi. After the pushad completes,
esp will point to this string.
All of the strings that would be pushed as literals are lined up in the corresponding registers instead. I haven’t attempted to obfuscate the literals itself in this example. This is perhaps fairly weak evasion but it could be combined with other techniques too if necessary.
Next the argv array must be set up. I will do this by pushing pointers as before, except this time I will be pushing pointers by their relative positions from
argv is on top of stack, all that remains is to put the correct arguments into registers for the
I use some trivial two-instruction substitutions to make this look different. Ultimately
execve is called with the correct parameters.
I run my modified version and confirm that my iptables rules are flushed.
chmod 666 /etc/shadow shellcode
This shell-storm source file is a little confusing because there are two versions in the same file. I took the assembled version from the comments and disassembled it with ndisasm.
Without going into too much detail, I analysed it and confirmed that it works in a test program. It does the following:
- Push “/etc/shadowS” onto the stack
- Fix the last byte so it’s a null instead of
- Set parameter 0666 = 0x1b6 in
- Point to “/etc/shadow” in
- Execute the
chmodsystem call (15)
The biggest difference in my version is how I load the data onto the stack. I obfuscate the string literals by XORing each dword with the key 0x11223344. This has the additional advantage that I can include the null byte directly in the encoded version and don’t need to fix it up later. The encoded values were calculated with python:
>>> hex(0x53776f64 ^ 0x11223344) '0x42555c20' >>> hex(0x6168732f ^ 0x11223344) '0x704a406b' >>> hex(0x6374652f ^ 0x11223344) '0x7256566b'
With that explained, let’s go from top to bottom.
In this shellcode
ebx did not need to be cleared at all. However
ecx will need to be zero later so I modify this instruction to do it here.
Here we see the encoded strings being decoded and pushed onto the stack. Originally I tried using a small routine for the decoding but with so little data it actually ended up smaller to just repeat the
Use an extra level of indirection that ends up placing
This time ecx is already zero because of the changed xor operation at the beginning. The xor encoding means we no longer need to update the byte at
Instead of placing 0x1b6 directly in the register we will use double that value, 0x36c, and then use an arithmetic shift right to divide it by 2.
The original loads the number 15 into
eax, taking a little care to avoid null bytes in the op-codes. This time we will start with the known value in
ecx and subtract a constant to get back to 15.
I have taken a small liberty here. If the
eax will contain the return value 0. Therefore we don’t need to clear it. If the
chmod fails - perhaps the program is not running as root - then it may not exit cleanly. If this is important it could be re-inserted for a cost of 2 extra bytes.
This new version works equivalently to the old one. Here it is making my computer insecure.