Class: Ronin::Exploits::Exploit

Inherits:
Object
  • Object
show all
Includes:
Core::Metadata::Authors, Core::Metadata::Description, Core::Metadata::ID, Core::Metadata::References, Core::Metadata::Summary, Core::Params::Mixin, Support::CLI::Printing
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 include additional mixin modules to add additional functionality, such as defining targets or loading in a payload.

Philosophy

Exploits are just programs with steps to build and launch the exploit. Exploits also typically contain metadata that describes the exploit's author(s), release date, what the exploit does, etc.

The Exploit class defines six key parts:

  1. Metadata - defines information about the exploit.
  2. Params - user configurable parameters.
  3. test - optional method that tests whether the target is vulnerable or not.
  4. build - method which builds the exploit.
  5. launch - method which launches the exploit.
  6. cleanup - optional Method which performs additional cleanup steps.

Example

require 'ronin/exploits/exploit'
require 'ronin/exploits/mixins/remote_tcp'

module Ronin
  module Exploits
    class MyExploit < Exploit

      include Mixins::RemoteTCP

      register 'my_exploit'

      summary 'My first exploit'
      description <<~EOS
        This is my first exploit.
        Bla bla bla bla.
      EOS

      author '...'
      author '...', email: '...', twitter: '...'

      disclosure_date 'YYY-MM-DD'
      release_date 'YYYY-MM-DD'

      advisory 'CVE-YYYY-NNNN'
      advisory 'GHSA-XXXXXX'
      software 'TestHTTP'
      software_versions '1.0.0'..'1.5.4'

      param :cmd, desc: 'The command to run'

      def test
        # ...
      end

      def build
        # ...
      end

      def launch
        # ...
      end

      def cleanup
        # ...
      end

    end
  end
end

register

Registers the exploit with Ronin::Exploits.

register 'my_exploit'

quality

Defines the quality level of the exploit. Accepted values are:

  • :untested
  • :testing
  • :poc
  • :weaponized

    quality :poc

summary

Defines a short one-sentence description of the exploit.

summary 'My first exploit'

description

Defines a longer multi-paragraph description of the exploit.

description <<~EOS
  This is my first exploit.
  Bla bla bla bla.
EOS

Note: that <<~ heredoc, unlike the regular << heredoc, removes leading whitespace.

author

Add an author's name and additional information to the exploit.

author 'John Smith'

author 'doctor_doom', email: '...', twitter: '...'

software

Defines the software which the exploit targets.

software 'TestApp'

software_versions

Defines the software versions which the exploit targets:

software_versions %w[
  1.0.0
  1.0.1
  1.0.2
  1.1.0
]

software_versions '1.0.0'..'1.5.4'

param

Defines a user configurable param. Params may have a type class, but default to String. Params must have a one-line description.

param :str, desc: 'A basic string param'

param :feature_flag, Boolean, desc: 'A boolean param'

param :enum, Enum[:one, :two, :three],
             desc: 'An enum param'

param :num1, Integer, desc: 'An integer param'

param :num2, Integer, default: 42,
                     desc: 'A param with a default value'

param :num3, Integer, default: ->{ rand(42) },
                      desc: 'A param with a dynamic default value'

param :float, Float, 'Floating point param'

param :url, URI, desc: 'URL param'

param :pattern, Regexp, desc: 'Regular Expression param'

Params may then be accessed in instance methods using params Hash.

param :padding, Integer, desc: 'Amount of additional padding'

def build
  # ...

  if params[:padding]
    @buffer << 'A' * params[:padding]
  end
end

test

The method which may define tests which confirm whether the target is vulnerable. The method must return a Vulnerable, NotVulnerable, or an #Unknown object.

def test
  case http.get_body('/')
  when /Powered by Foo 4\.19\./
    Vulnerable('host is vulnerable')
  when /Powered by Foo 4\.2[0-9]\./
    NotVulnerable('host is patched')
  else
    Unknown('cannot determine whether the host is vulnerable or not')
  end
end

build

The method which defines the logic that builds the exploit before launching it.

def build
  @buffer = "..."
  @buffer << "..."
end

launch

The method which launches the built exploit against the target.

def launch
  @socket = tcp_connect do |socket|
    socket.write(@buffer)
  end
end

cleanup

The method which defines additional cleanup tasks after the exploit has successfully launched and any post-exploitation tasks have been completed.

def cleanup
  @socket.close
end

Direct Known Subclasses

CommandInjection, MemoryCorruption, Web

Exploit API Methods collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(**kwargs) ⇒ Exploit

Initializes the exploit.

Parameters:

  • kwargs (Hash{Symbol => Object})

    Additional keyword arguments.

Options Hash (**kwargs):

  • :params (Hash{Symbol => Object})

    The param values for the exploit.



451
452
453
# File 'lib/ronin/exploits/exploit.rb', line 451

def initialize(**kwargs)
  super(**kwargs)
end

Class Method Details

.advisoriesSet<Advisory>

The advisory IDs for the exploit.

Returns:

  • (Set<Advisory>)

    The set of advisories for the exploit.



