Skip to main content

SLAE64 - Assignment 5

This post is a continuation of a seven (7) part blog series as part of the SLAE64 certification challenge. You can read the previous blog posts using the links below.

Previous Posts:

The requirements for Assignment 5 are as follows:
  • Take up at least 3 shellcode samples created using MSFPayload for linux/x86_64
  • Use GDB to dissect the functionality of shellcode
  • Document your analysis

For this assignment, I chose the following Metasploit payloads:
  • linux/x64/shell_reverse_tcp
  • linux/x64/exec
  • linux/x64/shell_find_port

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

Additional scripts developed and used for this course can be found here:

We'll tackle these in order listed starting with the non-staged reverse shell. If you want to follow along, make sure to run the following command. I'm using Kali rolling 2016.2. As you can see, by not specifying bad characters, we have a couple of NULL bytes. This is fine for now but wouldn't fly if this was used for an actual exploit.

root@kali:~# msfvenom --payload linux/x64/shell_reverse_tcp rhost= rport=4444 -a x86_64 --platform linux -f c

No encoder or badchars specified, outputting raw payload
Payload size: 74 bytes
Final size of c file: 335 bytes
unsigned char buf[] = 

Excellent, now we just shove this into a C program to execute and throw it in GDB so we can see the assembly of this shellcode. I intentionally started with this one because if you've read my post on Assignment 2, this should be pretty familiar.

The program starts by pushing 0x29 onto the stack and a POP RAX moves that value into RAX. This is the system call number for define __NR_socket 41. The next instruction CDQ sign extends RAX which results in RDX getting zeroed out. 0x00 is the value for IP in /etc/protocols. Following this, a series of PUSH and POP instructions are used to setup RDI, and RSI before executing SYSCALL. Excellent, our socket is created. An XCHG instruction is used to store the resultant return value SOCKFD into RDI.

Here's what our registers look like before entering the next block of instructions. RDI has our SOCKFD return value 0x3 since we'll need it later.

The next part of this code is interesting and I actually learned something about Metasploit. Let's focus on the MOVABS instruction, and more specifically the value being copied into RCX. Remember, when we ran the MSFVENOM command, we specified an IP Address of Instead of 0x101017ffor the first part of our value, like we should expect, we have a value of 0x89faa8c0. When converted, using some Python magic, this winds up being in Little Endian. What's interesting is that was my IP address at the time I coded this assignment. So, despite entering a loopback address, MSF used my actual IP Address! And, if you haven't figured it out already, the MOVABS RCX, 0x89faa8c05c110002 is setting up our IP, Port, and AF_INET values for our CONNECT system call. We PUSH RCX onto the stack and copy the stack pointer address into RSI to setup our second argument to CONNECT. PUSH and POP instructions setup RDX and remember, RDI already contains our SOCKFD which is the second argument to the CONNECT system call. Finally, SYSCALL executes CONNECT.

So, let's quickly rehash a bit. We've created a socket, we've connected that socket to a remote host. The next step is to redirect the connection to standard in, out, and error using the define __NR_dup2 33 system call. Figure 4 shows the block of instructions that loop through executing DUP2. Notice that we start with the value of 0x3 in RSI, and each time the execution jumps, it jumps to the DEC instruction which decreases the value in RSI by one. So, as we think through this, the first time this block executes, it's redirecting the sockfd standard error (0x2), the second time it executes, it's changing the sockfd to standard out (0x1), and the final time changes the sockfd to standard in (0x0).

As you, the astute reader probably figured out already, this last block of code uses EXECVE system call to execute /bin/sh. We'll once again use a PUSH and POP technique to setup RAX. The CDQ instruction will be used to zero out RDX and we'll use RCX to hold /bin/sh in Little Endian format so that we can push a qword onto the stack and reference it with a pointer. The address to /bin/sh on the stack gets copied into RDI. The next several instructions setup RSI for the 2nd argument to EXECVE. Finally, the SYSCALL instruction executes the EXECVE system call.

The next shellcode we will examine is a simple linux/x64/exec payload from Metasploit. You can get the raw shellcode in C format by running the following command.

root@kali:~# msfvenom --payload linux/x64/exec cmd="cat /etc/passwd" -a x86_64 --platform linux -f c

No encoder or badchars specified, outputting raw payload
Payload size: 55 bytes
Final size of c file: 256 bytes
unsigned char buf[] = 

This one was interesting to dissect in GDB because I had problems with GDB and this shellcode yet the compiled C program ran the shellcode just fine. So, i'll just detail what it does based on the first several instructions before it crashes. We start by setting up RAX to hold our 0x3b value which is the system call number for EXECVE. We initialize RDX with a CDQ instruction again and copy /bin/sh into RBX temporarily, push it onto the stack, and then copy the stack pointer address into RDI the next instructions setup the -c option to /bin/sh and get that setup onto the stack to complete our struct. We then move those instructions into RSI. This is where the code breaks in GDB but runs fine. To quickly break it down, this code will call EXECVE to execute /bin/sh -c against whatever we enter into the CMD option in MSFVENOM.

