Fixes #730 by fixing up the Postgres query module and nicifying the output.
git-svn-id: file:///home/svn/framework3/trunk@8352 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
44fbe35871
commit
2ffe4abb5d
|
@ -28,7 +28,9 @@ module Exploit::Remote::Postgres
|
||||||
OptString.new('DATABASE', [ true, 'The database to authenticate against', 'template1']),
|
OptString.new('DATABASE', [ true, 'The database to authenticate against', 'template1']),
|
||||||
OptString.new('USERNAME', [ true, 'The username to authenticate as', 'postgres']),
|
OptString.new('USERNAME', [ true, 'The username to authenticate as', 'postgres']),
|
||||||
OptString.new('PASSWORD', [ true, 'The password for the specified username', '']),
|
OptString.new('PASSWORD', [ true, 'The password for the specified username', '']),
|
||||||
OptBool.new('VERBOSE', [false, 'Enable verbose output', false])
|
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])
|
||||||
], Msf::Exploit::Remote::Postgres)
|
], Msf::Exploit::Remote::Postgres)
|
||||||
|
|
||||||
register_autofilter_ports([ 5432 ])
|
register_autofilter_ports([ 5432 ])
|
||||||
|
@ -40,36 +42,34 @@ module Exploit::Remote::Postgres
|
||||||
# :connected, or will return :error, :error_databse, or :error_credentials
|
# :connected, or will return :error, :error_databse, or :error_credentials
|
||||||
# Fun fact: if you get :error_database, it means your username and password
|
# Fun fact: if you get :error_database, it means your username and password
|
||||||
# was accepted (you just failed to guess a correct running database instance).
|
# was accepted (you just failed to guess a correct running database instance).
|
||||||
#
|
# Note that postgres_login will first trigger postgres_logout if the module
|
||||||
# TODO: Clean this up some.
|
# is already connected.
|
||||||
def postgres_login(args={})
|
def postgres_login(args={})
|
||||||
postgres_logout if self.postgres_conn
|
postgres_logout if self.postgres_conn
|
||||||
|
|
||||||
db = args[:database] || datastore['DATABASE']
|
db = args[:database] || datastore['DATABASE']
|
||||||
username = args[:username] || datastore['USERNAME']
|
username = args[:username] || datastore['USERNAME']
|
||||||
password = args[:password] || datastore['PASSWORD']
|
password = args[:password] || datastore['PASSWORD']
|
||||||
ip = args[:server] || datastore['RHOST']
|
ip = args[:server] || datastore['RHOST']
|
||||||
port = args[:port] || datastore['RPORT']
|
port = args[:port] || datastore['RPORT']
|
||||||
uri = "tcp://#{ip}:#{port}"
|
uri = "tcp://#{ip}:#{port}"
|
||||||
|
verbose = args[:verbose] || datastore['VERBOSE']
|
||||||
begin
|
begin
|
||||||
self.postgres_conn = Connection.new(db,username,password,uri)
|
self.postgres_conn = Connection.new(db,username,password,uri)
|
||||||
rescue RuntimeError => e
|
rescue RuntimeError => e
|
||||||
case e.to_s.split("\t")[1]
|
case e.to_s.split("\t")[1]
|
||||||
when "C3D000"
|
when "C3D000"
|
||||||
print_error "#{ip}:#{port} Postgres - Bad database name: #{db} (Credentials '#{username}:#{password}' is OK)"
|
print_error "#{ip}:#{port} Postgres - Bad database name: #{db} (Credentials '#{username}:#{password}' is OK)" if verbose
|
||||||
return :error_database # Note this means the user:pass is good!
|
return :error_database # Note this means the user:pass is good!
|
||||||
when "C28000"
|
when "C28000"
|
||||||
print_error "#{ip}:#{port} Postgres - Bad username or password: '#{username}:#{password}'"
|
print_error "#{ip}:#{port} Postgres - Bad username or password: '#{username}:#{password}'" if verbose
|
||||||
return :error_credentials
|
return :error_credentials
|
||||||
else
|
else
|
||||||
print_error "#{ip}:#{port} Postgres - Error: #{e.inspect}"
|
print_error "#{ip}:#{port} Postgres - Error: #{e.inspect}" if verbose
|
||||||
return :error
|
return :error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.postgres_conn
|
if self.postgres_conn
|
||||||
print_good "#{ip}:#{port} Postgres - Logged in to '#{db}' with '#{username}':'#{password}'" if datastore['VERBOSE']
|
print_good "#{ip}:#{port} Postgres - Logged in to '#{db}' with '#{username}':'#{password}'" if verbose
|
||||||
return :connected
|
return :connected
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -82,38 +82,65 @@ module Exploit::Remote::Postgres
|
||||||
self.postgres_conn.close if(self.postgres_conn.kind_of?(Connection) && self.postgres_conn.instance_variable_get("@conn"))
|
self.postgres_conn.close if(self.postgres_conn.kind_of?(Connection) && self.postgres_conn.instance_variable_get("@conn"))
|
||||||
self.postgres_conn = nil
|
self.postgres_conn = nil
|
||||||
end
|
end
|
||||||
print_status "#{ip}:#{port} Postgres - Disconnected." if datastore['VERBOSE']
|
print_status "#{ip}:#{port} Postgres - Disconnected" if datastore['VERBOSE']
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: Deal with malformed queries.
|
# 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.
|
||||||
def postgres_query(sql=nil,doprint=false)
|
def postgres_query(sql=nil,doprint=false)
|
||||||
ip = datastore['RHOST']
|
ip = datastore['RHOST']
|
||||||
port = datastore['RPORT']
|
port = datastore['RPORT']
|
||||||
postgres_login unless self.postgres_conn
|
postgres_login unless self.postgres_conn
|
||||||
|
unless self.postgres_conn
|
||||||
|
print_error "#{ip}:#{port} Postgres - Could not connect! #{datastore['VERBOSE'] ? nil : "(Set VERBOSE to see login errors)"}"
|
||||||
|
return :error
|
||||||
|
end
|
||||||
if self.postgres_conn
|
if self.postgres_conn
|
||||||
sql ||= datastore['SQL']
|
sql ||= datastore['SQL']
|
||||||
print_status "#{ip}:#{port} Postgres - querying with '#{sql}'" if datastore['VERBOSE']
|
print_status "#{ip}:#{port} Postgres - querying with '#{sql}'" if datastore['VERBOSE']
|
||||||
resp = self.postgres_conn.query(sql)
|
begin
|
||||||
postgres_print_reply(resp,sql) if doprint
|
resp = self.postgres_conn.query(sql)
|
||||||
|
rescue RuntimeError => e
|
||||||
|
case e.to_s.split("\t")[1] # Deal with some common errors
|
||||||
|
when "C42601"
|
||||||
|
print_error "#{ip}:#{port} Postgres - Error: Bad SQL Syntax: '#{sql}'"
|
||||||
|
when "C42P01"
|
||||||
|
print_error "#{ip}:#{port} Postgres - Error: Table does not exist: '#{sql}'"
|
||||||
|
when "C42703"
|
||||||
|
print_error "#{ip}:#{port} Postgres - Error: Column does not exist: '#{sql}'"
|
||||||
|
else # Let the user figure out the rest.
|
||||||
|
print_error "#{ip}:#{port} Postgres - Error: SQL statement '#{sql}' returns #{e.inspect}"
|
||||||
|
end
|
||||||
|
return :error
|
||||||
|
end
|
||||||
|
postgres_print_reply(resp,sql) if doprint
|
||||||
|
print_good "#{ip}:#{port} Postgres - Command complete."
|
||||||
|
return :complete
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: write out the error conditions.
|
# If resp is not actually a Connection::Result object, then return
|
||||||
|
# :error (but not an actual Exception, that's up to the caller.
|
||||||
|
# Otherwise, create a rowset using Rex::Ui::Text::Table (if there's
|
||||||
|
# more than 0 rows) and return :complete.
|
||||||
def postgres_print_reply(resp=nil,sql=nil)
|
def postgres_print_reply(resp=nil,sql=nil)
|
||||||
ip = datastore['RHOST']
|
ip = datastore['RHOST']
|
||||||
port = datastore['RPORT']
|
port = datastore['RPORT']
|
||||||
return nil unless resp.kind_of? Connection::Result
|
return :error unless resp.kind_of? Connection::Result
|
||||||
print_status "#{ip}:#{port} Postgres Query: Type: #{resp.cmd_tag} | Query Text: #{sql}"
|
if resp.rows and resp.fields
|
||||||
if resp.rows and resp.fields and resp.rows.size > 0
|
print_status "#{ip}:#{port} Rows Returned: #{resp.rows.size}"
|
||||||
print_status "#{ip}:#{port} Row Count: #{resp.rows.size}"
|
if resp.rows.size > 0
|
||||||
tbl = Rex::Ui::Text::Table.new(
|
tbl = Rex::Ui::Text::Table.new(
|
||||||
'Indent' => 1,
|
'Indent' => 4,
|
||||||
'Header' => "",
|
'Header' => "Query Text: '#{sql}'",
|
||||||
'Columns' => resp.fields.map {|x| x.name}
|
'Columns' => resp.fields.map {|x| x.name}
|
||||||
)
|
)
|
||||||
resp.rows.each {|row| tbl << row.map { |x| x.nil? ? "NIL" : x } }
|
resp.rows.each {|row| tbl << row.map { |x| x.nil? ? "NIL" : x } }
|
||||||
print_line(tbl.to_s)
|
print_line(tbl.to_s)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
return :complete
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,17 +29,23 @@ class Metasploit3 < Msf::Auxiliary
|
||||||
'References' =>
|
'References' =>
|
||||||
[
|
[
|
||||||
[ 'URL', 'www.postgresql.org' ]
|
[ 'URL', 'www.postgresql.org' ]
|
||||||
]
|
],
|
||||||
|
'Version' => '$Revision$'
|
||||||
))
|
))
|
||||||
|
|
||||||
register_options(
|
register_options( [ ], self.class) # None needed.
|
||||||
[
|
end
|
||||||
OptString.new('SQL', [ false, 'The SQL query to execute', 'select version()']),
|
|
||||||
], self.class)
|
def rhost
|
||||||
|
datastore['RHOST']
|
||||||
|
end
|
||||||
|
|
||||||
|
def rport
|
||||||
|
datastore['RPORT']
|
||||||
end
|
end
|
||||||
|
|
||||||
def run
|
def run
|
||||||
postgres_query(datastore['SQL'], true)
|
postgres_query(datastore['SQL'],datastore['RETURN_ROWSET'])
|
||||||
postgres_logout if self.postgres_conn
|
postgres_logout if self.postgres_conn
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue