Module: Msf::Auxiliary::Web::Analysis::Differential

Defined in:
lib/msf/core/auxiliary/web/analysis/differential.rb

Constant Summary collapse

DIFFERENTIAL_OPTIONS =
{
  # amount of refinement iterations
  :precision => 2
}

Instance Method Summary collapse

Instance Method Details

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

Performs differential analysis and logs an issue should there be one.

Fuzzer must provide:

- #boolean_seeds_for - array of boolean injection strings
 (these are supposed to not alter the webapp behavior when interpreted)
- #fault_seeds_for - array of fault injection strings
 (these are supposed to force erroneous conditions when interpreted)

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.

Parameters:

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

    Options Hash (default: {}) :precision - amount of refinement iterations (default: 2)



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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
# File 'lib/msf/core/auxiliary/web/analysis/differential.rb', line 42

def differential_analysis( opts = {}, &block )
  opts = DIFFERENTIAL_OPTIONS.merge( opts )

  return if fuzzed? :type => :differential
  fuzzed :type => :differential

  # don't continue if there's a missing value
  params.values.each { |val| return if !val || val.empty? }

  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 do
    # get the default responses
    submit_async do |res|
      responses[:orig] ||= res.body.to_s
      # remove context-irrelevant dynamic content like banners and such
      responses[:orig] = Rex::Text.refine( responses[:orig], res.body.to_s )
    end
  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 do
    params.map do |name, value|
      fuzzer.fault_seeds_for( value ).map { |seed| permutation_for( name, seed ) }
         end.flatten.uniq.each do |elem|

      # submit the mutation and store the response
      elem.submit_async do |res|
              responses[:bad][elem.altered] ||= res.body.to_s.dup

        # remove context-irrelevant dynamic content like banners and such
        # from the error page
        responses[:bad][elem.altered] =
          Rex::Text.refine( responses[:bad][elem.altered], res.body.to_s.dup )
      end
        end
  end

  # get injection variations that will not affect the outcome of the query
  params.map do |name, value|
    fuzzer.boolean_seeds_for( value ).map { |seed| permutation_for( name, seed ) }
  end.flatten.uniq.each do |elem|

    # submit the mutation and store the response
    elem.submit_async do |res|
      responses[:good][elem.altered] ||= []
      # save the response and some data for analysis
      responses[:good][elem.altered] << {
        'res'  => res,
        'elem' => elem.dup
      }
    end
  end

  http.after_run do
    responses[:good].keys.each do |key|
      responses[:good][key].each do |res|

        # if default_response_body == bool_response_body AND
        #	fault_response_body != bool_response_body AND
        #	bool_response_code == 200
        if responses[:orig] == res['res'].body &&
          responses[:bad][key] != res['res'].body &&
          res['res'].code.to_i == 200

          # check to see if the current boolean response we're analyzing
          # is a custom 404 page
          http.if_not_custom_404( action, res['res'].body ) do
            # if this isn't a custom 404 page then it means that
            # the element is vulnerable, so go ahead and log the issue
            fuzzer.process_vulnerability( res['elem'], 'Boolean manipulation.' )
          end
        end
      end
    end
  end
end