Class: Ronin::Exploits::Exploit

Inherits:
Object
  • Object
show all
Includes:
Behaviors::Buildable, Behaviors::Deployable, Behaviors::Testable, Tests, Payloads::HasPayload, PostExploitation::Mixin, Script
Defined in:
lib/ronin/exploits/exploit.rb

Overview

The Exploit class allows for describing exploits for security vulnerabilities, purely in Ruby. Exploits contain metadata about the exploit/vulnerability and methods which defines the functionality of the exploit. Exploits may also be coupled with a payload, which can be used with the exploit.

Metadata

An Exploit is described via metadata, which is cached into the Ronin Database. The cacheable metadata must be defined within a cache block, so that the metadata is set only before the exploit is cached:

cache do
  self.name = 'FailTTPd 0.2.6b Buffer Overflow'
  self.version = '0.2'
  self.description = %{
    FailHTTPd 0.2.6b contains a buffer overflow in it's handling
    of the TRACE request.
  }

  # ...
end

License

An Exploit may associate with a specific software license using the licensed_under method:

cache do
  # ...

  licensed_under :cc_sa_by
end

Authors

An Exploit may have one or more authors which contributed to the exploit, using the author method:

cache do
  # ...

  author name: 'evoltech', organization: 'HackBloc'
  author name: 'postmodern', organization: 'SophSec'
end

Status

An Exploit has a specific development status, which describes the reliability of the exploit:

cache do
  # ...

  self.status = :proven
end

The #status property may be one of the following values:

  • potential
  • proven
  • weaponized

Disclosure

An Exploit may specify whether it has been released publically.

cache do
  # ...

  self.released = true
end

#released defaults to false.

An Exploit may also specify whether it has been reported.

cache do
  # ...

  self.released = false
  self.reported = true
end

#reported defaults to false.

Advisories

An Exploit may list the advisories that describe the vulnerability.

cache do
  # ...

  advisory 'CVE-2011-1234',
           'OSVDB-4567'
end

Targets

An Exploit may have one or more targets, which discribe the Architectures, Operating Systems and Products the exploit targets. Each target may also define extra data that is specific to that target.

cache do
  # ...

  targeting do |t|
    t.arch! :i686
    t.os! name: 'Linux'
    t.software! name: 'FailTTPd', version: '0.2.6b'

    # target specific data
    t.data = {value1: 0x03c1, value2: 0xa0}
  end
end

Methods

The functionality of an Exploit is defined by four main methods:

  • build - Handles building the exploit.
  • test - Optional method which handles testing a built exploit, before it is deployed.
  • deploy - Handles deploying a built and tests exploit against a host.
  • evacuate - Handles cleaning up after a deployed exploit.

The build, test, deploy, evacuate methods can be invoked individually using the build!, test!, deploy!, evacuate! methods, respectively. Additionally, the exploit! method will accept additional parameter values and will call build!, test!, deploy!, in that order.

Exploit/Payload Coupling

An Exploit may also couple with a payload, which will be built, tested and deployed along with the exploit. When a payload is coupled with an exploit, the payload instance variable of the exploit will contain the new payload.

To use a cached payload, from the Ronin Database, simply use the Payloads::HasPayload#use_payload! method:

exploit.use_payload!(name.like => '%Bind Shell%')

In order to use a payload, loaded directly from a file, call the Payloads::HasPayload#use_payload_from! method:

exploit.use_payload_from!('path/to/my_payload.rb')

Before an exploit is built, the payload will be built and encoded. The built payload will be stored in the #raw_payload instance variable.

After an exploit is deployed, the payload will also be deployed, ensuring that any post-deploy functionality is triggered. Before the exploit is cleaned up after, the payload will be cleaned up.

Direct Known Subclasses

Local, Remote

Instance Attribute Summary collapse

Attributes included from Payloads::HasPayload

#payload

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Tests

#is_restricted?, #test_arch!, #test_os!, #test_restricted!, #test_software!, #test_target!

Methods included from PostExploitation::Mixin

#fs, #post_exploitation, #process, #resources, #shell

Methods included from Payloads::HasPayload

#default_payload, #method_missing, #payload_class, #respond_to?, #use_payload!, #use_payload_from!

Constructor Details

#initialize(attributes = {}) ⇒ Exploit

Creates a new Exploit object.


268
269
270
271
272
273
274
275
276
# File 'lib/ronin/exploits/exploit.rb', line 268

