# -*- coding: binary -*-

module Msf

###
#
# This module exposes methods that may be useful to exploits that deal with
# webservers.
#
###
module Exploit::Remote::Web
  include Exploit::Remote::Tcp
  include Exploit::Remote::HttpClient

  # Default value for #web_payload_stub
  WEB_PAYLOAD_STUB = '!payload!'

  #
  # Optional stub to be replaced with the exploit payload.
  #
  # Default stub is 'WEB_PAYLOAD_STUB'.
  #
  attr_accessor :web_payload_stub

  # Creates an instance of a Telnet exploit module.
  #
  def initialize( info = {} )
    super

    register_options([
      OptString.new( 'PATH',    [ true,  'The path to the vulnerable script.', '/' ] ),
      OptString.new( 'GET',     [ false, "GET parameters. ('foo=bar&vuln=#{WEB_PAYLOAD_STUB}', #{WEB_PAYLOAD_STUB} will be substituted with the payload.)", "" ] ),
      OptString.new( 'POST',    [ false, "POST parameters. ('foo=bar&vuln=#{WEB_PAYLOAD_STUB}', #{WEB_PAYLOAD_STUB} will be substituted with the payload.)", "" ] ),
      OptString.new( 'COOKIES', [ false, "Cookies to be sent with the request. ('foo=bar;vuln=#{WEB_PAYLOAD_STUB}', #{WEB_PAYLOAD_STUB} will be substituted with the payload.)", "" ] ),
      OptString.new( 'HEADERS', [ false, "Headers to be sent with the request. ('User-Agent=bar&vuln=#{WEB_PAYLOAD_STUB}', #{WEB_PAYLOAD_STUB} will be substituted with the payload.)", "" ] ),
    ], self.class )

    self.web_payload_stub = WEB_PAYLOAD_STUB
  end

  def path
    Rex::Text.uri_encode( substitute_web_payload_stub( datastore['PATH'] ) )
  end

  def get
    substitute_in_hash( parse_query( datastore['GET'] ) )
  end

  def post
    substitute_in_hash( parse_query( datastore['POST'] ) )
  end

  def cookies
    substitute_web_payload_stub( datastore['COOKIES'], ',;' )
  end

  def headers
    substitute_in_hash( parse_query( datastore['HEADERS'] ) )
  end

  def method
    post.empty? ? 'GET' : 'POST'
  end

  def check
    path = datastore['PATH']
    print_status "Checking #{path}"

    response = send_request_raw( 'uri' => path )
    return Exploit::CheckCode::Detected if response.code == 200

    print_error "Server responded with #{response.code}"
    Exploit::CheckCode::Unknown
  end

  def exploit
    print_status "Sending HTTP request for #{path}"
    res = perform_request
    if res
      print_status "The server responded with HTTP status code #{res.code}."
    else
      print_status 'The server did not respond to our request.'
    end
    handler
  end

  def tries
    1
  end

  private

  def perform_request
    send_request_cgi({
      'global'    => true,
      'uri'       => path,
      'method'    => method,
      'vars_get'  => get,
      'vars_post' => post,
      'headers'   => headers,
      'cookie'    => cookies
    }, 10 )
  end

  #
  # Converts a URI query string into a key=>value hash.
  #
  def parse_query( query, sep = '&' )
    query = query.to_s
    return {} if query.empty?

    query.split( sep ).inject({}) do |h, part|
      k, v = part.split( '=', 2 )
      h[k.to_s] = v.to_s
      h
    end
  end

  #
  # With what to replace 'WEB_PAYLOAD_STUB'.
  #
  # By default returns 'payload.encoded', override as needed.
  #
  def stub_value
    payload.encoded
  end

  #
  # Substitutes 'web_payload_stub' with the exploit payload.
  #
  def substitute_web_payload_stub( str, escape = '' )
    value = stub_value
    value = URI::DEFAULT_PARSER.escape( stub_value, escape ) if !escape.empty?
    str.to_s.gsub( web_payload_stub, value )
  end

  def substitute_in_hash( hash )
    hash.inject({}) do |h, (k, v)|
      h[substitute_web_payload_stub( k )] = substitute_web_payload_stub( v )
      h
    end
  end

end
end
