# -*- coding: binary -*-

module Msf

###
#
# This module exposes methods that may be useful to exploits that deal with
# servers that speak the telnet protocol.
#
###
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.encode( 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