def initialize(attributes={})
  super(attributes)

  @helpers = Set[]

  @target = nil
  @restricted_chars = Chars::CharSet.new
  @encoders = []
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Ronin::Payloads::HasPayload

Instance Attribute Details

#encodersObject (readonly)

Encoders to run on the payload


256
257
258
# File 'lib/ronin/exploits/exploit.rb', line 256

def encoders
  @encoders
end

#helpersObject (readonly)

The helpers used by the exploit


247
248
249
# File 'lib/ronin/exploits/exploit.rb', line 247

def helpers
  @helpers
end

#raw_payloadObject

The raw unencoded payload


259
260
261
# File 'lib/ronin/exploits/exploit.rb', line 259

def raw_payload
  @raw_payload
end

#restricted_charsObject (readonly)

Characters to restrict


253
254
255
# File 'lib/ronin/exploits/exploit.rb', line 253

def restricted_chars
  @restricted_chars
end

#targetTarget


640
641
642
# File 'lib/ronin/exploits/exploit.rb', line 640

def target
  @target ||= self.targets.first
end

Class Method Details

.advisory(*identifiers) ⇒ DataMapper::Collection<Ronin::Exploits::Exploit>

Searches for exploits for the given advisories.

Examples:

Exploit.advisory('CVE-2011-1234', 'OSVDB-1234')

Since:

  • 1.0.0


292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/ronin/exploits/exploit.rb', line 292

def self.advisory(*identifiers)
  query = all

  identifiers.each do |identifier|
    publisher, year, number = Advisory.split(identifier)

    query |= all(
      'advisories.publisher' => publisher,
      'advisories.year'      => year,
      'advisories.number'    => number
    )
  end

  return query
end

.targeting_arch(name) ⇒ Array<Exploit>

Finds all exploits which target a given architecture.


317
318
319
# File 'lib/ronin/exploits/exploit.rb', line 317

def self.targeting_arch(name)
  all('targets.arch.name' => name.to_s)
end

.targeting_os(name) ⇒ Array<Exploit>

Finds all exploits which target a given OS.


330
331
332
# File 'lib/ronin/exploits/exploit.rb', line 330

def self.targeting_os(name)
  all('targets.os.name' => name.to_s)
end

.targeting_software(name) ⇒ Array<Exploit>

Finds all exploits which target a given software.


343
344
345
# File 'lib/ronin/exploits/exploit.rb', line 343

def self.targeting_software(name)
  all('targets.software.name.like' => "%#{name}%")
end

Instance Method Details

#advisory(*identifiers) ⇒ DataMapper::Collection<Ronin::Advisory>

Adds a new reference to an Advisory.

Examples:

advisory 'CVE-2011-1234',
         'OSVDB-1234'

Raises:

  • (ArgumentError)

    The publisher name for the advisory was unknown.

Since:

  • 1.0.0


365
366
367
368
369
370
371
# File 'lib/ronin/exploits/exploit.rb', line 365

def advisory(*identifiers)
  identifiers.each do |identifier|
    self.advisories << Advisory.parse(identifier)
  end

  return self.advisories
end

#archArch


648
649
650
# File 'lib/ronin/exploits/exploit.rb', line 648

def arch
  target.arch if target
end

#build!(options = {}, &block) ⇒ Object

Builds the exploit and checks for restricted characters or patterns.

See Also:

  • build

773
774
775
776
777
778
# File 'lib/ronin/exploits/exploit.rb', line 773

def build!(options={},&block)
  build_payload!(options)
  encode_payload!

  super(options,&block)
end

#build_payload!(options = {}) ⇒ String

Builds the current payload, saving the result to the @raw_payload instance variable.

See Also:

  • Payload#build!

Since:

  • 0.3.0


726
727
728
729
730
731
732
733
734
735
736
737
# File 'lib/ronin/exploits/exploit.rb', line 726

def build_payload!(options={})
  if @payload
    @raw_payload = ''

    @payload.build!(options)
    @raw_payload = @payload.raw_payload
  else
    @raw_payload ||= ''
  end

  return @raw_payload
end

#deploy! {|exploit| ... } ⇒ Exploit

Verifies then deploys the exploit. If a payload has been set, the payload will also be deployed.

Yields:

  • (exploit)

    If a block is given, it will be passed the deployed exploit.

Yield Parameters:

  • exploit (Exploit)

    The deployed exploit.

