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:

  • :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

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.



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

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.



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

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.



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

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)


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

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.



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

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



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

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)


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

def self.exploit_type
  :exploit
end

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

Gets or sets the quality of the exploit.

Parameters:

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

    The optional new quality to set.

Returns:

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

    The exploit's quality.



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

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.



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

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.



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

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)


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

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.



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

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.



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

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



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

def build
end

#cleanupObject

This method is abstract.

Place holder method that cleans up after the exploit.

Since:

  • 1.0.0



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

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



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

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:



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

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

#launchObject

This method is abstract.

Place holder method that launches the exploit.

Since:

  • 1.0.0



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

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



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

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

#perform_buildObject

Builds the exploit.

Since:

  • 1.0.0



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

def perform_build
  build
end

#perform_cleanupObject

Cleans up the exploit.

Since:

  • 1.0.0



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

def perform_cleanup
  cleanup
end

#perform_launchObject

Launches the exploit.

Since:

  • 1.0.0



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

def perform_launch
  launch
end

#perform_testObject

Tests whether the target is vulnerable or not.

Since:

  • 1.0.0



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

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.



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

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



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

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



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

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

#validateObject

This method is abstract.

Place holder methods for additional validation logic.

Since:

  • 1.0.0



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

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



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

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