id: CVE-2026-31431 info: name: Copy Fail - Linux Kernel Local Privilege Escalation via AF_ALG author: ritikchaddha severity: high description: | In the Linux kernel, the following vulnerability has been resolved: crypto: algif_aead - Revert to operating out-of-place This mostly reverts commit 72548b093ee3 except for the copying of the associated data. There is no benefit in operating in-place in algif_aead since the source and destination come from different mappings. Get rid of all the complexity added for in-place operation and just copy the AD directly. impact: | A local attacker can escalate from any unprivileged user to root. The exploit: remediation: | Update the Linux kernel to a patched version. Upstream: 6.18.22+, 6.19.12+, 7.0+, LTS backports: 6.14.5+, 6.13.13+, 6.12.25+, 6.6.87+, 6.1.137+. If immediate patching is not possible, disable or blacklist the algif_aead kernel module reference: - https://discourse.ubuntu.com/t/fixes-available-for-cve-2026-31431-copy-fail-linux-kernel-local-privilege-escalation-vulnerability/81498 - https://cert.europa.eu/publications/security-advisories/2026-005 - https://xint.io/blog/copy-fail-linux-distributions - https://nvd.nist.gov/vuln/detail/CVE-2026-31431 classification: cvss-metrics: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H cvss-score: 7.8 cve-id: CVE-2026-31431 epss-score: 0.94545 epss-percentile: 0.99846 cwe-id: CWE-787 cpe: cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* metadata: verified: true max-request: 3 vendor: linux product: linux_kernel tags: cve,cve2026,code,kernel,linux,privesc,local,lpe,kev,vkev,vuln self-contained: true flow: code(1) && code(2) && code(3) code: - engine: - sh - bash source: | whoami matchers: - type: word part: response words: - "root" negative: true internal: true - engine: - sh - bash source: | uname -r extractors: - type: regex name: kernel_version part: response regex: - '^[0-9]+\.[0-9]+\.[0-9]+' internal: true - engine: - sh - bash source: | KVER=$(uname -r) MAJOR=$(echo "$KVER" | cut -d. -f1) MINOR=$(echo "$KVER" | cut -d. -f2) PATCH=$(echo "$KVER" | cut -d. -f3 | cut -d- -f1) VULNERABLE=0 # Check if algif_aead module is loaded or built-in (required attack surface) AEAD_PRESENT=0 if lsmod 2>/dev/null | grep -q "algif_aead"; then AEAD_PRESENT=1 elif [ -f /proc/modules ] && grep -q "algif_aead" /proc/modules 2>/dev/null; then AEAD_PRESENT=1 elif [ -f "/lib/modules/$KVER/kernel/crypto/algif_aead.ko" ] || \ [ -f "/lib/modules/$KVER/kernel/crypto/algif_aead.ko.zst" ] || \ [ -f "/lib/modules/$KVER/kernel/crypto/algif_aead.ko.xz" ]; then AEAD_PRESENT=1 elif [ -f "/boot/config-$KVER" ] && grep -q "Cy" "/boot/config-$KVER" 2>/dev/null; then AEAD_PRESENT=1 elif [ -f /proc/config.gz ] && zcat /proc/config.gz 2>/dev/null | grep -q "Cy"; then AEAD_PRESENT=1 fi # Introduced in ~4.14 (commit 72548b093ee3). Patched upstream: 6.18.22, 6.19.12, 7.0+ # LTS backports: 6.14.5, 6.13.13, 6.12.25, 6.6.87, 6.1.137 if [ "$MAJOR" -eq 4 ] && [ "$MINOR" -ge 14 ]; then VULNERABLE=1 elif [ "$MAJOR" -eq 5 ]; then VULNERABLE=1 elif [ "$MAJOR" -eq 6 ]; then if [ "$MINOR" -le 0 ]; then VULNERABLE=1 elif [ "$MINOR" -eq 1 ] && [ "$PATCH" -le 136 ]; then VULNERABLE=1 elif [ "$MINOR" -ge 2 ] && [ "$MINOR" -le 5 ]; then VULNERABLE=1 elif [ "$MINOR" -eq 6 ] && [ "$PATCH" -le 86 ]; then VULNERABLE=1 elif [ "$MINOR" -ge 7 ] && [ "$MINOR" -le 11 ]; then VULNERABLE=1 elif [ "$MINOR" -eq 12 ] && [ "$PATCH" -le 24 ]; then VULNERABLE=1 elif [ "$MINOR" -eq 13 ] && [ "$PATCH" -le 12 ]; then VULNERABLE=1 elif [ "$MINOR" -eq 14 ] && [ "$PATCH" -le 4 ]; then VULNERABLE=1 elif [ "$MINOR" -ge 15 ] && [ "$MINOR" -le 17 ]; then VULNERABLE=1 elif [ "$MINOR" -eq 18 ] && [ "$PATCH" -le 21 ]; then VULNERABLE=1 elif [ "$MINOR" -eq 19 ] && [ "$PATCH" -le 11 ]; then VULNERABLE=1 fi fi if [ "$VULNERABLE" -eq 1 ]; then if [ "$AEAD_PRESENT" -eq 1 ]; then echo "VULNERABLE: kernel $KVER - algif_aead module present, version in affected range" else echo "VULNERABLE: kernel $KVER - version in affected range (algif_aead module status unknown, may be loadable)" fi else echo "NOT_VULNERABLE: kernel $KVER" fi matchers-condition: and matchers: - type: word part: response words: - "VULNERABLE:" - type: word part: response words: - "NOT_VULNERABLE" negative: true extractors: - type: regex name: detection_detail part: response regex: - 'VULNERABLE:.*' # digest: 4a0a00473045022054183d61f89150f3424dbb727ad27927bd7b451e53cb81327a08c4f198f3a45d022100852840af60879a6a4ffbf1f3b312c9f0731b64d90d2f8e6550b8f9f6d2f689db:922c64590222798bb761d5b6d8e72950