366
367
368
# File 'lib/ronin/exploits/exploit.rb', line 366

def self.advisories
  @advisories ||= Set.new
end

.advisory(id, url = Advisory.url_for(id)) ⇒ Object

Adds an advisory for the exploit.

Parameters:

  • id (String)

    The advisory ID.

  • url (String) (defaults to: Advisory.url_for(id))

    The optional advisory URL. If the advisory id begins with CVE- or GHSA-, then the URL will automatically be derived from the id.



382
383
384
# File 'lib/ronin/exploits/exploit.rb', line 382

def self.advisory(id,url=Advisory.url_for(id))
  advisories << Advisory.new(id,url)
end

.disclosed?Boolean

Determines whether the exploit has been disclosed yet.

Returns:

  • (Boolean)


354
355
356
# File 'lib/ronin/exploits/exploit.rb', line 354

def self.disclosed?
  !disclosure_date.nil?
end

.disclosure_date(new_date = nil) ⇒ Date?

Gets or sets the disclosure date for the exploit.

Examples:

disclosure_date '2022-04-20'

Parameters:

  • new_date (String, nil) (defaults to: nil)

    The optional new disclosure date to set.

Returns:

  • (Date, nil)

    The exploit's disclosure date.



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

def self.disclosure_date(new_date=nil)
  if new_date then @disclosure_date = Date.parse(new_date)
  else             @disclosure_date
  end
end

.exploit(**kwargs) {|exploit| ... } ⇒ Exploit

Initializes and runs the exploit.

Parameters:

  • kwargs (Hash{Symbol => Object})

    Additional keyword arguments for #initialize.

Yields:

  • (exploit)

    If a block is given, it will be yielded the exploit after it has been launched. Once the block has returned, #cleanup will automatically be called.

Yield Parameters:

  • exploit (Exploit)

    The launched exploit.

Returns:

  • (Exploit)

    The launched exploit.

Since:

  • 1.0.0



474
475
476
# File 'lib/ronin/exploits/exploit.rb', line 474

def self.exploit(**kwargs,&block)
  new(**kwargs).exploit(&block)
end

.exploit_typeSymbol

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Note:

This is used internally to map an exploit class to a printable type.

Returns the type or kind of exploit.

Returns:

  • (Symbol)


438
439
440
# File 'lib/ronin/exploits/exploit.rb', line 438

def self.exploit_type
  :exploit
end

.quality(new_quality = nil) ⇒ :untested, ...

Gets or sets the quality of the exploit.

Parameters:

  • new_quality (:untested, :testing, :poc, :weaponized, nil) (defaults to: nil)

    The optional new quality to set.

Returns:

  • (:untested, :testing, :poc, :weaponized, nil)

    The exploit's quality.



301
302
303
304
305
# File 'lib/ronin/exploits/exploit.rb', line 301

def self.quality(new_quality=nil)
  if new_quality then @new_quality = new_quality
  else                @new_quality
  end
end

.register(exploit_id) ⇒ Object

Registers the exploit with the given name.

Examples:

register 'my_exploit'

Parameters:

  • exploit_id (String)

    The exploit's id.



285
286
287
288
# File 'lib/ronin/exploits/exploit.rb', line 285

def self.register(exploit_id)
  id(exploit_id)
  Exploits.register(exploit_id,self)
end

.release_date(new_date = nil) ⇒ Date?

Gets or sets the release date for the exploit.

Parameters:

  • new_date (String, nil) (defaults to: nil)

    The optional new release date to set.

Returns:

  • (Date, nil)

    The exploit's release date.



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

def self.release_date(new_date=nil)
  if new_date then @release_date = Date.parse(new_date)
  else             @release_date
  end
end

.released?Boolean

Determines whether the exploit has been publicly released yet.

Returns:

  • (Boolean)


327
328
329
# File 'lib/ronin/exploits/exploit.rb', line 327

def self.released?
  !release_date.nil?
end

.software(new_software = nil) ⇒ String?

Gets or sets the software which the exploit targets.

Parameters:

  • new_software (String, nil) (defaults to: nil)

    the optional new software name to set.

Returns:

  • (String, nil)

    The name of the software which the exploit targets.



397
398
399
400
401
402
403
404
405
# File 'lib/ronin/exploits/exploit.rb', line 397

def self.software(new_software=nil)
  if new_software
    @software = new_software
  else
    @software ||= if superclass < Exploit
                    superclass.software
                  end
  end
end

.software_versions(new_software_versions = nil) ⇒ Array<String>, ...

Gets or sets the software version(s) which the exploit targets.

Parameters:

  • new_software_versions (Array<String>, Range<String,String>, nil) (defaults to: nil)

    the optional new software version(s) to set.

Returns:

  • (Array<String>, Range<String,String>, nil)

    The name of the software version which the exploit targets.



418
419
420
421
422
423
424
425
426
# File 'lib/ronin/exploits/exploit.rb', line 418

def self.software_versions(new_software_versions=nil)
  if new_software_versions
    @software_versions = new_software_versions
  else
    @software_versions ||= if superclass < Exploit
                             superclass.software_versions
                           end
  end