Raises:

  • (ExploitNotBuilt)

    The exploit has not been built, and cannot be deployed.

See Also:

  • deploy

798
799
800
801
802
803
# File 'lib/ronin/exploits/exploit.rb', line 798

def deploy!(&block)
  super do
    @payload.deploy!() if @payload
    yield self if block_given?
  end
end

#encode_payload(encoder = nil) {|payload| ... } ⇒ Array

Adds a new encoder to the list of encoders to use for encoding the payload.

Examples:

exploit.encode_payload(some_encoder)
exploit.encode_payload do |payload|
  # ...
end

Yields:

  • (payload)

    If a block is given, and an encoder object is not, the block will be used to encode the payload.

Yield Parameters:

  • payload (String)

    The payload to be encoded.

Raises:

  • (ArgumentError)

    The payload encoder object does not provide an encode method. Either a payload encoder object or a block can be given.


442
443
444
445
446
447
448
449
450
451
452
453
454
# File 'lib/ronin/exploits/exploit.rb', line 442

def encode_payload(encoder=nil,&block)
  if encoder
    unless encoder.respond_to?(:encode)
      raise(ArgumentError,"The payload encoder must provide an encode method")
    end

    @encoders << encoder
  elsif (encoder.nil? && block)
    @encoders << block
  else
    raise(ArgumentError,"either a payload encoder or a block can be given")
  end
end

#encode_payload!String

Encodes the current payload and makes the result available via the #raw_payload instance variable.


746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
# File 'lib/ronin/exploits/exploit.rb', line 746

def encode_payload!
  @raw_payload = @raw_payload.to_s

  @encoders.each do |encoder|
    print_debug "Encoding payload: #{@raw_payload.dump}"

    new_payload = if encoder.respond_to?(:encode)
                    encoder.encode(@raw_payload)
                  elsif encoder.respond_to?(:call)
                    encoder.call(@raw_payload)
                  end

    @raw_payload = (new_payload || @raw_payload).to_s
  end

  return @raw_payload
end

#evacuate! {|exploit| ... } ⇒ Exploit

Cleans up after the deployed exploit. If a payload has been set, the payload will be cleaned up before the exploit.

Yields:

  • (exploit)

    If a block is given, it will be passed the exploit before it has been cleaned up.

Yield Parameters:

  • exploit (Exploit)

    The exploit before it has been cleaned up.

Since:

  • 1.0.0


821
822
823
824
825
826
# File 'lib/ronin/exploits/exploit.rb', line 821

def evacuate!
  super do
    @payload.evacuate!() if @payload
    yield self if block_given?
  end
end

#exploit!(options = {}) {|exploit| ... } ⇒ Exploit

Builds, tests and then deploys the exploit.

Options Hash (options):

  • :dry_run (Boolean) — default: false

    Specifies whether to do a dry-run of the exploit, where the exploit will be built, tested but not deployed.

Yield Parameters:

  • exploit (Exploit)

    The deployed exploit.

Since:

  • 0.3.0


849
850
851
852
853
854
855
856
857
# File 'lib/ronin/exploits/exploit.rb', line 849

def exploit!(options={},&block)
  build!(options)

  unless options[:dry_run]
    deploy!(&block)
  end

  return self
end

#helper(name) ⇒ Boolean (protected)

Loads a helper module from ronin/exploits/helpers and extends the exploit with it.

Examples:

helper :buffer_overflow

Raises:

  • (UnknownHelper)

    No valid helper module could be found or loaded with the similar name.


880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
# File 'lib/ronin/exploits/exploit.rb', line 880

def helper(name)
  name = name.to_sym

  return false if @helpers.include?(name)

  unless (helper_module = Helpers.require_const(name))
    raise(UnknownHelper,"unknown helper #{name}")
  end

  unless helper_module.kind_of?(Module)
    raise(UnknownHelper,"unknown helper #{name}")
  end

  @helpers << name
  extend helper_module
  return true
end

#osOS


656
657
658
# File 'lib/ronin/exploits/exploit.rb', line 656

def os
  target.os if target
end

#payload=(new_payload) ⇒ Payload

Associates a payload with the exploit, and the exploit with the payload.

Since:

  • 0.3.0


680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
# File 'lib/ronin/exploits/exploit.rb', line 680

def payload=(new_payload)
  if (@payload && new_payload.nil?)
    @payload.exploit = nil
  end

  super(new_payload)

  if @payload
    print_info "Using payload: #{new_payload}"

    @payload.exploit = self
  end

  return @payload
