Mastering Metasploit
上QQ阅读APP看书,第一时间看更新

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-x64\bin>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 + "\t"

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