ronin-rb/ronin-vulns

View on GitHub
lib/ronin/vulns/web_vuln/http_request.rb

Summary

Maintainability
A
1 hr
Test Coverage
# frozen_string_literal: true
#
# ronin-vulns - A Ruby library for blind vulnerability testing.
#
# Copyright (c) 2022-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# ronin-vulns is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ronin-vulns is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ronin-vulns.  If not, see <https://www.gnu.org/licenses/>.
#

require 'ronin/vulns/vuln'
require 'ronin/support/network/http/cookie'

require 'uri/query_params'

module Ronin
  module Vulns
    class WebVuln < Vuln
      #
      # Represents a HTTP request.
      #
      # @api private
      #
      class HTTPRequest

        # The URL of the request.
        #
        # @return [URI::HTTP]
        attr_reader :url

        # The HTTP request method.
        #
        # @return [:copy, :delete, :get, :head, :lock, :mkcol, :move,
        #         :options, :patch, :post, :propfind, :proppatch, :put,
        #         :trace, :unlock]
        attr_reader :request_method

        # The user to authenticate as.
        #
        # @return [String, nil]
        attr_reader :user

        # The password to authenticate with.
        #
        # @return [String, nil]
        attr_reader :password

        # The optional HTTP `Referer` header for the request.
        #
        # @return [String, nil]
        attr_reader :referer

        # The query param for the request.
        #
        # @return [Hash{String,Symbol => String}, nil]
        attr_reader :query_params

        # Additional `Cookie` header for the request.
        #
        # @return [Ronin::Support::Network::HTTP::Cookie, nil]
        attr_reader :cookie

        # Additional HTTP header names and values to add to the request.
        #
        # @return [Hash{Symbol,String => String}, nil]
        attr_reader :headers

        # The form data that may be sent in the body of the request.
        #
        # @return [Hash{String => Object}, nil]
        attr_reader :form_data

        #
        # Initializes the HTTP request object.
        #
        # @param [URI::HTTP] url
        #   The URL to test or exploit.
        #
        # @param [:copy, :delete, :get, :head, :lock, :mkcol, :move,
        #         :options, :patch, :post, :propfind, :proppatch, :put,
        #         :trace, :unlock] request_method
        #   The HTTP request mehtod for each request.
        #
        # @param [String, nil] user
        #   The user to authenticate as.
        #
        # @param [String, nil] password
        #   The password to authenticate with.
        #
        # @param [Hash{Symbol,String => String}, nil] query_params
        #   Additional URL query params for the request.
        #
        # @param [Hash{Symbol,String => String}, nil] headers
        #   Additional HTTP header names and values to add to the request.
        #
        # @param [String, Hash{String => String}, nil] cookie
        #   Additional `Cookie` header for the request..
        #
        # @param [Hash, nil] form_data
        #   The form data that may be sent in the body of the request.
        #
        def initialize(url, request_method: :get,
                            user:           nil,
                            password:       nil,
                            referer:        nil,
                            query_params:   nil,
                            headers:        nil,
                            cookie:         nil,
                            form_data:      nil)
          @url = url

          if query_params && !query_params.empty?
            @url = url.dup

            @url.query_params = query_params
          end

          @request_method = request_method
          @user           = user
          @password       = password
          @referer        = referer

          @query_params = query_params
          @cookie       = if cookie
                            Support::Network::HTTP::Cookie.new(cookie)
                          end
          @headers      = headers
          @form_data    = form_data
        end

        #
        # Converts the HTTP request to a `curl` command.
        #
        # @return [String]
        #
        def to_curl
          escape = ->(str) { "'#{str.to_s.tr("'","\\'")}'" }

          command = ['curl']

          if @request_method != :get
            command << '--request' << @request_method.upcase
          end

          if (@user || @password)
            command << '--user' << escape.call("#{@user}:#{@password}")
          end

          if @referer
            command << '--referer' << escape.call(@referer)
          end

          if (@cookie && !@cookie.empty?)
            command << '--cookie' << escape.call(@cookie)
          end

          if @headers
            @headers.each do |name,value|
              command << '--header' << escape.call("#{name}: #{value}")
            end
          end

          if (@form_data && !@form_data.empty?)
            form_string = URI.encode_www_form(@form_data)
            command << '--form-string' << escape.call(form_string)
          end

          command << escape.call(@url)

          return command.join(' ')
        end

        # HTTP newline deliminator.
        CRLF = "\r\n"

        #
        # Converts the HTTP request to a raw HTTP request.
        #
        # @return [String]
        #
        def to_http
          request = []
          request << "#{@request_method.upcase} #{@url.request_uri} HTTP/1.1"

          if (@form_data && !@form_data.empty?)
            request << "Content-Type: x-www-form-urlencoded"
          end

          if (@user || @password)
            basic_auth = ["#{@user}:#{@password}"].pack('m0')
            request << "Authorization: Basic #{basic_auth}"
          end

          request << "Referer: #{@referer}" if @referer
          request << "Cookie: #{@cookie}"   if (@cookie && !@cookie.empty?)

          if @headers
            @headers.each do |name,value|
              request << "#{name}: #{value}"
            end
          end

          if (@form_data && !@form_data.empty?)
            request << ''
            request << URI.encode_www_form(@form_data)
          end

          return request.join(CRLF) << CRLF
        end
      end
    end
  end
end