Today I want to review how to create a metasploit module. This process was entirely new to me, so I decided to start from scratch, using the Metasploit Unleashed site as a guide. My aim was to create an auxiliary scanner to look for Dropbox listeners running on the default ports of TCP/17500 and UDP/17500. I use Kali Linux, so all of my examples will reflect such.

Where to begin?

I decided to start by identifying the conditions my module would search for. Having just installed the Dropbox package for Debian - upon which Kali is based - I thought I should create a module to search for default Dropbox instances.

To check which ports I should be looking for, I issued the following commands:

~ $ netstat -anup |grep dropbox
udp    0  0 0.0.0.0:17500   0.0.0.0:* 4616/dropbox
and

~ $ netstat -antp |grep dropbox
tcp    0  0 0.0.0.0:17500  0.0.0.0:* LISTEN 4616/dropbox
...

Alright, this tells me I should be writing a scanning module to look for TCP and UDP port 17500 listeners. I’ve never written a module for Metasploit before, so I first need to find out where the existing ones are so I can get some ideas

Finding modules

To get an idea about how a Metasploit scanning module is written, I wanted to use an existing module as a starting point. But how did I find out where the existing modules were located?

I thought of a protocol I normally search for and which should be fairly straightfoward, so I settled on telnet. I started up the Metasploit console and searched for “telnet”:

~ $ msfconsole
...
msf > search telnet
...
auxiliary/scanner/telnet/telnet_version        normal    Telnet Service Banner Detection
...

Alright! This looks promising. I’m familiar enough with Metasploit to know that the modules follow a similar directory structure as they’re stored on the filesystem and that they’re written in Ruby. Back to bash:

~ $ locate auxiliary/scanner/telnet/telnet_version.rb
/usr/share/metasploit-framework/modules/auxiliary/scanner/tftp/tftpbrute.rb

Sane enough. Time to copy and modify for my own purposes:

~ $ cp /usr/share/metasploit-framework/modules/auxiliary/scanner/telnet/telnet_version.rb msfmodules/dropbox.rb
~ $ vi dropbox.rb

Note on Module Locations

Now is a good time to mention that the canonical directory for Metasploit modules in Kali Linux is under the /usr/share/metasploit-framework/ directory. Metasploit will also look in the user’s home folder in the ~/.msf4/ directory which makes things great for you and for me! We can write a scanner or other module, place it in our home directory, and not worry about it! Metasploit can merrily update itself with the latest modules from msfupdate and our custom modules won’t be touched!

For the remainder of this entry, the file I will be modifying is

~/.msf4/modules/auxiliary/scanner/misc/dropbox.rb

This means that when I go to use this scanner in Metasploit, I will type:

msf > use auxiliary/scanner/misc/dropbox

Oh, Crap…

Since this module was a template for my own purposes, I decided to leave as much of it intact as I thought I would need to accomplish my goal. After reading through the telnet_version.rb file, I decided that this was not the right information I needed. Ruby is new to me - as is writing Metasploit modules - and although it is similar to Python, it is just different enough that I realized I would have to build this from the ground up to really appreciate what was going on.

After some googling, I ended up where I should probably have checked at first: Metasploit Unleashed (Note: the Metasploit Development Reference was also extremely handy!)

So, backtracking, and with a blank file and a couple of references, I set about re-writing my Dropbox scanner.

No More UDP Scanning

Unfortunately, I decided not to scan for UDP port 17500 in this version of the scanner. UDP scanning is another learning endeavor for another time. Besides, as far as I know, it is not possible to change the ports Dropbox works on, although you can fiddle with proxy settings in the network proxy settings dialog

Yeah..

Feeling a bit disheartened that my scanner was reducing in what was already some basic functionality, I re-set about my task. Time to put on the thinking cap!

Any Metasploit module will include the following:

require 'msf/core'

This line is needed because the Metaploit core library is required by all modules, scanners, exploits, etc.

