Method: Arachni::RPC::Server::Instance#scan

Defined in:
lib/arachni/rpc/server/instance.rb

#scan(opts = {}, &block) ⇒ Object

Note:

Options marked with an asterisk are required.

Note:

Options which expect patterns will interpret their arguments as regular expressions regardless of their type.

Configures and runs a scan.

Parameters:

  • opts (Hash) (defaults to: {})

    Scan options to be passed to Options#update (along with some extra ones to keep configuration in one place).

    _The options presented here are the most commonly used ones, in actuality, you can use anything supported by Options#update._

Options Hash (opts):

  • *:url (String)

    Target URL to audit.

  • :authorized_by (String) — default: nil

    The e-mail address of the person who authorized the scan.

    john.doe@bigscanners.com
    
  • :audit (Hash)

    Audit options.

  • :scope (Hash)

    Scope options.

  • :http (Hash)

    HTTP options.

  • :login (Hash)

    Session options.

  • :checks (String, Array<String>) — default: []

    Checks to load, by name.

    # To load all checks use the wildcard on its own
    '*'
    
    # To load all XSS and SQLi checks:
    [ 'xss*', 'sql_injection*' ]
    
  • :plugins (Hash<Hash>) — default: {}

    Plugins to load, by name, along with their options.

    {
        'proxy'      => {}, # empty options
        'autologin'  => {
            'url'         => 'http://demo.testfire.net/bank/login.aspx',
            'parameters' => 'uid=jsmith&passw=Demo1234',
            'check'       => 'MY ACCOUNT'
        },
    }
    
  • :platforms (String, Symbol, Array<String, Symbol>) — default: []

    Initialize the fingerprinter with the given platforms.

    The fingerprinter cannot identify database servers so specifying the remote DB backend will greatly enhance performance and reduce bandwidth consumption.

  • :no_fingerprinting (Bool) — default: false

    Disable platform fingerprinting and include all payloads in the audit.

    Use this option in addition to the ‘:platforms` one to restrict the audit payloads to explicitly specified platforms.

  • :grid (Bool) — default: false

    Use the Dispatcher Grid to load-balance scans across the available nodes.

    If set to ‘true`, it serves as a shorthand for:

    grid_mode: :balance
    
  • :grid_mode (String, Symbol) — default: nil

    Grid mode to use, available modes are:

    • ‘nil` – No grid.

    • ‘:balance` – Slave Instances will be provided by the least burdened

      grid members to keep the overall Grid workload even across all Dispatchers.
      
    • ‘:aggregate` – Used to perform a multi-Instance scan and will only

      request Instances from Grid members with different Pipe-IDs, resulting
      in application-level bandwidth aggregation.
      
  • :spawns (Integer) — default: 0

    The amount of slaves to spawn. The behavior of this option changes depending on the ‘grid_mode` setting:

    • ‘nil` – All slave Instances will be spawned by this Instance directly,

      and thus reside in the same machine.
      
    • ‘:balance` – Slaves will be provided by the least burdened Grid Dispatchers.

    • ‘:aggregate` – Slaves will be provided by Grid Dispatchers with unique

      Pipe-IDs and the value of this option will be treated as a possible
      maximum rather than a hard setting. Actual spawn count will be determined
      by Dispatcher availability at the time.
      


527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
# File 'lib/arachni/rpc/server/instance.rb', line 527

def scan( opts = {}, &block )
    # If the instance isn't clean bail out now.
    if busy? || @called
        block.call false
        return false
    end

    # Normalize this sucker to have symbols as keys.
    opts = opts.my_symbolize_keys( false )

    slaves      = opts.delete(:slaves) || []
    spawn_count = opts[:spawns]
    spawn_count = spawn_count.to_i

    if (platforms = opts.delete(:platforms))
        begin
            Platform::Manager.new( [platforms].flatten.compact )
        rescue => e
            fail ArgumentError, e.to_s
        end
    end

    opts[:dispatcher] ||= {}
    opts[:scope]      ||= {}

    if opts[:grid] || opts[:grid_mode]
        if spawn_count <= 0
            fail ArgumentError,
                 'Option \'spawns\' must be greater than 1 for Grid scans.'
        end

        if [opts[:scope]['restrict_paths']].flatten.compact.any?
            fail ArgumentError,
                 'Scope option \'restrict_paths\' is not supported when in' <<
                     ' multi-Instance mode.'
        end
    end

    # There may be follow-up/retry calls by the client in cases of network
    # errors (after the request has reached us) so we need to keep minimal
    # track of state in order to bail out on subsequent calls.
    @called = @scan_initializing = true

    # Plugins option needs to be a hash...
    if opts[:plugins] && opts[:plugins].is_a?( Array )
        opts[:plugins] = opts[:plugins].inject( {} ) { |h, n| h[n] = {}; h }
    end

    if opts.include?( :grid )
        @framework.options.dispatcher.grid = opts.delete(:grid)
    end

    if opts.include?( :grid_mode )
        @framework.options.dispatcher.grid_mode = opts.delete(:grid_mode)
    end

    @active_options.set( opts )

    if @framework.options.url.to_s.empty?
        fail ArgumentError, 'Option \'url\' is mandatory.'
    end

    @framework.checks.load opts[:checks] if opts[:checks]
    @framework.plugins.load opts[:plugins] if opts[:plugins]

    # Starts the scan after all necessary options have been set.
    after = proc { block.call @framework.run; @scan_initializing = false }

    if @framework.options.dispatcher.grid?
        # If a Grid scan has been selected then just set us as the master,
        # the Framework will sort out the rest.
        @framework.set_as_master

        # Rock n' roll!
        after.call
    else
        # Handles each spawn, enslaving it for a multi-Instance scan.
        each = proc do |slave, iter|
            @framework.enslave( slave ){ iter.next }
        end

        spawn( spawn_count ) do |spawns|
            # Add our spawns to the slaves list which was passed as an option.
            slaves |= spawns

            # Process the Instances.
            Reactor.global.create_iterator( slaves, slaves.empty? ? 1 : slaves.size ).
                each( each, after )
        end
    end

    true
end