Module: Arachni::Element::Capabilities::Auditable::RDiff
- Included in:
- Arachni::Element::Capabilities::Auditable
- Defined in:
- lib/arachni/element/capabilities/auditable/rdiff.rb
Overview
Performs boolean, fault injection and behavioral analysis (using the rDiff algorithm) in order to determine whether the web application is responding to the injected data and how.
If the behavior can be manipulated by the injected data in ways that it’s not supposed to (like when evaluating injected code) then the element is deemed vulnerable.
Constant Summary collapse
- RDIFF_OPTIONS =
{ # append our seeds to the default values format: [Mutable::Format::APPEND], # allow duplicate requests redundant: true, # amount of rdiff iterations precision: 2, respect_method: true }
Class Method Summary collapse
Instance Method Summary collapse
-
#rdiff_analysis(opts = {}, &block) ⇒ Object
Performs differential analysis and logs an issue should there be one.
Class Method Details
.included(mod) ⇒ Object
34 35 36 37 38 |
# File 'lib/arachni/element/capabilities/auditable/rdiff.rb', line 34 def self.included( mod ) # the rdiff attack performs it own redundancy checks so we need this to # keep track of audited elements @@rdiff_audited ||= BloomFilter.new end |
Instance Method Details
#rdiff_analysis(opts = {}, &block) ⇒ Object
Performs differential analysis and logs an issue should there be one.
opts = {
:precision => 3,
:faults => [ 'fault injections' ],
:bools => [ 'boolean injections' ]
}
element.rdiff_analysis( opts )
Here’s how it goes:
-
let default be the default/original response
-
let fault be the response of the fault injection
-
let bool be the response of the boolean injection
A vulnerability is logged if:
default == bool AND bool.code == 200 AND fault != bool
The “bool” response is also checked in order to determine if it’s a custom 404, if it is it’ll be skipped.
If a block has been provided analysis and logging will be delegated to it.
88 89 90 91 92 93 94 95 96 97 98 99 100 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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/arachni/element/capabilities/auditable/rdiff.rb', line 88 def rdiff_analysis( opts = {}, &block ) opts = self.class::MUTATION_OPTIONS.merge( RDIFF_OPTIONS.merge( opts ) ) # don't continue if there's a missing value auditable.values.each { |val| return if !val || val.empty? } return if rdiff_audited? rdiff_audited responses = { # will hold the original, default, response that results from submitting orig: nil, # will hold responses of boolean injections good: {}, # will hold responses of fault injections bad: {} } # submit the element, as is, opts[:precision] amount of times and # rdiff the responses in order to arrive to a refined response without # any superfluous dynamic content opts[:precision].times { # get the default responses audit( '', opts ) do |res| responses[:orig] ||= res.body # remove context-irrelevant dynamic content like banners and such responses[:orig] = responses[:orig].rdiff( res.body ) end } # perform fault injection opts[:precision] amount of times and # rdiff the responses in order to arrive to a refined response without # any superfluous dynamic content opts[:precision].times { opts[:faults].each do |str| # get mutations of self using the fault seed, which will # cause an internal/silent error when evaluated mutations( str, opts ).each do |elem| print_status elem.status_string # submit the mutation and store the response elem.submit( opts ) do |res| responses[:bad][elem.altered] ||= res.body.clone # remove context-irrelevant dynamic content like banners and such # from the error page responses[:bad][elem.altered] = responses[:bad][elem.altered].rdiff( res.body.clone ) end end end } # get injection variations that will not affect the outcome of the query opts[:bools].each do |str| # get mutations of self using the boolean seed, which will not # alter the execution flow mutations( str, opts ).each do |elem| print_status elem.status_string # submit the mutation and store the response elem.submit( opts ) do |res| responses[:good][elem.altered] ||= [] # save the response and some data for analysis responses[:good][elem.altered] << { 'str' => str, 'res' => res, 'elem' => elem } end end end # when this runs the "responses" hash will have been populated and we # can continue with analysis http.after_run { responses[:good].keys.each do |key| responses[:good][key].each do |res| # if there's a block passed then delegate analysis to it if block exception_jail( false ){ block.call( res['str'], res['elem'], responses[:orig], res['res'], responses[:bad][key] ) } # if default_response_body == bool_response_body AND # bool_response_code == 200 AND # fault_response_body != bool_response_body elsif responses[:orig] == res['res'].body && responses[:bad][key] != res['res'].body && res['res'].code == 200 # check to see if the current boolean response we're analyzing # is a custom 404 page http.custom_404?( res['res'] ) do |bool| # if this is a custom 404 page bail out next if bool # if this isn't a custom 404 page then it means that # the element is vulnerable, so go ahead and log the issue # information for the Metareport report opts = { injected_orig: res['str'], combo: res['elem'].auditable } @auditor.log({ var: key, opts: opts, injected: res['str'], id: res['str'], elem: res['elem'].type, }, res['res'] ) end end end end } end |