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.94016
    epss-percentile: 0.99835
    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: 4a0a00473045022100dbeedb1717314af76645389c9810f609d65fbd96d11d31e279c663f5779937360220059e7e5035277df39beecd3a191dbcefc10c68a715b38a3a4a8e05b37580f3f8:922c64590222798bb761d5b6d8e72950