I decided it was time to get better at both reading and writing Assembly, so based on a few reviews, I decided to check out the SecurityTube Linux Assembly Expert course offered by Pentester Academy / Security Tube. I’ll be posting as I work through the exam.
This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:
Student ID: PA-7462
All the code from the project is available at my GitHub.
The first assignment was to study a TCP Bind reverse shell created by MSFVenom. I’m not going to go too deep into the details of the analysis of that shell code, as it’s been covered by others quite a bit. That being said, if you analyze the shellcode, you will see it makes a number of syscalls in order to open the port and execute the shell. These syscalls are a Sock_Stream, Sys_Bind, Sys_Listen, Sys_Accept, Sys_Dup2, and finally a Sys_execve. Contrary to the name of the exam/cert, I definitely wouldn’t call myself an expert (maybe I will be by the end of this) and I know there are things I could have done better with this code and it’s not the cleanest and smallest it could be, but this is what I’ve come up with thus far.
The syscall for the Sock_Stream is 0x66, so I move it to AL as the system call number is expected in EAX and I move it to the lower register to avoid any null bytes. Sock_Stream expect a 1 for a sys_socket, 0 for TCP, 1 for byte stream and 2 for IP. Finally, I move the socket file descriptor that is returned from the syscall in EAX to the EDI register. Additional information about Sys_Socket here.
Now moving on to the second syscall. We will be making a bind syscall. This syscall is where we set the port to listen on. This is pushed onto the stack, in my code i used port 4444 which is 115C in hex, but to account for Endianness we need to push 5C11.
Next we will make a Sys_Listen syscall. This syscall is a lot simpler than some of the others
The Sys_accept call is the next syscall, not a lot going on in this one either. We push 0 to the stack with ESI to listen on all IPs on the box.
Next for the Sys_dup2 call. This is where we redirect stderr, stdout and stdin to the socket that we created so we may interact with the shell once we connect to it. I decided to do a loop to set this up, to save some space, but you could do it three separate times and achieve the same goal. I initially ran into an issue with this one as I was trying to use a JZ (Jump if zero) instead of the JS(jump if signed), so I was setting up the stderr(2) and stdout(1), but was not setting up stdin. When I would connect to the shell, I got no response from my commands(since stdin was still on the “victim” side). If I issued a command on the victim side, I would see it on the attacker side, which is finally what lead me to realizing my mistake. ECX is used in this loop as our counter, but it is also an argument for the syscall (specifying stderr, stdout, and stdin).
The final call we have left is the Sys_Execve. This call is made to execute something on system. In this case, we are going to execute /bin/sh. Again, we need to consider Endianness and push the string on to the stack in reverse for Little Endian. Additionally, you want to ensure you are pushing the string in 8 bytes segments, luckily for us the system doesn’t care about additional slashes (/) so we push hs//nib/ (/bin/bash//). Since we’ve already set up the socket to listen on the port specified earlier and redirected stderr, stdout, and stdin to the socket all we have left is to connect to the port. If you’ve tested your shellcode and you know it works, but for some reason you are unable to connect, be sure to make sure there are no other security controls that could be hindering you. This could be a firewall between you and the system or on the host itself. Additionally, if the system is NAT’ed and that port is not being forwarded, you wouldn’t be able to connect.
The resulting shellcode is:
As you can see, there are no null bytes, so we’re good there. I called attention the where we specified the port earlier 115c (4444). You can change the bytes here to the port you would like, keeping in mind that you can’t have any null bytes (00) and if you pick a port under 1024, you will likely need root to open the port. Additionally, I wrote a python wrapper for the shellcode that I will discuss later that can change the port for you.
Here I’ve loaded the shellcode into a piece of C code for testing shellcode, you can see the shellcode length is 103 bytes, a little bigger than it could be, but you could go through and clean some stuff up and make it smaller. You can also see that ./shellcode is listening on port 4444 like I specified in the shellcode. Finally, you can see that I am able to use netcat to connect to localhost on port 4444 and get a shell.
As I mentioned earlier, I wrote a python wrapper for changing to port on the shellcode, it is available on my GitHub. The python script takes a port (between 1025 and 65535) and replaces the original port (4444/115c) in the shell code with the specified port. Not much to it, but it’s there in case you don’t want to manually mess with the shellcode.