Module: Arachni::Module::Auditor
Overview
Auditor module
Included by Base and provides helper audit methods to all modules.
There are 3 main types of audit and analysis techniques available:
-
Taint analysis – #audit
-
Timeout analysis – #audit_timeout
-
Differential analysis – #audit_rdiff
It should be noted that actual analysis takes place at the element level, and to be more specific, the Element::Capabilities::Auditable element level.
The module 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 =
Holds constants that describe Issue severities.
Severity = Issue::Severity
{ # # Elements to audit. # # If no elements have been passed to audit candidates will be # determined by {#candidate_elements}. # 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
- .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_loaded_modules ⇒ Object
Instance Method Summary collapse
-
#audit(injection_str, 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 attacks.
-
#audit_taint(taint, opts = {}) ⇒ Object
Provides easy access to element auditing using simple taint analysis.
-
#audit_timeout(strings, opts = {}) ⇒ Object
Audits elements using timing attacks and automatically logs results.
- #audited(id) ⇒ Object
-
#audited?(id) ⇒ Bool
trueif audited,falseotherwise. -
#candidate_elements(opts = {}) ⇒ Array<Arachni::Element::Capabilities::Auditable] array of auditable elements
Returns a list of prepared elements to be audited.
- #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 the “string” (default string is the HTML code in page.body) to an array of regular expressions and logs the results.
-
#override_instance_scope? ⇒ Bool
abstract
OPTIONAL.
- #preferred ⇒ Object
-
#register_results(issues) ⇒ Object
Just a delegator logs an array of issues.
-
#remote_file_exist?(url, &block) ⇒ Boolean
Checks that the response points to an existing file/page and not an error or custom 404 response.
-
#skip?(elem) ⇒ Boolean
This is called right before an [Arachni::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, #flush_buffer, #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, #uncap_buffer, #unmute, #verbose, #verbose?
Instance Attribute Details
#framework ⇒ Arachni::Framework (readonly)
REQUIRED
Must return the Framework
151 152 153 |
# File 'lib/arachni/module/auditor.rb', line 151 def framework @framework end |
#page ⇒ Arachni::Page (readonly)
REQUIRED
Must return the Page object you wish to be audited.
140 141 142 |
# File 'lib/arachni/module/auditor.rb', line 140 def page @page end |
Class Method Details
.current_timeout_audit_operations_cnt ⇒ Object
66 67 68 |
# File 'lib/arachni/module/auditor.rb', line 66 def self.current_timeout_audit_operations_cnt Element::Capabilities::Auditable.current_timeout_audit_operations_cnt end |
.on_timing_attacks(&block) ⇒ Object
54 55 56 |
# File 'lib/arachni/module/auditor.rb', line 54 def self.on_timing_attacks( &block ) Element::Capabilities::Auditable.on_timing_attacks( &block ) end |
.reset ⇒ Object
44 45 46 |
# File 'lib/arachni/module/auditor.rb', line 44 def self.reset audited.clear end |
.running_timeout_attacks? ⇒ Boolean
57 58 59 |
# File 'lib/arachni/module/auditor.rb', line 57 def self.running_timeout_attacks? Element::Capabilities::Auditable.running_timeout_attacks? end |
.timeout_audit_blocks ⇒ Object
48 49 50 |
# File 'lib/arachni/module/auditor.rb', line 48 def self.timeout_audit_blocks Element::Capabilities::Auditable.timeout_audit_blocks end |
.timeout_audit_operations_cnt ⇒ Object
63 64 65 |
# File 'lib/arachni/module/auditor.rb', line 63 def self.timeout_audit_operations_cnt Element::Capabilities::Auditable.timeout_audit_operations_cnt end |
.timeout_audit_run ⇒ Object
60 61 62 |
# File 'lib/arachni/module/auditor.rb', line 60 def self.timeout_audit_run Element::Capabilities::Auditable.timeout_audit_run end |
.timeout_loaded_modules ⇒ Object
51 52 53 |
# File 'lib/arachni/module/auditor.rb', line 51 def self.timeout_loaded_modules Element::Capabilities::Auditable.timeout_loaded_modules end |
Instance Method Details
#audit(injection_str, 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 #candidate_elements to decide which elements to audit.
477 478 479 480 481 482 483 484 |
# File 'lib/arachni/module/auditor.rb', line 477 def audit( injection_str, opts = {}, &block ) opts = OPTIONS.merge( opts ) if !block_given? audit_taint( injection_str, opts ) else candidate_elements( opts ).each { |e| e.audit( injection_str, opts, &block ) } end end |
#audit_rdiff(opts = {}, &block) ⇒ Object
Audits elements using differential analysis attacks.
Uses #candidate_elements to decide which elements to audit.
507 508 509 510 |
# File 'lib/arachni/module/auditor.rb', line 507 def audit_rdiff( opts = {}, &block ) opts = OPTIONS.merge( opts ) candidate_elements( opts ).each { |e| e.rdiff_analysis( opts, &block ) } end |
#audit_taint(taint, opts = {}) ⇒ Object
Provides easy access to element auditing using simple taint analysis.
Uses #candidate_elements to decide which elements to audit.
494 495 496 497 |
# File 'lib/arachni/module/auditor.rb', line 494 def audit_taint( taint, opts = {} ) opts = OPTIONS.merge( opts ) candidate_elements( opts ).each { |e| e.taint_analysis( taint, opts ) } end |
#audit_timeout(strings, opts = {}) ⇒ Object
Audits elements using timing attacks and automatically logs results.
Uses #candidate_elements to decide which elements to audit.
520 521 522 523 |
# File 'lib/arachni/module/auditor.rb', line 520 def audit_timeout( strings, opts = {} ) opts = OPTIONS.merge( opts ) candidate_elements( opts ).each { |e| e.timeout_analysis( strings, opts ) } end |
#audited(id) ⇒ Object
75 76 77 |
# File 'lib/arachni/module/auditor.rb', line 75 def audited( id ) Auditor.audited << "#{self.class}-#{id}" end |
#audited?(id) ⇒ Bool
Returns true if audited, false otherwise.
86 87 88 |
# File 'lib/arachni/module/auditor.rb', line 86 def audited?( id ) Auditor.audited.include?( "#{self.class}-#{id}" ) end |
#candidate_elements(opts = {}) ⇒ Array<Arachni::Element::Capabilities::Auditable] array of auditable elements
Returns a list of prepared elements to be audited.
If no element types have been specified in ‘opts’ it will use the elements from the module’s “self.info()” hash.
If no elements have been specified in ‘opts’ or “self.info()” it will use the elements in OPTIONS.
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 |
# File 'lib/arachni/module/auditor.rb', line 432 def candidate_elements( 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 failt "Unknown element to audit: #{elem}" end end elements.map { |e| d = e.dup; d.auditor = self; d } end |
#http ⇒ Arachni::HTTP
169 170 171 |
# File 'lib/arachni/module/auditor.rb', line 169 def http HTTP end |
#log(opts, res = nil) ⇒ Object
Populates and logs an Issue based on data from “opts” and “res”.
335 336 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 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 |
# File 'lib/arachni/module/auditor.rb', line 335 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 << " var '#{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? log_issue( var: var, url: url, injected: opts[:injected], id: opts[:id], regexp: opts[:regexp], regexp_match: opts[:match], elem: element, verification: !!opts[:verification], method: method, response: response, opts: opts, headers: { request: request_headers, response: response_headers, } ) end |
#log_issue(opts) ⇒ Object
Helper method for issue logging.
272 273 274 275 |
# File 'lib/arachni/module/auditor.rb', line 272 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.
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'lib/arachni/module/auditor.rb', line 245 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
Logs a remote file or directory if it exists.
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/arachni/module/auditor.rb', line 198 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 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 http.trainer.push( res ) end end true end |
#match_and_log(regexps, string = page.body, &block) ⇒ Object
Matches the “string” (default string is the HTML code in page.body) to an array of regular expressions and logs the results.
For good measure, regexps will also be run against the page headers (page.response_headers).
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/arachni/module/auditor.rb', line 288 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 |
#override_instance_scope? ⇒ Bool
OPTIONAL
Allows modules to ignore HPG scope restrictions
This way they can audit elements that are not on the Grid sanctioned whitelist.
164 165 166 |
# File 'lib/arachni/module/auditor.rb', line 164 def override_instance_scope? false end |
#preferred ⇒ Object
394 395 396 |
# File 'lib/arachni/module/auditor.rb', line 394 def preferred [] end |
#register_results(issues) ⇒ Object
Just a delegator logs an array of issues.
180 181 182 |
# File 'lib/arachni/module/auditor.rb', line 180 def register_results( issues ) Module::Manager.register_results( issues ) end |
#remote_file_exist?(url, &block) ⇒ Boolean
Checks that the response points to an existing file/page and not an error or custom 404 response.
224 225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'lib/arachni/module/auditor.rb', line 224 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 [Arachni::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.
406 407 408 409 410 411 412 413 414 415 416 417 |
# File 'lib/arachni/module/auditor.rb', line 406 def skip?( elem ) if framework @modname ||= framework.modules.map { |k, v| k if v == self.class }.compact.first (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 end false end |