end

Instance Method Details

#buildObject

This method is abstract.

Place holder method that builds the exploit.

Since:

  • 1.0.0



679
680
# File 'lib/ronin/exploits/exploit.rb', line 679

def build
end

#cleanupObject

This method is abstract.

Place holder method that cleans up after the exploit.

Since:

  • 1.0.0



703
704
# File 'lib/ronin/exploits/exploit.rb', line 703

def cleanup
end

#exploit(dry_run: false) {|exploit| ... } ⇒ Exploit

Builds the exploit and then launches the exploit.

Parameters:

  • dry_run (Boolean) (defaults to: false)

    If true performs a dry-run by only calling #build and not launching the exploit.

Yields:

  • (exploit)

    If a block is given, it will be yielded the exploit after it has been launched. Once the block has returned, #cleanup will automatically be called.

Yield Parameters:

  • exploit (Exploit)

    The launched exploit.

Returns:

  • (Exploit)

    The launched exploit.

Since:

  • 1.0.0



560
561
562
563
564
565
566
567
568
569
570
571
572
573
# File 'lib/ronin/exploits/exploit.rb', line 560

def exploit(dry_run: false)
  perform_build

  unless dry_run
    perform_launch

    if block_given?
      yield self
      perform_cleanup
    end
  end

  return self
end

#fail(message) ⇒ Object

Indicates that the exploit has failed.

Parameters:

  • message (String)

    The failure message.

Raises:



714
715
716
# File 'lib/ronin/exploits/exploit.rb', line 714

def fail(message)
  raise(ExploitFailed,message)
end

#launchObject

This method is abstract.

Place holder method that launches the exploit.

Since:

  • 1.0.0



691
692
# File 'lib/ronin/exploits/exploit.rb', line 691

def launch
end

#NotVulnerable(message) ⇒ TestResult::NotVulnerable

Returns a not vulnerable test result for the #test method.

Examples:

def test
  # ...
  return NotVulnerable("the host is not vulnerable")
  # ...
end

Returns:

Since:

  • 1.0.0



623
624
625
# File 'lib/ronin/exploits/exploit.rb', line 623

def NotVulnerable(message)
  TestResult::NotVulnerable.new(message)
end

#perform_buildObject

Builds the exploit.

Since:

  • 1.0.0



512
513
514
# File 'lib/ronin/exploits/exploit.rb', line 512

def perform_build
  build
end

#perform_cleanupObject

Cleans up the exploit.

Since:

  • 1.0.0



534
535
536
# File 'lib/ronin/exploits/exploit.rb', line 534

def perform_cleanup
  cleanup
end

#perform_launchObject

Launches the exploit.

Since:

  • 1.0.0



523
524
525
# File 'lib/ronin/exploits/exploit.rb', line 523

def perform_launch
  launch
end

#perform_testObject

Tests whether the target is vulnerable or not.

Since:

  • 1.0.0



501
502
503
# File 'lib/ronin/exploits/exploit.rb', line 501

def perform_test
  test
end

#perform_validateObject

Validates that the exploit is ready to be used.

Raises:

  • (Ronin::Core::Params::RequiredParam)

    One of the required params was not set.

  • (ValidationError)

    Another kind of validation error occurred.



489
490
491
492
# File 'lib/ronin/exploits/exploit.rb', line 489

def perform_validate
  validate_params
  validate
end

#testTest::Vulnerable, ...

This method is abstract.

Place holder method for testing whether the target is vulnerable.

Examples:

def test
  case http.get_body('/')
  when /Powered by Foo 4\.19\./
    Vulnerable('host is vulnerable')
  when /Powered by Foo 4\.2[0-9]\./
    NotVulnerable('host is patched')
  else
    Unknown('cannot determine whether the host is vulnerable or not')
  end
end

Returns:

  • (Test::Vulnerable, Test::NotVulnerable, Test::Unknown)

Since:

  • 1.0.0



666
667
668
# File 'lib/ronin/exploits/exploit.rb', line 666

def test
  Unknown("no vulnerability testing logic defined")
end

#Unknown(message) ⇒ TestResult::Unknown

Returns an unknown test result for the #test method.

Examples:

def test
  # ...
  return Unknown("cannot determine whether the host is vulnerable")
  # ...
end

Returns:

Since:

  • 1.0.0



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

def Unknown(message)
  TestResult::Unknown.new(message)
end

#validateObject

This method is abstract.

Place holder methods for additional validation logic.

Since:

  • 1.0.0



588
589
# File 'lib/ronin/exploits/exploit.rb', line 588

def validate
end

#Vulnerable(message) ⇒ TestResult::Vulnerable

Returns a vulnerable test result for the #test method.

Examples:

def test
  # ...
  return Vulnerable("the host is vulnerable")
  # ...
end

Returns:

Since:

  • 1.0.0



605
606
607
# File 'lib/ronin/exploits/exploit.rb', line 605

def Vulnerable(message)
  TestResult::Vulnerable.new(message)
end