Fixes #811 by implementing an enumerator for PostgreSQL.
git-svn-id: file:///home/svn/framework3/trunk@8371 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
@@ -27,7 +27,7 @@ module Exploit::Remote::Postgres
|
||||
Opt::RPORT(5432),
|
||||
OptString.new('DATABASE', [ true, 'The database to authenticate against', 'template1']),
|
||||
OptString.new('USERNAME', [ true, 'The username to authenticate as', 'postgres']),
|
||||
OptString.new('PASSWORD', [ true, 'The password for the specified username', '']),
|
||||
OptString.new('PASSWORD', [ false, 'The password for the specified username. Leave blank for a random password.', '']),
|
||||
OptBool.new('VERBOSE', [false, 'Enable verbose output', false]),
|
||||
OptString.new('SQL', [ false, 'The SQL query to execute', 'select version()']),
|
||||
OptBool.new('RETURN_ROWSET', [false, "Set to true to see query result sets", true])
|
||||
@@ -78,16 +78,19 @@ module Exploit::Remote::Postgres
|
||||
def postgres_logout
|
||||
ip = datastore['RHOST']
|
||||
port = datastore['RPORT']
|
||||
verbose = datastore['VERBOSE']
|
||||
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
|
||||
end
|
||||
print_status "#{ip}:#{port} Postgres - Disconnected" if datastore['VERBOSE']
|
||||
print_status "#{ip}:#{port} Postgres - Disconnected" if verbose
|
||||
end
|
||||
|
||||
# If not currently connected, postgres_query will attempt to connect. If an
|
||||
# error is encountered while executing the query, it will return with
|
||||
# :error ; otherwise, it will return with :complete.
|
||||
# TODO: move print_status up to the module; functions like this should just
|
||||
# return things like error codes and :status and the like.
|
||||
def postgres_query(sql=nil,doprint=false)
|
||||
ip = datastore['RHOST']
|
||||
port = datastore['RPORT']
|
||||
@@ -115,8 +118,8 @@ module Exploit::Remote::Postgres
|
||||
return :error
|
||||
end
|
||||
postgres_print_reply(resp,sql) if doprint
|
||||
print_good "#{ip}:#{port} Postgres - Command complete."
|
||||
return :complete
|
||||
print_good "#{ip}:#{port} Postgres - Command complete." if datastore['VERBOSE']
|
||||
return resp
|
||||
end
|
||||
end
|
||||
|
||||
@@ -143,5 +146,80 @@ module Exploit::Remote::Postgres
|
||||
return :complete
|
||||
end
|
||||
|
||||
# postgres_fingerprint attempts to fingerprint a remote Postgresql instance,
|
||||
# inferring version number from the failed authentication messages.
|
||||
def postgres_fingerprint(args={})
|
||||
postgres_logout if self.postgres_conn
|
||||
db = args[:database] || datastore['DATABASE']
|
||||
username = args[:username] || datastore['USERNAME']
|
||||
password = args[:password] || datastore['PASSWORD']
|
||||
rhost = args[:server] || datastore['RHOST']
|
||||
rport = args[:port] || datastore['RPORT']
|
||||
uri = "tcp://#{rhost}:#{rport}"
|
||||
verbose = args[:verbose] || datastore['VERBOSE']
|
||||
begin
|
||||
self.postgres_conn = Connection.new(db,username,password,uri)
|
||||
rescue RuntimeError => e
|
||||
version_hash = analyze_auth_error e
|
||||
return version_hash
|
||||
end
|
||||
if self.postgres_conn # Just ask for the version.
|
||||
resp = postgres_query("select version()",false)
|
||||
ver = resp.rows[0][0].split(/\s/)[1]
|
||||
return {:auth => ver}
|
||||
end
|
||||
end
|
||||
|
||||
# Matches up filename, line number, and routine with a version.
|
||||
# These all come from source builds of Postgres. TODO: check
|
||||
# in on the binary distros, see if they're different.
|
||||
def analyze_auth_error(e)
|
||||
fname,fline,froutine = e.to_s.split("\t")[3,3]
|
||||
fingerprint = "#{fname}:#{fline}:#{froutine}"
|
||||
case fingerprint
|
||||
|
||||
when "Fauth.c:L395:Rauth_failed" ; return {:preauth => "7.4.26-27"} # Failed (bad db, bad credentials)
|
||||
when "Fpostinit.c:L264:RInitPostgres" ; return {:preauth => "7.4.26-27"} # Failed (bad db, good credentials)
|
||||
when "Fauth.c:L452:RClientAuthentication" ; return {:preauth => "7.4.26-27"} # Rejected (maybe good, but not allowed due to pg_hba.conf)
|
||||
|
||||
when "Fauth.c:L400:Rauth_failed" ; return {:preauth => "8.0.22-23"} # Failed (bad db, bad credentials)
|
||||
when "Fpostinit.c:L274:RInitPostgres" ; return {:preauth => "8.0.22-23"} # Failed (bad db, good credentials)
|
||||
when "Fauth.c:L457:RClientAuthentication" ; return {:preauth => "8.0.22-23"} # Rejected (maybe good)
|
||||
|
||||
when "Fauth.c:L337:Rauth_failed" ; return {:preauth => "8.1.18-19"} # Failed (bad db, bad credentials)
|
||||
when "Fpostinit.c:L354:RInitPostgres" ; return {:preauth => "8.1.18-19"} # Failed (bad db, good credentials)
|
||||
when "Fauth.c:L394:RClientAuthentication" ; return {:preauth => "8.1.18-19"} # Rejected (maybe good)
|
||||
|
||||
when "Fauth.c:L362:Rauth_failed" ; return {:preauth => "8.2.14-15"} # Failed (bad db, bad credentials)
|
||||
when "Fpostinit.c:L319:RInitPostgres" ; return {:preauth => "8.2.14-15"} # Failed (bad db, good credentials)
|
||||
when "Fauth.c:L419:RClientAuthentication" ; return {:preauth => "8.2.14-15"} # Rejected (maybe good)
|
||||
|
||||
when "Fauth.c:L1003:Rauth_failed" ; return {:preauth => "8.3.8"} # Failed (bad db, bad credentials)
|
||||
when "Fpostinit.c:L388:RInitPostgres" ; return {:preauth => "8.3.8-9"} # Failed (bad db, good credentials)
|
||||
when "Fauth.c:L1060:RClientAuthentication" ; return {:preauth => "8.3.8"} # Rejected (maybe good)
|
||||
|
||||
when "Fauth.c:L1017:Rauth_failed" ; return {:preauth => "8.3.9"} # Failed (bad db, bad credentials)
|
||||
when "Fauth.c:L1074:RClientAuthentication" ; return {:preauth => "8.3.9"} # Rejected (maybe good, but not allowed due to pg_hba.conf)
|
||||
|
||||
when "Fauth.c:L258:Rauth_failed" ; return {:preauth => "8.4.1"} # Failed (bad db, bad credentials)
|
||||
when "Fpostinit.c:L422:RInitPostgres" ; return {:preauth => "8.4.1-2"} # Failed (bad db, good credentials)
|
||||
when "Fauth.c:L349:RClientAuthentication" ; return {:preauth => "8.4.1"} # Rejected (maybe good)
|
||||
|
||||
when "Fauth.c:L273:Rauth_failed" ; return {:preauth => "8.4.2"} # Failed (bad db, bad credentials)
|
||||
when "Fauth.c:L364:RClientAuthentication" ; return {:preauth => "8.4.2"} # Rejected (maybe good)
|
||||
|
||||
else
|
||||
return {:unknown => fingerprint}
|
||||
end
|
||||
end
|
||||
|
||||
def postgres_password
|
||||
if datastore['PASSWORD'].to_s.size > 0
|
||||
datastore['PASSWORD'].to_s
|
||||
else
|
||||
Rex::Text.rand_text_english(rand(6)+2)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
##
|
||||
# $Id$
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::Postgres
|
||||
include Msf::Auxiliary::Scanner
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
# Creates an instance of this module.
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'PostgreSQL Login Utility',
|
||||
'Description' => %q{
|
||||
Enumerates the verion of PostgreSQL servers.
|
||||
},
|
||||
'Author' => [ 'todb' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'www.postgresql.org' ]
|
||||
],
|
||||
'Version' => '$Revision$' # 2009-02-05
|
||||
))
|
||||
|
||||
register_options([ ], self.class) # None needed.
|
||||
|
||||
deregister_options('SQL', 'RETURN_ROWSET')
|
||||
end
|
||||
|
||||
# Loops through each host in turn. Note the current IP address is both
|
||||
# ip and datastore['RHOST']
|
||||
def run_host(ip)
|
||||
user = datastore['USERNAME']
|
||||
pass = postgres_password
|
||||
do_fingerprint(user,pass,datastore['DATABASE'],datastore['VERBOSE'])
|
||||
end
|
||||
|
||||
# Alias for RHOST
|
||||
def rhost
|
||||
datastore['RHOST']
|
||||
end
|
||||
|
||||
# Alias for RPORT
|
||||
def rport
|
||||
datastore['RPORT']
|
||||
end
|
||||
|
||||
# Test the connection with Rex::Socket before handing
|
||||
# off to Postgres-PR, since Postgres-PR takes forever
|
||||
# to return from connection errors. TODO: convert
|
||||
# Postgres-PR to use Rex::Socket natively to avoid
|
||||
# this double-connect business.
|
||||
def test_connection
|
||||
begin
|
||||
sock = Rex::Socket::Tcp.create(
|
||||
'PeerHost' => rhost,
|
||||
'PeerPort' => rport
|
||||
)
|
||||
rescue Rex::ConnectionError
|
||||
print_error "#{rhost}:#{rport} Connection Error: #{$!}" if datastore['VERBOSE']
|
||||
raise $!
|
||||
end
|
||||
end
|
||||
|
||||
# Test the connection, then actually do all the fingerprinting.
|
||||
def do_fingerprint(user=nil,pass=nil,database=nil,verbose=false)
|
||||
begin
|
||||
test_connection
|
||||
rescue Rex::ConnectionError
|
||||
return :done
|
||||
end
|
||||
msg = "#{rhost}:#{rport} Postgres -"
|
||||
password = pass || postgres_password
|
||||
print_status("#{msg} Trying username:'#{user}' with password:'#{password}' against #{rhost}:#{rport} on database '#{database}'") if verbose
|
||||
result = postgres_fingerprint(
|
||||
:db => database,
|
||||
:username => user,
|
||||
:password => password
|
||||
)
|
||||
if result[:auth]
|
||||
print_good "#{rhost}:#{rport} Postgres - Logged in to '#{db}' with '#{user}':'#{password}'" if verbose
|
||||
print_good "#{rhost}:#{rport} Postgres - Version #{result[:auth]} (Post-Auth)"
|
||||
elsif result[:preauth]
|
||||
print_good "#{rhost}:#{rport} Postgres - Version #{result[:preauth]} (Pre-Auth)"
|
||||
else # It's something we don't know yet
|
||||
print_status "#{rhost}:#{rport} Postgres - Authentication Error Fingerprint: #{result[:unknown]}" if datastore['VERBOSE']
|
||||
print_error "#{rhost}:#{rport} Postgres - Version Unknown (Pre-Auth)"
|
||||
end
|
||||
|
||||
# Reporting
|
||||
|
||||
report_service(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:name => "postgresql",
|
||||
:info => result.values.first
|
||||
)
|
||||
|
||||
if self.postgres_conn
|
||||
report_auth_info(
|
||||
:host => rhost,
|
||||
:proto => "postgresql",
|
||||
:user => user,
|
||||
:pass => password,
|
||||
:targ_host => rhost,
|
||||
:targ_port => rport
|
||||
)
|
||||
end
|
||||
|
||||
if result[:unknown]
|
||||
report_note(
|
||||
:host => rhost,
|
||||
:proto => 'postgresql',
|
||||
:port => rport,
|
||||
:data => "Unknown Pre-Auth fingerprint: #{result[:unknown]}"
|
||||
)
|
||||
end
|
||||
|
||||
# Logout
|
||||
|
||||
postgres_logout
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user