Class: Msf::Auxiliary::Web::HTTP

Inherits:
Object
  • Object
show all
Defined in:
lib/msf/core/auxiliary/web/http.rb

Defined Under Namespace

Classes: Request, Response

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ HTTP

Returns a new instance of HTTP.

Parameters:

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

    Configuration for the HTTP helper.

Options Hash (opts):

  • :framework (Object)

    The active framework instance used for threading.

  • :parent (Object)

    The owning module used for error reporting.

  • :headers (Hash)

    Additional HTTP headers to send with every request.

  • :cookie_string (String)

    Cookie header value to set by default.

  • :auth (Hash)

    Default authentication options.

  • :redirect_limit (Integer)

    Maximum redirects to follow.



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
# File 'lib/msf/core/auxiliary/web/http.rb', line 106

def initialize( opts = {} )
  @opts = opts.dup

  @framework = opts[:framework]
  @parent    = opts[:parent]

  @headers = {
    'Accept' => '*/*',
    'Cookie' => opts[:cookie_string]
  }.merge( opts[:headers] || {} )

  @headers.delete( 'Cookie' ) if !@headers['Cookie']

  @request_opts = {}
  if opts[:auth].is_a? Hash
    @username = opts[:auth][:user].to_s
    @password = opts[:auth][:password].to_s
    @domain   = opts[:auth][:domain].to_s
  end

  self.redirect_limit = opts[:redirect_limit] || 20

  @queue = Queue.new

  @after_run_blocks = []
end

Instance Attribute Details

#domainObject

Returns the value of attribute domain.



97
98
99
# File 'lib/msf/core/auxiliary/web/http.rb', line 97

def domain
  @domain
end

#frameworkObject (readonly)

Returns the value of attribute framework.



93
94
95
# File 'lib/msf/core/auxiliary/web/http.rb', line 93

def framework
  @framework
end

#headersObject (readonly)

Returns the value of attribute headers.



92
93
94
# File 'lib/msf/core/auxiliary/web/http.rb', line 92

def headers
  @headers
end

#optsObject (readonly)

Returns the value of attribute opts.



91
92
93
# File 'lib/msf/core/auxiliary/web/http.rb', line 91

def opts
  @opts
end

#parentObject (readonly)

Returns the value of attribute parent.



94
95
96
# File 'lib/msf/core/auxiliary/web/http.rb', line 94

def parent
  @parent
end

#passwordObject

Returns the value of attribute password.



97
98
99
# File 'lib/msf/core/auxiliary/web/http.rb', line 97

def password
  @password
end

#redirect_limitObject

Returns the value of attribute redirect_limit.



96
97
98
# File 'lib/msf/core/auxiliary/web/http.rb', line 96

def redirect_limit
  @redirect_limit
end

#usernameObject

Returns the value of attribute username.



97
98
99
# File 'lib/msf/core/auxiliary/web/http.rb', line 97

def username
  @username
end

Instance Method Details

#after_run { ... } ⇒ Array<Proc>

Registers a callback to be executed after #run drains the request queue.

Yields:

  • A block to run after all queued requests finish.

Returns:

  • (Array<Proc>)

    The accumulated after-run callbacks.



137
138
139
# File 'lib/msf/core/auxiliary/web/http.rb', line 137

def after_run( &block )
  @after_run_blocks << block
end

#connectRex::Proto::Http::Client

Creates a Rex HTTP client using the configured target and authentication defaults.

Returns:



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/msf/core/auxiliary/web/http.rb', line 144

def connect
  c = Rex::Proto::Http::Client.new(
    opts[:target].host,
    opts[:target].port,
    {},
    opts[:target].ssl,
    'Auto',
    nil,
    username,
    password,
    subscriber: opts[:http_subscriber]
  )

  c.set_config({
    'vhost' => opts[:target].vhost,
    'ssl_server_name_indication' => opts[:target].ssl_server_name_indication || opts[:target].vhost,
    'agent' => opts[:user_agent] || Rex::UserAgent.session_agent,
    'domain' => domain
  })
  c
end

#custom_404?(path, body) {|is_custom_404| ... } ⇒ nil

Determines whether a response body matches the application’s custom 404 behavior.

The helper probes several random paths, refines their dynamic content, and compares the result to the supplied response body.

Parameters:

  • path (String)

    The original request path.

  • body (String)

    The response body to classify.

Yield Parameters:

  • is_custom_404 (Boolean)

    True when the body matches the custom 404 fingerprint.

Returns:

  • (nil)

    Returns nil immediately and reports the result via the callback.



283
284
285
286
287
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
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/msf/core/auxiliary/web/http.rb', line 283