We can see it works by running the compiled C program with our shellcode. Figure 7 shows the result of doing so. One interesting thing we notice is that our shellcode is only 13 bytes long! What? Metasploit told us it was 55 bytes long! What is going on?

Let's examine the first 14 bytes just for giggles. Son of a bisquit! The 14th byte is a NULL byte. Most likely this is throwing our C program off quite a bit and as a result, GDB freaks out. So, the point with this particular payload is to always tell MSFVENOM to considre NULL bytes as bad characters. So while the C program and GDB freak out, the assembly code does actually run. I went back and checked the first script and sure enough, because we're executing this via a C program, it also shows that the payload at runtime is only 17 bytes as opposed to 74 bytes. For some reason though, GDB didn't have a tough time with our first script, only the len() function within our C program. Most likely this is due to the position of our first NULL byte in that script compared to this one. Our first NULL byte in this script falls in a much earlier place.


Let's unpack this and dive in deeper. I'll make sure our CMD option is divisible by 8 this time to eliminate that as a possibility. Unfortunately it doesn't matter. We still have a NULL byte at position 14. This just goes to show how annoying NULL bytes can be.

root@kali:~# msfvenom --payload linux/x64/exec cmd="/////bin/cat /etc/passwd" -a x86_64 --platform linux -f c

No encoder or badchars specified, outputting raw payload
Payload size: 64 bytes
Final size of c file: 295 bytes
unsigned char buf[] = 

the only other option is to use a different tool to figure out what this code does, assuming we didn't really understand from the get go. I tried NDISASM and it too had trouble with that NULL byte. I know this because of the way it interpreted that section of shellcode into an IMUL instruction followed by the ADD.

Our final shellcode we'll examine is the Linux/x64/shell_find_port. Instantly when we run the below command to generate our shellcode we notice no NULL bytes. This is excellent so far.

root@kali:~# msfvenom --payload linux/x64/shell_find_port CPORT=12345 -a x86_64 --platform linux -f c

No encoder or badchars specified, outputting raw payload
Payload size: 91 bytes
Final size of c file: 409 bytes
unsigned char buf[] = 

We start by setting up our registers for a define __NR_getpeername 52 system call which, according to its man page, gets the name of a connected peer socket. We don't have a socket connection right now so this script may not do much. Instead we'll dissect how it works using GDB.

Once we execute our system call for define __NR_getpeername 52, we continue with a DUP2 system call provided we didn't get an error from a comparison instruction. If we did, we jump back and execute our define __NR_getpeername 52 system call again. We keep doing this until finally getting a match. We then proceed to execute DUP2 system call to redirect the sockfd to std error, std out, and std in, in that order.

Figure 11 shows the remaining instructions to redirect our discovered socket.

Our final block of instructions use the EXECVE system call to execute /bin/sh through our redirected discovered socket. It's important to note I didn't have a socket running so pay little attention to the register setup in the last few screen shots. I merely wanted to show you the instructions and walk you through them.

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

Student ID: SLAE64 - 1439

Next: SLAE64 - Assignment 6


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 P

Binary Analysis Cookbook: The Process and Working with Packt

Hello to you, yes you, reading this blog. Thank you for stopping by and I apologize for going silent for a while. Life has been busy in one way or another and unfortunately, this was one area that suffered. Thank you for understanding and please read on. At the end of 2018, I received a LinkedIn message from someone at Packt Publishing inquiring whether I would have any interest in writing a book for them. Naturally, and partly due to my lack of knowledge or experience with the process, I was a bit skeptical. I replied to the e-mail and said I would be interested in finding out more, all while nearly simultaneously I reached out to Packt via their web contact form to verify this person was who she said she was. I mean after all, this kind of thing doesn't normally happen to me and I have done what I can to regain some of my public anonymity following a career in broadcast television.

SLAE32 - Assignment 1

In preparation for the next Offensive Security certification class and challenge (CTP and OSCE), I decided to invest some time and energy into the Security Tube Linux Assembly Expert 32-bit class. That way I can have a solid foundation in understanding the finer workings of Assembly. Especially since my focus for my second Bachelor's degree was more along the lines of system administration and back-end web development instead of the programming focus of Computer Science. Still, I never stop with my learning and barely slow down at times. This was the first assignment out of seven (7) and the requirements for assignment one (1) were as follows: Create a Shell_Bind_TCP shellcode Binds to a port Execs shell upon connection The PORT number should be easily configurable This is a pretty standard request but I must admit the process was only somewhat familiar. I knew I could write the code pretty easily once I understood the process. For this, I had to fall back on my love