Module: Arachni::Module::Auditor
Overview
Included by Base and provides helper audit methods to all modules.
There are 3 main types of audit and analysis techniques available:
It should be noted that actual analysis takes place at the element level, and to be more specific, the Element::Capabilities::Auditable element level.
It also provides:
-
Discovery helpers for checking and logging the existence of remote files.
-
Pattern matching helpers for checking and logging the existence of strings in responses or in the body of the page that’s being audited.
-
General Issue logging helpers.
Constant Summary collapse
- Format =
Holds constant bitfields that describe the preferred formatting of injection strings.
Element::Capabilities::Mutable::Format
- OPTIONS =
Default audit options.
{ # # Elements to audit. # # If no elements have been passed to audit methods, candidates will be # determined by {#each_candidate_element}. # elements: [Element::LINK, Element::FORM, Element::COOKIE, Element::HEADER, Element::BODY], # # If set to `true` the HTTP response will be analyzed for new elements. # Be careful when enabling it, there'll be a performance penalty. # # If set to `false`, no training is going to occur. # # If set to `nil`, when the Auditor submits a form with original or # sample values this option will be overridden to `true` # train: nil }
Instance Attribute Summary collapse
-
#framework ⇒ Arachni::Framework
readonly
abstract
REQUIRED.
-
#page ⇒ Arachni::Page
readonly
abstract
REQUIRED.
Class Method Summary collapse
- .current_timeout_audit_operations_cnt ⇒ Object
- .included(m) ⇒ Object
- .on_timing_attacks(&block) ⇒ Object
- .reset ⇒ Object
- .running_timeout_attacks? ⇒ Boolean
- .timeout_audit_blocks ⇒ Object
- .timeout_audit_operations_cnt ⇒ Object
- .timeout_audit_run ⇒ Object
- .timeout_candidates ⇒ Object
- .timeout_loaded_modules ⇒ Object
Instance Method Summary collapse
-
#audit(payloads, opts = {}, &block) ⇒ Object
If a block has been provided it calls Element::Capabilities::Auditable#audit for every element, otherwise, it defaults to #audit_taint.
-
#audit_rdiff(opts = {}, &block) ⇒ Object
Audits elements using differential analysis and automatically logs results.
-
#audit_taint(payloads, opts = {}) ⇒ Object
Provides easy access to element auditing using simple taint analysis and automatically logs results.
-
#audit_timeout(payloads, opts = {}) ⇒ Object
Audits elements using timing attacks and automatically logs results.
- #audited(id) ⇒ Object
-
#audited?(id) ⇒ Bool
‘true` if audited, `false` otherwise.
-
#each_candidate_element(opts = {}) {|element| ... } ⇒ Object
Passes each element prepared for audit to the block.
- #http ⇒ Arachni::HTTP
-
#log(opts, res = nil) ⇒ Object
Populates and logs an Issue based on data from ‘opts` and `res`.
-
#log_issue(opts) ⇒ Object
Helper method for issue logging.
-
#log_remote_file(res, silent = false) ⇒ Object
(also: #log_remote_directory)
Logs the existence of a remote file as an issue.
-
#log_remote_file_if_exists(url, silent = false, &block) ⇒ Object
(also: #log_remote_directory_if_exists)
Logs a remote file or directory if it exists.
-
#match_and_log(regexps, string = page.body, &block) ⇒ Object
Matches an array of regular expressions against a string and logs the result as an issue.
- #max_issues ⇒ Object
-
#override_instance_scope? ⇒ Bool
abstract
OPTIONAL.
- #preferred ⇒ Object abstract
-
#register_results(issues) ⇒ Object
Just a delegator, logs an array of issues.
-
#remote_file_exist?(url, &block) ⇒ Object
(also: #remote_file_exists?)
Checks whether or not a remote resource exists.
-
#skip?(elem) ⇒ Boolean
This is called right before an Element is audited and is used to determine whether to skip it or not.
Methods included from Output
#fancy_name, #print_bad, #print_debug, #print_error, #print_info, #print_line, #print_ok, #print_status, #print_verbose
Methods included from UI::Output
#debug?, #debug_off, #debug_on, #disable_only_positives, #error_logfile, #flush_buffer, #log_error, #mute, #muted?, old_reset_output_options, #only_positives, #only_positives?, #print_bad, #print_debug, #print_debug_backtrace, #print_debug_pp, #print_error, #print_error_backtrace, #print_info, #print_line, #print_ok, #print_status, #print_verbose, #reroute_to_file, #reroute_to_file?, reset_output_options, #set_buffer_cap, #set_error_logfile, #uncap_buffer, #unmute, #verbose, #verbose?
Instance Attribute Details
#framework ⇒ Arachni::Framework (readonly)
REQUIRED
178 179 180 |
# File 'lib/arachni/module/auditor.rb', line 178 def framework @framework end |
#page ⇒ Arachni::Page (readonly)
REQUIRED
169 170 171 |
# File 'lib/arachni/module/auditor.rb', line 169 def page @page end |
Class Method Details
.current_timeout_audit_operations_cnt ⇒ Object
77 78 79 |
# File 'lib/arachni/module/auditor.rb', line 77 def self.current_timeout_audit_operations_cnt Element::Capabilities::Auditable.current_timeout_audit_operations_cnt end |
.included(m) ⇒ Object
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/arachni/module/auditor.rb', line 101 def self.included( m ) m.class_eval do def self.issue_counter @issue_counter ||= 0 end def self.issue_counter=( int ) @issue_counter = int end def increment_issue_counter self.class.issue_counter += 1 end def issue_limit_reached?( count = max_issues ) self.class.issue_limit_reached?( count ) end def self.issue_limit_reached?( count = max_issues ) issue_counter >= count if !count.nil? end def self.max_issues info[:max_issues] end end end |
.on_timing_attacks(&block) ⇒ Object
65 66 67 |
# File 'lib/arachni/module/auditor.rb', line 65 def self.on_timing_attacks( &block ) Element::Capabilities::Auditable.on_timing_attacks( &block ) end |
.reset ⇒ Object
51 52 53 54 |
# File 'lib/arachni/module/auditor.rb', line 51 def self.reset audited.clear Element::Capabilities::Auditable::Timeout.reset end |
.running_timeout_attacks? ⇒ Boolean
68 69 70 |
# File 'lib/arachni/module/auditor.rb', line 68 def self.running_timeout_attacks? Element::Capabilities::Auditable.running_timeout_attacks? end |
.timeout_audit_blocks ⇒ Object
56 57 58 |
# File 'lib/arachni/module/auditor.rb', line 56 def self.timeout_audit_blocks Element::Capabilities::Auditable.timeout_audit_blocks end |
.timeout_audit_operations_cnt ⇒ Object
74 75 76 |
# File 'lib/arachni/module/auditor.rb', line 74 def self.timeout_audit_operations_cnt Element::Capabilities::Auditable.timeout_audit_operations_cnt end |
.timeout_audit_run ⇒ Object
71 72 73 |
# File 'lib/arachni/module/auditor.rb', line 71 def self.timeout_audit_run Element::Capabilities::Auditable.timeout_audit_run end |
.timeout_candidates ⇒ Object
59 60 61 |
# File 'lib/arachni/module/auditor.rb', line 59 def self.timeout_candidates Element::Capabilities::Auditable.timeout_candidates end |
.timeout_loaded_modules ⇒ Object
62 63 64 |
# File 'lib/arachni/module/auditor.rb', line 62 def self.timeout_loaded_modules Element::Capabilities::Auditable.timeout_loaded_modules end |
Instance Method Details
#audit(payloads, opts = {}, &block) ⇒ Object
If a block has been provided it calls Element::Capabilities::Auditable#audit for every element, otherwise, it defaults to #audit_taint.
Uses #each_candidate_element to decide which elements to audit.
551 552 553 554 555 556 557 558 |
# File 'lib/arachni/module/auditor.rb', line 551 def audit( payloads, opts = {}, &block ) opts = OPTIONS.merge( opts ) if !block_given? audit_taint( payloads, opts ) else each_candidate_element( opts ) { |e| e.audit( payloads, opts, &block ) } end end |
#audit_rdiff(opts = {}, &block) ⇒ Object
Audits elements using differential analysis and automatically logs results.
Uses #each_candidate_element to decide which elements to audit.
582 583 584 585 |
# File 'lib/arachni/module/auditor.rb', line 582 def audit_rdiff( opts = {}, &block ) opts = OPTIONS.merge( opts ) each_candidate_element( opts ) { |e| e.rdiff_analysis( opts, &block ) } end |
#audit_taint(payloads, opts = {}) ⇒ Object
Provides easy access to element auditing using simple taint analysis and automatically logs results.
Uses #each_candidate_element to decide which elements to audit.
569 570 571 572 |
# File 'lib/arachni/module/auditor.rb', line 569 def audit_taint( payloads, opts = {} ) opts = OPTIONS.merge( opts ) each_candidate_element( opts ) { |e| e.taint_analysis( payloads, opts ) } end |
#audit_timeout(payloads, opts = {}) ⇒ Object
Audits elements using timing attacks and automatically logs results.
Uses #each_candidate_element to decide which elements to audit.
595 596 597 598 |
# File 'lib/arachni/module/auditor.rb', line 595 def audit_timeout( payloads, opts = {} ) opts = OPTIONS.merge( opts ) each_candidate_element( opts ) { |e| e.timeout_analysis( payloads, opts ) } end |
#audited(id) ⇒ Object
86 87 88 |
# File 'lib/arachni/module/auditor.rb', line 86 def audited( id ) Auditor.audited << "#{self.class}-#{id}" end |
#audited?(id) ⇒ Bool
Returns ‘true` if audited, `false` otherwise.
97 98 99 |
# File 'lib/arachni/module/auditor.rb', line 97 def audited?( id ) Auditor.audited.include?( "#{self.class}-#{id}" ) end |
#each_candidate_element(opts = {}) {|element| ... } ⇒ Object
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 |
# File 'lib/arachni/module/auditor.rb', line 501 def each_candidate_element( opts = {} ) if !opts.include?( :elements) || !opts[:elements] || opts[:elements].empty? opts[:elements] = self.class.info[:elements] end if !opts.include?( :elements) || !opts[:elements] || opts[:elements].empty? opts[:elements] = OPTIONS[:elements] end elements = [] opts[:elements].each do |elem| next if !Options.audit?( elem ) elements |= case elem when Element::LINK page.links when Element::FORM page.forms when Element::COOKIE page. when Element::HEADER page.headers when Element::BODY else fail ArgumentError, "Unknown element: #{elem}" end end while (e = elements.pop) next if e.auditable.empty? d = e.dup d.auditor = self yield d end end |
#http ⇒ Arachni::HTTP
195 196 197 |
# File 'lib/arachni/module/auditor.rb', line 195 def http HTTP end |
#log(opts, res = nil) ⇒ Object
Populates and logs an Issue based on data from ‘opts` and `res`.
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
# File 'lib/arachni/module/auditor.rb', line 386 def log( opts, res = nil ) response_headers = {} request_headers = {} response = nil method = nil if page request_headers = page.request_headers response_headers = page.response_headers response = page.body url = page.url method = page.method.to_s.upcase if page.method end if res request_headers = res.request.headers response_headers = res.headers_hash response = res.body url = opts[:action] || res.effective_url method = res.request.method.to_s.upcase end if !response_headers['content-type'].to_s.include?( 'text' ) response = nil end var = opts[:altered] || opts[:var] element = opts[:element] || opts[:elem] msg = "In #{element}" msg << " input '#{var}'" if var print_ok "#{msg} ( #{url} )" print_verbose( "Injected string:\t" + opts[:injected] ) if opts[:injected] print_verbose( "Verified string:\t" + opts[:match].to_s ) if opts[:match] print_verbose( 'Matched regular expression: ' + opts[:regexp].to_s ) if opts[:regexp] print_debug( 'Request ID: ' + res.request.id.to_s ) if res print_verbose( '---------' ) if only_positives? # Platform identification by vulnerability. platform_type = nil if (platform = opts[:platform]) Platform::Manager[url] << platform if Options.fingerprint? platform_type = Platform::Manager[url].find_type( platform ) end log_issue( var: var, url: url, platform: platform, platform_type: platform_type, injected: opts[:injected], id: opts[:id], regexp: opts[:regexp], regexp_match: opts[:match], elem: element, verification: !!opts[:verification], remarks: opts[:remarks], method: method, response: response, opts: opts, headers: { request: request_headers, response: response_headers, } ) end |
#log_issue(opts) ⇒ Object
Helper method for issue logging.
318 319 320 321 |
# File 'lib/arachni/module/auditor.rb', line 318 def log_issue( opts ) # register the issue register_results( [ Issue.new( opts.merge( self.class.info ) ) ] ) end |
#log_remote_file(res, silent = false) ⇒ Object Also known as: log_remote_directory
Logs the existence of a remote file as an issue.
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
# File 'lib/arachni/module/auditor.rb', line 291 def log_remote_file( res, silent = false ) url = res.effective_url filename = File.basename( URI( res.effective_url ).path ) log_issue( url: url, injected: filename, id: filename, elem: Element::PATH, response: res.body, headers: { request: res.request.headers, response: res.headers_hash, } ) print_ok( "Found #{filename} at #{url}" ) if !silent end |
#log_remote_file_if_exists(url, silent = false, &block) ⇒ Object Also known as: log_remote_directory_if_exists
Ignores custom 404 responses.
Logs a remote file or directory if it exists.
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/arachni/module/auditor.rb', line 233 def log_remote_file_if_exists( url, silent = false, &block ) return nil if !url print_status( "Checking for #{url}" ) if !silent remote_file_exist?( url ) do |bool, res| print_status( 'Analyzing response for: ' + url ) if !silent next if !bool block.call( res ) if block_given? log_remote_file( res ) # If the file exists let the trainer parse it since it may contain # brand new data to audit. framework.trainer.push( res ) end true end |
#match_and_log(regexps, string = page.body, &block) ⇒ Object
Matches an array of regular expressions against a string and logs the result as an issue.
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
# File 'lib/arachni/module/auditor.rb', line 337 def match_and_log( regexps, string = page.body, &block ) # make sure that we're working with an array regexps = [regexps].flatten elems = self.class.info[:elements] elems = OPTIONS[:elements] if !elems || elems.empty? regexps.each do |regexp| string.scan( regexp ).flatten.uniq.each do |match| next if !match next if block && !block.call( match ) log( regexp: regexp, match: match, element: Element::BODY ) end if elems.include? Element::BODY next if string != page.body page.response_headers.each do |k,v| next if !v v.to_s.scan( regexp ).flatten.uniq.each do |match| next if !match next if block && !block.call( match ) log( var: k, regexp: regexp, match: match, element: Element::HEADER ) end end if elems.include? Element::HEADER end end |
#max_issues ⇒ Object
129 130 131 |
# File 'lib/arachni/module/auditor.rb', line 129 def max_issues self.class.max_issues end |
#override_instance_scope? ⇒ Bool
OPTIONAL
Allows modules to ignore multi-Instance scope restrictions in order to audit elements that are not on the sanctioned whitelist.
190 191 192 |
# File 'lib/arachni/module/auditor.rb', line 190 def override_instance_scope? false end |
#preferred ⇒ Object
457 458 459 |
# File 'lib/arachni/module/auditor.rb', line 457 def preferred [] end |
#register_results(issues) ⇒ Object
Just a delegator, logs an array of issues.
206 207 208 209 210 211 |
# File 'lib/arachni/module/auditor.rb', line 206 def register_results( issues ) return if issue_limit_reached? self.class.issue_counter += issues.size framework.modules.register_results( issues ) end |
#remote_file_exist?(url, &block) ⇒ Object Also known as: remote_file_exists?
Ignores custom 404 responses.
Checks whether or not a remote resource exists.
266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/arachni/module/auditor.rb', line 266 def remote_file_exist?( url, &block ) req = http.get( url ) return false if !req req.on_complete do |res| if res.code != 200 block.call( false, res ) else http.custom_404?( res ) { |bool| block.call( !bool, res ) } end end true end |
#skip?(elem) ⇒ Boolean
This is called right before an Element is audited and is used to determine whether to skip it or not.
Running modules can override this as they wish but at their own peril.
472 473 474 475 476 477 478 479 480 481 482 483 484 485 |
# File 'lib/arachni/module/auditor.rb', line 472 def skip?( elem ) # Find out our own shortname. @modname ||= framework.modules.map { |k, v| k if v == self.class }.compact.first # Don't audit elements which have been already logged as vulnerable # either by us or preferred modules. (preferred | [@modname]).each do |mod| next if !framework.modules.include?( mod ) issue_id = elem.provisioned_issue_id( framework.modules[mod].info[:name] ) return true if framework.modules.issue_set.include?( issue_id ) end false end |