Next, the scanner needs to tell the Metasploit class that it is an Auxiliary subclass. For more information on what this means, see some guides on Ruby programming and the Metasploit API. I’m still learning this myself. Anyway, the line you’ll see in modules, and in this Auxilary module is:

class Metasploit3 < Msf::Auxiliary

Wonderful. Moving on, it is time to include the “mixins” that extend the ability of your Metasploit module by using some pre-built functionality that you can find in the Metasploit API.

The three mixins I used for this module were:

Msf::Exploit::Remote::Tcp # Used to attempt a TCP connection to the target
Msf::Auxiliary::Scanner   # Used to specify a range of hosts (RHOSTS) instead of a specific host (RHOST)
Msf::Auxiliary::Report    # Used to include host, port, and a label in the Metasploit database

Note: I found that the order of mixins is important! Originally, I declared the Scanner mixin before TCP, and my module was requiring both RHOSTS and RHOST. HD Moore explains that the options required by your module are dependent on the mixins you specify on Seclists.

Initialize

Next I defined the initialization of the module. I need to read up on exactly what the data structure “super” is, but its reminiscent of a Python tuple. It contains information about the module which is included when you run info in the Metasploit console.

def initialize
    super(
      'Name'        => 'Dropbox scanner',
      'Version'     => '$Revision: 1 $',
      'Description' => 'This module scans for a dropbox listener on TCP port 17500',
      'Author'      => '@Dagorim (http://www.dagorim.com)',
      'License'     => MSF_LICENSE

The “register_options” data structure (array with a class reference?) allows you to specify what the user will be presented with when she types __show options__ in the Metasploit console. I want the default port option to be 17500, so I specify it by setting it thus:

register_options(
      [
        Opt::RPORT(17500)
      ], self.class)

Scanning

To perform the actual scanning, I define a function to take the ip address passed in by RHOSTS, all handled by Metasploit behind the scenes, and only print a status message if the Msf::Exploit::Remote::Tcp class was actually able to open a connection to it. The report_service function will add any hosts to the current workspace and add Dropbox as a service associated with port 17500:

def run_host(ip)
  begin
    if(connect)
      print_status("#{ip}:#{rport} DROPBOX")
      report_service(:host => rhost, :port => rport, :name => "Dropbox")
      disconnect
    end
  end

Error Handling

Handling connection or general errors in this module will prevent Metasploit from spitting out verbose messages to the user, muddying actual results. This is my initial method of handling a general connection error and any raised exceptions:

rescue ::Rex::ConnectionError
rescue ::Exception => e
    print_error("#{e},#{e.backtrace}")

The final version

Putting everything together, here is the Dropbox scanner, version 1:

require 'msf/core'

class Metasploit3 < Msf::Auxiliary
  include Msf::Exploit::Remote::Tcp
  include Msf::Auxiliary::Scanner
  include Msf::Auxiliary::Report

  def initialize
    super(
      'Name'        => 'Dropbox scanner',
      'Version'     => '$Revision: 1 $',
      'Description' => 'This module scans for a dropbox listener on TCP port 17500',
      'Author'      => '@Dagorim (http://www.dagorim.com)',
      'License'     => MSF_LICENSE
    )

    register_options(
      [
        Opt::RPORT(17500)
      ], self.class)
  end

  def run_host(ip)
    begin
      if(connect)
        print_status("#{ip}:#{rport} DROPBOX")
        report_service(:host => rhost, :port => rport, :name => "Dropbox")
        disconnect
      end
    end
    rescue ::Rex::ConnectionError
    rescue ::Exception => e
        print_error("#{e},#{e.backtrace}")
  end
end

And that completes it! There are likely several bugs in this, but for my purposes, it worked. I think it speaks to the power and efficiency of both the Metasploit framework and Ruby that I was able to approach this topic and, in just under four hours, have a working scanner built from the ground up.