9 min read

(For more resources related to this topic, see here.)

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
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.

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::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)"
case response.headers['Server']
when /Win32/, /(Windows/, /IIS/
os = "Windows"
when /Apache//
os = "*Nix"
os = "Unknown Server Header Reporting: "+response.headers['Server']
return os
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)
res = send_request_raw({'uri' => '/', 'method' => 'GET' })
return if not res os_info=os_fingerprint(res)
fp = http_fingerprint(res)
print_status("#{ip}:#{rport} is running
#{fp} version And Is Powered By:
#{pb} Running On #{os_info}") 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 [*] 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 def initialize super(
'Name' => 'Drive Disabler Module',
'Description' => 'C Drive Disabler Module',
'License' => MSF_LICENSE,
'Author' => 'Nipun Jaswal' )

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
print_line("Disabling C Drive") meterpreter_registry_setvaldata
print_line("Setting No Drives For C") meterpreter_registry_setvaldata
print_line("Removing View On The Drive") print_line("Disabled C Drive")
end end

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.

Subscribe to the weekly Packt Hub newsletter

* indicates required


Please enter your comment!
Please enter your name here