Merge pull request #21144 from Nayeraneru/docs/msf-auxiliary-web-http-yard
Enhance documentation for HTTP module methods
This commit is contained in:
@@ -11,11 +11,16 @@ require 'uri'
|
||||
module Msf
|
||||
class Auxiliary::Web::HTTP
|
||||
|
||||
# Wraps a queued HTTP request and the callbacks that should process its response.
|
||||
class Request
|
||||
attr_accessor :url
|
||||
attr_reader :opts
|
||||
attr_reader :callbacks
|
||||
|
||||
# @param url [String, URI] The target URL to request.
|
||||
# @param opts [Hash] Request options forwarded to {HTTP#request}.
|
||||
# @yield [response] Handles the completed response.
|
||||
# @yieldparam response [Response] The received response.
|
||||
def initialize( url, opts = {}, &callback )
|
||||
@url = url.to_s.dup
|
||||
@opts = opts.dup
|
||||
@@ -25,17 +30,27 @@ class Auxiliary::Web::HTTP
|
||||
@callbacks = [callback].compact
|
||||
end
|
||||
|
||||
# @return [Symbol] The HTTP verb used when the request is executed.
|
||||
def method
|
||||
opts[:method]
|
||||
end
|
||||
|
||||
|
||||
# Calls each registered callback with the received response.
|
||||
#
|
||||
# @param response [Response] The response to dispatch.
|
||||
# @return [Array<Proc>] The stored callbacks.
|
||||
def handle_response( response )
|
||||
callbacks.each { |c| c.call response }
|
||||
end
|
||||
end
|
||||
|
||||
# Decorates {Rex::Proto::Http::Response} with convenience helpers used by web auxiliaries.
|
||||
class Response < Rex::Proto::Http::Response
|
||||
|
||||
# Builds a response object from a Rex HTTP response.
|
||||
#
|
||||
# @param response [Rex::Proto::Http::Response, nil] The response returned by Rex.
|
||||
# @return [Response] A wrapped response, or {empty} when no response was received.
|
||||
def self.from_rex_response( response )
|
||||
return empty if !response
|
||||
|
||||
@@ -46,20 +61,28 @@ class Auxiliary::Web::HTTP
|
||||
r
|
||||
end
|
||||
|
||||
# @return [Response] An empty placeholder response.
|
||||
def self.empty
|
||||
new( 0, '' )
|
||||
end
|
||||
|
||||
# Builds an empty response marked as having timed out.
|
||||
#
|
||||
# @return [Response] A timeout sentinel response.
|
||||
def self.timed_out
|
||||
r = empty
|
||||
r.timed_out
|
||||
r
|
||||
end
|
||||
|
||||
# @return [Boolean] True when the request timed out.
|
||||
def timed_out?
|
||||
!!@timed_out
|
||||
end
|
||||
|
||||
# Marks this response as timed out.
|
||||
#
|
||||
# @return [TrueClass] Always returns true.
|
||||
def timed_out
|
||||
@timed_out = true
|
||||
end
|
||||
@@ -73,6 +96,13 @@ class Auxiliary::Web::HTTP
|
||||
attr_accessor :redirect_limit
|
||||
attr_accessor :username , :password, :domain
|
||||
|
||||
# @param opts [Hash] Configuration for the HTTP helper.
|
||||
# @option opts [Object] :framework The active framework instance used for threading.
|
||||
# @option opts [Object] :parent The owning module used for error reporting.
|
||||
# @option opts [Hash] :headers Additional HTTP headers to send with every request.
|
||||
# @option opts [String] :cookie_string Cookie header value to set by default.
|
||||
# @option opts [Hash] :auth Default authentication options.
|
||||
# @option opts [Integer] :redirect_limit Maximum redirects to follow.
|
||||
def initialize( opts = {} )
|
||||
@opts = opts.dup
|
||||
|
||||
@@ -100,10 +130,17 @@ class Auxiliary::Web::HTTP
|
||||
@after_run_blocks = []
|
||||
end
|
||||
|
||||
# Registers a callback to be executed after {#run} drains the request queue.
|
||||
#
|
||||
# @yield A block to run after all queued requests finish.
|
||||
# @return [Array<Proc>] The accumulated after-run callbacks.
|
||||
def after_run( &block )
|
||||
@after_run_blocks << block
|
||||
end
|
||||
|
||||
# Creates a Rex HTTP client using the configured target and authentication defaults.
|
||||
#
|
||||
# @return [Rex::Proto::Http::Client] A configured HTTP client.
|
||||
def connect
|
||||
c = Rex::Proto::Http::Client.new(
|
||||
opts[:target].host,
|
||||
@@ -126,6 +163,12 @@ class Auxiliary::Web::HTTP
|
||||
c
|
||||
end
|
||||
|
||||
# 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.
|
||||
#
|
||||
# @return [void]
|
||||
def run
|
||||
return if @queue.empty?
|
||||
|
||||
@@ -152,6 +195,12 @@ class Auxiliary::Web::HTTP
|
||||
call_after_run_blocks
|
||||
end
|
||||
|
||||
# Sends an HTTP request and optionally follows redirect responses.
|
||||
#
|
||||
# @param url [String, URI] The URL to request.
|
||||
# @param opts [Hash] Request options forwarded to {#_request}.
|
||||
# @option opts [Boolean] :follow_redirect Whether to follow Location headers.
|
||||
# @return [Response, nil] The final response, or nil when the redirect limit is exceeded.
|
||||
def request( url, opts = {} )
|
||||
rlimit = self.redirect_limit
|
||||
|
||||
@@ -163,30 +212,74 @@ class Auxiliary::Web::HTTP
|
||||
nil
|
||||
end
|
||||
|
||||
# Enqueues a request to be sent later by {#run}.
|
||||
#
|
||||
# @param url [String, URI] The URL to request.
|
||||
# @param opts [Hash] Request options.
|
||||
# @yield [response] Processes the response after execution.
|
||||
# @yieldparam response [Response] The completed response.
|
||||
# @return [Queue] The internal request queue.
|
||||
def request_async( url, opts = {}, &callback )
|
||||
queue Request.new( url, opts, &callback )
|
||||
end
|
||||
|
||||
# Enqueues a GET request.
|
||||
#
|
||||
# @param url [String, URI] The URL to request.
|
||||
# @param opts [Hash] Additional request options.
|
||||
# @yield [response] Processes the response after execution.
|
||||
# @return [Queue] The internal request queue.
|
||||
def get_async( url, opts = {}, &callback )
|
||||
request_async( url, opts.merge( :method => :get ), &callback )
|
||||
end
|
||||
|
||||
# Enqueues a POST request.
|
||||
#
|
||||
# @param url [String, URI] The URL to request.
|
||||
# @param opts [Hash] Additional request options.
|
||||
# @yield [response] Processes the response after execution.
|
||||
# @return [Queue] The internal request queue.
|
||||
def post_async( url, opts = {}, &callback )
|
||||
request_async( url, opts.merge( :method => :post ), &callback )
|
||||
end
|
||||
|
||||
# Sends a synchronous GET request.
|
||||
#
|
||||
# @param url [String, URI] The URL to request.
|
||||
# @param opts [Hash] Additional request options.
|
||||
# @return [Response, nil] The received response.
|
||||
def get( url, opts = {} )
|
||||
request( url, opts.merge( :method => :get ) )
|
||||
end
|
||||
|
||||
# Sends a synchronous POST request.
|
||||
#
|
||||
# @param url [String, URI] The URL to request.
|
||||
# @param opts [Hash] Additional request options.
|
||||
# @return [Response, nil] The received response.
|
||||
def post( url, opts = {} )
|
||||
request( url, opts.merge( :method => :post ) )
|
||||
end
|
||||
|
||||
# Yields only when the supplied response body does not appear to be a custom 404 page.
|
||||
#
|
||||
# @param path [String] The original request path.
|
||||
# @param body [String] The response body to compare.
|
||||
# @yield Runs when the body is not identified as a custom 404.
|
||||
# @return [void]
|
||||
def if_not_custom_404( path, body, &callback )
|
||||
custom_404?( path, body ) { |b| callback.call if !b }
|
||||
end
|
||||
|
||||
# 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.
|
||||
#
|
||||
# @param path [String] The original request path.
|
||||
# @param body [String] The response body to classify.
|
||||
# @yieldparam is_custom_404 [Boolean] True when the body matches the custom 404 fingerprint.
|
||||
# @return [nil] Returns nil immediately and reports the result via the callback.
|
||||
def custom_404?( path, body, &callback )
|
||||
return if !path || !body
|
||||
|
||||
@@ -257,25 +350,51 @@ class Auxiliary::Web::HTTP
|
||||
|
||||
private
|
||||
|
||||
# Runs and clears the callbacks registered through {#after_run}.
|
||||
#
|
||||
# @return [void]
|
||||
def call_after_run_blocks
|
||||
while block = @after_run_blocks.pop
|
||||
block.call
|
||||
end
|
||||
end
|
||||
|
||||
# Executes a block while holding the helper's mutex.
|
||||
#
|
||||
# @yield The critical section.
|
||||
# @return [Object] The block result.
|
||||
def synchronize( &block )
|
||||
(@mutex ||= Mutex.new).synchronize( &block )
|
||||
end
|
||||
|
||||
# Compares the supplied body to the cached custom 404 signatures for a path.
|
||||
#
|
||||
# @param path [String] The original request path.
|
||||
# @param body [String] The response body to compare.
|
||||
# @return [Boolean] True when the body matches the cached custom 404 fingerprint.
|
||||
def is_404?( path, body )
|
||||
@@_404[path].each { |_404| return true if Rex::Text.refine( _404['body'], body ) == _404['rdiff'] }
|
||||
false
|
||||
end
|
||||
|
||||
# Appends a request to the internal work queue.
|
||||
#
|
||||
# @param request [Request] The queued request wrapper.
|
||||
# @return [Queue] The internal queue.
|
||||
def queue( request )
|
||||
@queue << request
|
||||
end
|
||||
|
||||
# Issues a single HTTP request without following redirects.
|
||||
#
|
||||
# @param url [String, URI] The URL to request.
|
||||
# @param opts [Hash] Request options.
|
||||
# @option opts [String] :body Optional request body.
|
||||
# @option opts [Integer] :timeout Request timeout in seconds.
|
||||
# @option opts [Hash] :params Query string or POST parameters.
|
||||
# @option opts [Hash] :headers Per-request headers.
|
||||
# @option opts [Hash] :rex Direct Rex request overrides.
|
||||
# @return [Response] The response wrapper.
|
||||
def _request( url, opts = {} )
|
||||
body = opts[:body]
|
||||
timeout = opts[:timeout] || 10
|
||||
@@ -323,6 +442,10 @@ class Auxiliary::Web::HTTP
|
||||
Response.empty
|
||||
end
|
||||
|
||||
# Prints an error through the owning module when available.
|
||||
#
|
||||
# @param message [String] The message to report.
|
||||
# @return [void]
|
||||
def print_error( message )
|
||||
return if !@parent
|
||||
@parent.print_error message
|
||||
|
||||
Reference in New Issue
Block a user