=begin Copyright 2010-2014 Tasos Laskos Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =end # # Vector feed plug-in. # # Can be used to perform extremely specialized/narrow audits # on a per vector/element basis. # # Useful for unit-testing or a gazillion other things. :) # # @author Tasos "Zapotek" Laskos # class Arachni::Plugins::VectorFeed < Arachni::Plugin::Base def prepare framework.pause print_status 'System paused.' end def run # if the 'vectors' option is an array at this point then someone fed # them to us programmatically if !options['vectors'].is_a? Array feed = if options['yaml_file'] IO.read( options['yaml_file'] ) elsif options['yaml_string'] options['yaml_string'] else '' end if !feed || feed.empty? print_bad 'The feed is empty, bailing out.' return end feed = YAML.load_stream( StringIO.new( feed ) ).flatten yaml_err = 'Invalid YAML syntax, bailing out..' begin if !feed.is_a? Array print_bad yaml_err return end rescue print_bad yaml_err return end else feed = options['vectors'] end pages = {} page_buffer = [] print_status "Imported #{feed.size} vectors." feed.each do |obj| vector = (obj.respond_to?( :value ) ? obj.value : obj).symbolize_keys( false ) begin exception_jail{ if page?( vector ) page_buffer << page_from_body_vector( vector ) next end next if !(element = hash_to_element( vector )) pages[element.url] ||= Page.new( code: 200, url: element.url ) pages[element.url].send( "#{element.type}s" ) << element } rescue next end end pages = pages.values pages |= page_buffer if !pages.empty? print_status 'Pushing the vectors to the audit queue...' pages.each { |page| framework.push_to_page_queue( page ) } print_status 'Done!' else print_bad 'Could not find any usable vectors.' end end def page?( vector ) vector[:type] == 'page' end def page_from_body_vector( vector ) Page.new( code: Integer( vector[:code] || 200 ), url: vector[:url] || framework.opts.url.to_s, body: vector[:body] || '', response_headers: vector[:headers] || {} ) end def hash_to_element( vector ) owner = framework.opts.url.to_s action = vector[:action] inputs = vector[:inputs] method = vector[:method] || 'get' type = vector[:type] || 'link' return if !inputs || inputs.empty? e = case type when Element::LINK Link.new( owner, action: action, inputs: inputs, ) when Element::FORM Form.new( owner, method: method, action: action, inputs: inputs ) when Element::COOKIE Cookie.new( action, inputs ) when Element::HEADER Header.new( action, inputs ) else Link.new( owner, action: action, inputs: inputs ) end (vector[:skip] || []).each { |i| e.immutables << i } e end def clean_up framework.resume print_status 'System resumed.' end def self.info { name: 'Vector feed', description: %q{Reads in vector data from which it creates elements to be audited. Can be used to perform extremely specialized/narrow audits on a per vector/element basis. Notes: * To only audit the vectors in the feed you must set the 'link-count' limit to 0 to prevent crawling. * Can handle multiple YAML documents. Example YAML file: - # you can pass pages to be audited by grep modules (and JS in the future) type: page url: http://localhost/ # response code code: 200 # response headers headers: Content-Type: "text/html; charset=utf-8" body: "HTML code goes here" - # default type is link which has method get #type: link action: http://localhost/link inputs: my_param: "my val" - # if a method is post it'll default to a form type type: form method: post action: http://localhost/form inputs: post_this: "HUA!" csrf: "my_csrf_token" # do not fuzz/mutate/audit the following inputs (by name obviously) skip: - csrf # GET only - type: cookie action: http://localhost/cookie inputs: session_id: "43434234343sddsdsds" # GET only - type: header action: http://localhost/header # only 1 input allowed, each header field=>value must be defined separately inputs: User-Agent: "Blah/2" }, author: 'Tasos "Zapotek" Laskos ', version: '0.1.3', options: [ Options::Base.new( 'vectors', [false, ' Vector array (for configuration over RPC).'] ), Options::String.new( 'yaml_string', [false, 'A string of YAML serialized vectors (for configuration over RPC).'] ), Options::Path.new( 'yaml_file', [false, 'A file containing the YAML serialized vectors.'] ) ] } end end