## https://sploitus.com/exploit?id=8BC9638E-19FB-5CC8-ADA0-8873C15D274C
# CVE Checker for Copy Fail (CVE-2026-31431)
Authors: Chris Foley & Patrick Doyle
Single statically-linked Go binary that classifies a Linux host's exposure
to **CVE-2026-31431**.
The tool is **passive**. It does not exploit the bug. The mechanism probe
(see safety statement below) issues exactly two syscalls and never touches
the vulnerable code path.
---
## What it checks
| Signal | Source | Flag |
|---|---|---|
| Kernel version vs. upstream fix | `uname(2)` | `--kernel-version` |
| Distro changelog mentions the CVE | `apt`/`rpm`/`apk` or on-disk `changelog.Debian.gz` | `--changelog` |
| `algif_aead` module status | `/proc/modules`, `modprobe.d`, `/lib/modules` | `--module` |
| Vulnerable surface reachable | `socket(AF_ALG)` + `bind(authencesn(...))` | `--mechanism` |
| Compiled into vmlinux | `/boot/config-$(uname -r)` or `/proc/config.gz` | (part of `--module`) |
If no check flags are given, all four run.
---
## Output modes
| Mode | Trigger | Use case |
|---|---|---|
| **Pretty styled** (Default) | TTY stdout, no `--format` override, no `NO_COLOR` env | Interactive shell runs |
| **Plain text** | non-TTY stdout, OR `--format=text`, OR `NO_COLOR=1`, OR `--no-color` | Shell Scripts, Pipes |
| **JSON** | `--format=json` | Syslog/SIEM ingest, Shell Scripts |
| **Quiet** | `--quiet` | Exit code only (automation/scripts) |
Force pretty when piped: `cvecheck --pretty | tee report.txt`.
Force plain in a TTY: `cvecheck --no-color` or `NO_COLOR=1 cvecheck`.
---
## Safety statement (mechanism probe)
The probe issues exactly two syscalls `socket(AF_ALG, SOCK_SEQPACKET, 0)`,
and `bind(fd, &SockaddrALG{Type:"aead", Name:"authencesn(hmac(sha256),
cbc(aes))"})`. It's then followed by `close(fd)`. It performs **no** `setsockopt`
for a key, **no** `accept`, **no** `sendmsg`, **no** `splice`, and **no**
pipe creation. The vulnerable code path requires `sendmsg` of crypto data
combined with a `splice` from a page-cache-backed file descriptor; none of
those operations occur, so the bug cannot trigger.
A successful probe means the surface is reachable. Whether the kernel still
contains the bug is decided by the kernel-version + changelog signals.
**Side effect**: a successful `bind` autoloads `algif_aead` via the kernel
module autoloader. The module check runs *before* the mechanism probe so
the loaded-state report is pre-probe. To suppress autoload entirely,
blacklist `algif_aead` first.
---
## Exit codes
| Code | Meaning |
|---|---|
| `0` | `PATCHED` / `NOT_VULNERABLE` / `MITIGATED` / `LIKELY_NOT_EXPLOITABLE` |
| `2` | `VULNERABLE` or `MECHANISM_REACHABLE` (partial scan, kernel-version not checked) |
| `3` | `INCONCLUSIVE` (insufficient signals) |
---
## Distro coverage
| Distro | Detect | Kernel pkg | Notes |
|---|---|---|---|
| Ubuntu / Debian / Mint / Pop!_OS | `apt` | `linux-image-$(uname -r)` | Disk-first changelog read avoids `apt changelog` network fragility on signed kernels |
| RHEL / CentOS / Rocky / Alma / Fedora / Amazon Linux | `rpm` | `kernel` | Ships `algif_aead` built-in (`CONFIG_..._AEAD=y`); blacklist mitigation **not effective** |
| Oracle Linux | `rpm` | `kernel` or `kernel-uek` | UEK detected via `uek` substring in `uname -r` |
| openSUSE / SLES | `rpm` | `kernel-default` | |
| Alpine | `apk` | `linux-lts` / `linux-virt` / etc. | Flavor picked from release suffix |
| Arch / CachyOS / Manjaro / Endeavour / Gentoo | none | n/a | Rolling/source: relies on kernel-version check |
---
## Deploy
### Download Binary
Download the latest binary from releases (amd64/arm64/x86).
### Build from Source
```bash
make build-all # bin/cvecheck-linux-{x86_64,arm64,x86}
scp bin/cvecheck-linux-x86_64 host:/tmp/ # ~3 MiB, static, no glibc dep
ssh host /tmp/cvecheck # pretty styled report
ssh host /tmp/cvecheck --format=json | jq # SIEM-friendly
```
For container/chroot scans, mount the host root somewhere readable and
pass `--root`:
```bash
docker run --rm -v /:/host:ro alpine /tmp/cvecheck --root /host
```
`--root` controls all on-disk lookups (`/etc/os-release`, `modprobe.d`,
`/lib/modules`, `/boot/config-*`, changelog files). The kernel-version and
mechanism probes still touch the **running** kernel via `uname(2)` and
`socket(AF_ALG, ...)`, both necessarily host-bound.
---
## Verdict ladder
Resolved in this precedence (first match wins):
1. `PATCHED`: distro changelog references the CVE ID
2. `NOT_VULNERABLE`: running kernel โฅ upstream fixed version
3. `MITIGATED`: `algif_aead` blacklisted *and* not loaded *and* not built into vmlinux
4. `LIKELY_NOT_EXPLOITABLE`: `AF_ALG` unavailable and module not on disk
5. `MECHANISM_REACHABLE`: surface reachable but kernel version not checked
6. `VULNERABLE`: kernel in vuln range and mechanism reachable
7. `INCONCLUSIVE`: none of the above
If `--mechanism` runs alongside `--kernel-version` and both signal
trouble, the result is `VULNERABLE` with a remediation hint.
---
## Remediation hints
- **Loadable-module distros (Debian, Ubuntu, SUSE, Alpine, Arch)**: blacklist
`algif_aead` until kernel upgrade lands.
```bash
echo 'blacklist algif_aead' | sudo tee /etc/modprobe.d/cve-2026-31431.conf
sudo rmmod algif_aead 2>/dev/null
```
- **Built-in distros (RHEL, CentOS, Rocky, Alma, Oracle, Amazon)**: blacklist
files are no-ops because `CONFIG_CRYPTO_USER_API_AEAD=y` puts the symbol
inside `vmlinux`. Only a kernel upgrade (or `kpatch`-style live patch)
resolves exposure. The tool detects this.
---
## Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| `changelog: error="apt: exit status 100 ... Changelog unavailable for linux-signed-amd64"` | Debian/Ubuntu signed kernel; `apt` mirror does not serve the source-pkg changelog | Error appears only when the on-disk file is missing in addition to the mirror being inaccessable, install `apt-doc` or wait for the mirror. |
| `module: ... config_src=""` | No kernel config readable (no `/boot/config-*`, no `/proc/config.gz`, `/boot` is root-only) | Run as root, or accept that `BuiltIn` cannot be determined. Verdict may still resolves via other signals. |
| `mechanism: error="EAFNOSUPPORT"` | Kernel built without `CONFIG_CRYPTO_USER_API`, so the host is not vulnerable via `AF_ALG` | Verdict will be `LIKELY_NOT_EXPLOITABLE`. |
---
## Build / develop
```bash
make all # vet, test, build
make test # race-detector tests
make lint # golangci-lint run (uses .golangci.yml)
make cover # coverage report (HTML in coverage.html)
make build-all # cross-compile linux/amd64 + linux/arm64 + linux/386
make fmt fmt-check # gofmt -s -w / fail on diff
make fuzz # run every Fuzz* for FUZZ_TIME (default 30s)
make bench # benchmarks
make update-golden # regenerate text+pretty golden files
make version # print just the version string
BUILD_NUMBER=42 make build # override build number
```
External deps: `golang.org/x/sys/unix`, `golang.org/x/term`,
`charm.land/lipgloss/v2`. Static build via `CGO_ENABLED=0`; same binary
runs on glibc *and* musl distros.
---
## Sources
- [NIST CVE Database](https://nvd.nist.gov/vuln/detail/CVE-2026-31431)
- [Official Copy.Fail Site](https://copy.fail/)
- [Official Technical Write-up](https://xint.io/blog/copy-fail-linux-distributions)
- [oss-security advisory](https://www.openwall.com/lists/oss-security/2026/04/29/23)
- [Theori writeup](https://xint.io/blog/copy-fail-linux-distributions)
- [Bugcrowd summary](https://www.bugcrowd.com/blog/what-we-know-about-copy-fail-cve-2026-31431/)