This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert Certification. The task for 6/7 assignment is to take up to 3 shellcodes from shell-storm and create polymorphic versions of those samples to beat pattern matching. The requirement is that the polymorphic versions cannot be larger than the 150% of the existing shellcode.
Student ID: SLAE64 - 1594
Shellcode 1 -
The first shellcode I chose from the Shell-Storm website was a generic 64-bit
execve() shellcode which is 30 bytes long. So, I created a polymorphic version with the length of 43 bytes (less than 150% of the original shellcode). On the picture below I have shown which exact instructions got replaced with something else and then continue to explain each desicion one step at a time.
1 - Instead of the
push rax marked in yellow, I decided to
xor rdx, rdx first and use that instead, as I would be needing to set
rdx zero anyway to prepare it for the
2 - I also decided to replace the
push rbx (in green) from the original shellcode in a manner where I could get the same result but via another register. For that I simply chose to move the value in
rbx to another register which I then push’ed instead.
3 - To prepare the 2nd argument
rsi for the syscall, I took another way of first allocating space for it right after I had pushed 8 zero-bytes to the stack. Then moved the value from
rdi to an address appointed to by the stack pointer (so right to the spot which I just allocated) and moved that address to
4 - since I did not clean the
rax register somewhere earlier, I made sure it was zeroed out before loading the syscall number to it and executed the syscall.
Shellcode 2 -
Going through shellcodes at Shell-Storm, I came by a shellcode which used the
execveat() syscall. I had never tried it myself before so seemed an interesting way to go with that one. I ended up with a 31 byte long polymorphic version of that shellcode, so only 2 bytes longer than the original one.
1 - the author of the original shellcode wanted to prepare the syscall number 0x142 for
execveat() as the very first thing. Without loading the value directly to its intended register
rax, he loaded a value 0x42 and then incremented the
ah by 1 ending up with the value 0x142. I decided to go the other way around and substract 1 from 0x143 instead, ending up with the same result.
cqo is an intresting instruction, which doubles the size of the operand in register
ax/eax/rax and stores the result as
dx:ax/edx:eax/rdx:rax. so basically what happens here is that
rdx register will simply be zerod out, because of sign-extending. We can get the same result by xor’ing
rdx with itself.
3 - the instructions marked in blue, where the program name “/bin/sh” is placed eventually to the top of the stack with
push reg instruction, I simply used another register (
4 - I removed the lines marked in red, as there was no need for those instructions. This ended up shortening my shellcode a little bit as well.
Shellcode 3 -
Shellcode number 3 is 27 bytes in length and quite much similar to the first one, but gets the program name for
rdi by negating the value in
rbx. I decided to go with the
imul instruction instead as you can see on the image below.
1 - I chose to substract the value in
rax from itself, ending up with a zero.
2 - Next I moved the value 0xEEBE234583299 to
rbx. Multiplying it with 0x7
EEBE234583299 × 7 = 68732F6E69622F and converting this hex value to ascii, will give us the string
cdq replacement with
mov rdx, rax allowed me to have the same effect of zeroing out
rdx as I assured earlier that the value in
rax would be zero.
4 - Finally the
pop rsi instruction. What pop essentially does is that it takes the value stored at an address pointed to by
rsp and places that value into rsi. As a side-effect,
rsp is incremented by 8. So, by using the
mov rsi, [rsp] I am going for the value stored at an address
rsp and moving it to
rsi. Then with
add rsp, 0x8 I increment the
rsp as this is what would happen automatically if I used the
So what we learnt?
cqois good to use when you try to zero out rdx by sign-extending the value in rax
- We can switch up the registers we use in our shellcode super easily
- We can use various combinations of instructions to end up with the same result value
- 64-bit bindshell with a passphrase protection
- 64-bit reverse shell with passphrase protection
- Egghunter (64-bit Linux) using access() syscall
- Writing a 64-bit custom encoder (reversed byte order of 4 byte chunks)
- Msfvenom generated Exec shellcode analysis - exec shellcode
- Msfvenom generated bind_tcp shellcode analysis
- Msfvenom generated shell_reverse_tcp payload
- Polymorphic shellcodes for samples taken from Shell-Storm
- Custom encrypter - One-time pad (OTP)