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.



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

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.



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

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.



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

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)


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

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.



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

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



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

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)


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

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.



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

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.



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

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.



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

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)


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

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.



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

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.



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

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



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

def build
end

#cleanupObject

This method is abstract.

Place holder method that cleans up after the exploit.

Since:

  • 1.0.0



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

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



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

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:



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

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

#launchObject

This method is abstract.

Place holder method that launches the exploit.

Since:

  • 1.0.0



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

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



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

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

#perform_buildObject

Builds the exploit.

Since:

  • 1.0.0



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

def perform_build
  build
end

#perform_cleanupObject

Cleans up the exploit.

Since:

  • 1.0.0



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

def perform_cleanup
  cleanup
end

#perform_launchObject

Launches the exploit.

Since:

  • 1.0.0



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

def perform_launch
  launch
end

#perform_testObject

Tests whether the target is vulnerable or not.

Since:

  • 1.0.0



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

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.



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

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



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

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



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

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

#validateObject

This method is abstract.

Place holder methods for additional validation logic.

Since:

  • 1.0.0



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

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



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

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