end

#restrict(*chars) ⇒ Array<String>

Adds new characters to the list of restricted characters.

Examples:

restrict 0x00, "\n"
# => #<Chars::CharSet: {"\0", "\n"}>

408
409
410
# File 'lib/ronin/exploits/exploit.rb', line 408

def restrict(*chars)
  @restricted_chars += chars
end

#softwareSoftware


664
665
666
# File 'lib/ronin/exploits/exploit.rb', line 664

def software
  target.software if target
end

#targeting(attributes = {}) {|target| ... } ⇒ Object

Adds a new target to the exploit.

Examples:

targeting do |t|
  t.arch! :i686
  t.os! name: 'Linux'
end

Yields:

  • (target)

    If a block is given, it will be passed the newly created target.

Yield Parameters:

  • target (Target)

    The newly created target.


391
392
393
# File 'lib/ronin/exploits/exploit.rb', line 391

def targeting(attributes={},&block)
  self.targets << self.targets.model.new(attributes,&block)
end

#targeting_arch(arch) ⇒ Array<Target>

Finds the targets for the specific Architecture.

Since:

  • 1.0.0


469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
# File 'lib/ronin/exploits/exploit.rb', line 469

def targeting_arch(arch)
  selector = case arch
             when Arch
               lambda { |target| target.arch == arch }
             when Symbol
               unless Arch.methods(false).include?(arch)
                 raise(ArgumentError,"unknown arch: #{arch}")
               end

               arch = Arch.send(arch)

               lambda { |target| target.arch == arch }
             else
               arch = arch.to_s

               lambda { |target| target.arch.name == arch }
             end

  self.targets.select(&selector)
end

#targeting_os(os, version = nil) ⇒ Array<Target>

Finds the targets for the specific Operating System.

Since:

  • 1.0.0


506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
# File 'lib/ronin/exploits/exploit.rb', line 506

def targeting_os(os,version=nil)
  selector = case os
             when OS
               lambda { |target| target.os == os }
             when Symbol
               unless OS.methods(false).include?(os)
                 raise(ArgumentError,"unknown os: #{os}")
               end

               os = OS.send(os)

               lambda { |target| target.os == os }
             else
               os = os.to_s

               if version
                 version = version.to_s

                 lambda { |target|
                   (target.os.name == os) &&
                   (target.os.version == version)
                 }
               else
                 lambda { |target| target.os.name == os }
               end
             end

  self.targets.select(&selector)
end

#targeting_software(software, version = nil) ⇒ Array<Target>

Finds the targets for the specific Software.

Since:

  • 1.0.0


552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
# File 'lib/ronin/exploits/exploit.rb', line 552

def targeting_software(software,version=nil)
  selector = case software
             when Software
               lambda { |target| target.software == software }
             else
               software = software.to_s

               if version
                 version = version.to_s

                 lambda { |target|
                   target.software.name.include?(software) &&
                   (target.software.version == version)
                 }
               else
                 lambda { |target|
                   target.software.name.include?(software)
                 }
               end
             end

  self.targets.select(&selector)
end

#use_target!(options = {}) ⇒ Target

Selects a target to use in exploitation.

Examples:

Targetting an Architecture

use_target! arch: :i686

Targetting an Operating System

use_target! os: 'Linux'

Targetting an Operating System and version

use_target! os: ['Linux', '2.6.24']

Targetting the Software

use_target! software: 'Lucene'

Targetting the Software name and version

use_target! software: ['Lucene', '1.8.0']

Options Hash (options):

  • :arch (Arch, Hash, Symbol, String)

    The targeted Architecture.

  • :os (OS, Hash, Array<String, String>, Symbol, String)

    The targeted Operating System.

  • :software (Software, Hash, Array<String, String>, String)

    The targeted Software.

Raises:

Since:

  • 0.3.0


614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
# File 'lib/ronin/exploits/exploit.rb', line 614

def use_target!(options={})
  query = self.targets

  if options[:arch]
    query = (query & targeting_arch(options[:arch]))
  end

  if options[:os]
    query = (query & targeting_os(*options[:os]))
  end

  if options[:software]
    query = (query & targeting_software(*options[:software]))
  end

  unless (@target = query.first)
    raise(TargetUnspecified,"could not find any matching targets")
  end

  return @target
end