def custom_404?( path, body, &callback )
  return if !path || !body

  precision = 2

  trv_back = File.dirname( path )
  trv_back << '/' if trv_back[-1,1] != '/'

  # 404 probes
  generators = [
    # get a random path with an extension
    proc{ path + Rex::Text.rand_text_alpha( 10 ) + '.' + Rex::Text.rand_text_alpha( 10 )[0..precision] },

    # get a random path without an extension
    proc{ path + Rex::Text.rand_text_alpha( 10 ) },

    # move up a dir and get a random file
    proc{ trv_back + Rex::Text.rand_text_alpha( 10 ) },

    # move up a dir and get a random file with an extension
    proc{ trv_back + Rex::Text.rand_text_alpha( 10 ) + '.' + Rex::Text.rand_text_alpha( 10 )[0..precision] },

    # get a random directory
    proc{ path + Rex::Text.rand_text_alpha( 10 ) + '/' }
  ]

  synchronize do
    @@_404 ||= {}
    @@_404[path] ||= []

    @@_404_gathered ||= Set.new

    gathered = 0
    if !@@_404_gathered.include?( path.hash )
      generators.each.with_index do |generator, i|
        @@_404[path][i] ||= {}

        precision.times {
          get_async( generator.call, :follow_redirect => true ) do |res|
            gathered += 1

            if gathered == generators.size * precision
              @@_404_gathered << path.hash
              callback.call is_404?( path, body )
            else
              @@_404[path][i]['rdiff_now'] ||= false

              if !@@_404[path][i]['body']
                @@_404[path][i]['body'] = res.body
              else
                @@_404[path][i]['rdiff_now'] = true
              end

              if @@_404[path][i]['rdiff_now'] && !@@_404[path][i]['rdiff']
                @@_404[path][i]['rdiff'] = Rex::Text.refine( @@_404[path][i]['body'], res.body )
              end
            end
          end
        }
      end
    else
      callback.call is_404?( path, body )
    end
  end

  nil
end

#get(url, opts = {}) ⇒ Response?

Sends a synchronous GET request.

Parameters:

  • url (String, URI)

    The URL to request.

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

    Additional request options.

Returns:

  • (Response, nil)

    The received response.



251
252
253
# File 'lib/msf/core/auxiliary/web/http.rb', line 251

def get( url, opts = {} )
  request( url, opts.merge( :method => :get ) )
end

#get_async(url, opts = {}) {|response| ... } ⇒ Queue

Enqueues a GET request.

Parameters:

  • url (String, URI)

    The URL to request.

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

    Additional request options.

Yields:

  • (response)

    Processes the response after execution.

Returns:

  • (Queue)

    The internal request queue.



232
233
234
# File 'lib/msf/core/auxiliary/web/http.rb', line 232

def get_async( url, opts = {}, &callback )
  request_async( url, opts.merge( :method => :get ), &callback )
end

#if_not_custom_404(path, body) { ... } ⇒ void

This method returns an undefined value.

Yields only when the supplied response body does not appear to be a custom 404 page.

Parameters:

  • path (String)

    The original request path.

  • body (String)

    The response body to compare.

Yields:

  • Runs when the body is not identified as a custom 404.



270
271
272
# File 'lib/msf/core/auxiliary/web/http.rb', line 270

def if_not_custom_404( path, body, &callback )
  custom_404?( path, body ) { |b| callback.call if !b }
end

#post(url, opts = {}) ⇒ Response?

Sends a synchronous POST request.

Parameters:

  • url (String, URI)

    The URL to request.

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

    Additional request options.

Returns:

  • (Response, nil)

    The received response.



260
261
262
# File 'lib/msf/core/auxiliary/web/http.rb', line 260

def post( url, opts = {} )
  request( url, opts.merge( :method => :post ) )
end

#post_async(url, opts = {}) {|response| ... } ⇒ Queue

Enqueues a POST request.

Parameters:

  • url (String, URI)

    The URL to request.

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

    Additional request options.

Yields:

  • (response)

    Processes the response after execution.

Returns:

  • (Queue)

    The internal request queue.



242
243
244
# File 'lib/msf/core/auxiliary/web/http.rb', line 242

def post_async( url, opts = {}, &callback )
  request_async( url, opts.merge( :method => :post ), &callback )
end

#request(url, opts = {}) ⇒ Response?

Sends an HTTP request and optionally follows redirect responses.

Parameters:

  • url (String, URI)

    The URL to request.

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

    Request options forwarded to #_request.

Options Hash (opts):

  • :follow_redirect (Boolean)

    Whether to follow Location headers.

Returns:

  • (Response, nil)

    The final response, or nil when the redirect limit is exceeded.



204
205
206
207
208
209
210
211
212
213
# File 'lib/msf/core/auxiliary/web/http.rb', line 204

def request( url, opts = {} )
  rlimit = self.redirect_limit

  while rlimit >= 0
    rlimit -= 1
    res = _request( url, opts )
    return res if !opts[:follow_redirect] || !url = res.headers['location']
  end
  nil
end

#request_async(url, opts = {}) {|response| ... } ⇒ Queue

Enqueues a request to be sent later by #run.

Parameters:

  • url (String, URI)

    The URL to request.

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

    Request options.

Yields:

  • (response)

    Processes the response after execution.

Yield Parameters:

  • response (Response)

    The completed response.

Returns:

  • (Queue)

    The internal request queue.



222
223
224
# File 'lib/msf/core/auxiliary/web/http.rb', line 222

def request_async( url, opts = {}, &callback )
  queue Request.new( url, opts, &callback )
end

#runvoid

This method returns an undefined value.

Executes all queued asynchronous requests using the configured thread pool size.

Callback exceptions are reported through the parent module and isolated from the rest of the queue processing.



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
# File 'lib/msf/core/auxiliary/web/http.rb', line 172

def run
  return if @queue.empty?

  tl = []
  loop do
    while tl.size <= (opts[:max_threads] || 5) && !@queue.empty? && (req = @queue.pop)
      tl << framework.threads.spawn( "#{self.class.name} - #{req})", false, req ) do |request|
        # Keep callback failures isolated.
        begin
          request.handle_response request( request.url, request.opts )
        rescue => e
          print_error e.to_s
          e.backtrace.each { |l| print_error l }
        end
      end
    end

    break if tl.empty?
    tl.reject! { |t| !t.alive? }

    select( nil, nil, nil, 0.05 )
  end

  call_after_run_blocks
end