Thanks to AI that I can have a test script quickly.
I am random picking a h874 with QTS 5.2.8 (2025/12/25) and run this script generated from Claude, and double checked by Codex to verify if the entrypoint exist on a QTS:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""CVE-2026-31431 surface check. Python 2.7 / 3.x compatible. Bind-only, safe."""
from __future__ import print_function
import os, socket, sys, errno
AF_ALG, SOCK_SEQPACKET = 38, 5
def in_container():
if os.path.exists("/.dockerenv"):
return True
try:
with open("/proc/1/cgroup") as f:
d = f.read()
return any(k in d for k in ("docker", "lxc", "kubepods", "containerd"))
except (IOError, OSError):
return False
def kernel_info():
u = os.uname()
# u: (sysname, nodename, release, version, machine)
print("[*] Kernel: {0} ({1})".format(u[2], u[4]))
print("[*] Container: {0}".format(in_container()))
def kconfig_probe():
paths = ["/proc/config.gz", "/boot/config-" + os.uname()[2]]
keys = ("CONFIG_CRYPTO_USER_API_AEAD", "CONFIG_CRYPTO_USER_API",
"CONFIG_CRYPTO_AUTHENC")
for p in paths:
if not os.path.exists(p):
continue
try:
if p.endswith(".gz"):
import gzip
f = gzip.open(p, "rt")
else:
f = open(p, "r")
data = f.read()
f.close()
except Exception as e:
print("[!] kconfig read fail {0}: {1}".format(p, e))
continue
print("[*] kconfig source: {0}".format(p))
for line in data.splitlines():
for k in keys:
if line.startswith(k + "=") or line.startswith("# " + k + " "):
print(" {0}".format(line))
return
print("[*] kconfig: not exposed (no /proc/config.gz, no /boot/config-*)")
def module_state():
loaded = False
try:
with open("/proc/modules") as f:
for line in f:
if line.startswith("algif_aead "):
loaded = True
break
except (IOError, OSError) as e:
print("[!] /proc/modules: {0}".format(e))
sysmod = os.path.isdir("/sys/module/algif_aead")
print("[*] algif_aead loaded: {0}".format(loaded))
print("[*] /sys/module/algif_aead present: {0}".format(sysmod))
bl = []
for d in ("/etc/modprobe.d", "/run/modprobe.d", "/lib/modprobe.d"):
if not os.path.isdir(d):
continue
try:
entries = os.listdir(d)
except OSError:
continue
for fn in entries:
full = os.path.join(d, fn)
try:
with open(full) as f:
if "algif_aead" in f.read():
bl.append(full)
except (IOError, OSError):
pass
print("[*] blacklist refs: {0}".format(bl if bl else "none"))
return loaded, sysmod
def afalg_bind_probe():
try:
s = socket.socket(AF_ALG, SOCK_SEQPACKET, 0)
except socket.error as e:
en = e.args[0] if e.args else 0
print("[+] AF_ALG socket() blocked: errno={0} ({1}) {2}".format(
en, errno.errorcode.get(en, "?"), os.strerror(en) if en else ""))
return False
try:
s.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
print("[!] authencesn bind SUCCEEDED -- vulnerable surface EXPOSED")
s.close()
return True
except socket.error as e:
en = e.args[0] if e.args else 0
print("[+] authencesn bind failed: errno={0} ({1}) {2}".format(
en, errno.errorcode.get(en, "?"), os.strerror(en) if en else ""))
s.close()
return False
def userns_state():
for p in ("/proc/sys/kernel/unprivileged_userns_clone",
"/proc/sys/user/max_user_namespaces",
"/proc/sys/kernel/apparmor_restrict_unprivileged_userns"):
if os.path.exists(p):
try:
with open(p) as f:
print("[*] {0} = {1}".format(p, f.read().strip()))
except (IOError, OSError):
pass
def main():
kernel_info()
kconfig_probe()
loaded, sysmod = module_state()
exposed = afalg_bind_probe()
userns_state()
print("")
if exposed:
print("VERDICT: VULNERABLE attack surface present.")
if in_container():
print(" In container -> Part 2 (host page-cache write) reachable.")
sys.exit(2)
elif not (loaded or sysmod):
print("VERDICT: Module absent and bind blocked.")
sys.exit(0)
else:
print("VERDICT: Module present but bind blocked -- review.")
sys.exit(1)
if __name__ == "__main__":
main()
And the result:
From host:
[qadmin@874QTS ~]$ uname -a
Linux 874QTS 5.10.60-qnap #1 SMP Thu Dec 25 01:08:48 CST 2025 x86_64 GNU/Linux
[qadmin@874QTS ~]$ python cpftest.py
[*] Kernel: 5.10.60-qnap (x86_64)
[*] Container: False
[*] kconfig: not exposed (no /proc/config.gz, no /boot/config-*)
[*] algif_aead loaded: False
[*] /sys/module/algif_aead present: False
[*] blacklist refs: none
[+] AF_ALG socket() blocked: errno=97 (EAFNOSUPPORT) Address family not supported by protocol
[*] /proc/sys/user/max_user_namespaces = 127243
VERDICT: Module absent and bind blocked.
From a python container inside same NAS:
root@1ebc107603de:/home# uname -a
Linux 1ebc107603de 5.10.60-qnap #1 SMP Thu Dec 25 01:08:48 CST 2025 x86_64 GNU/Linux
root@1ebc107603de:/home# python cpftest.py
[*] Kernel: 5.10.60-qnap (x86_64)
[*] Container: True
[*] kconfig: not exposed (no /proc/config.gz, no /boot/config-*)
[*] algif_aead loaded: False
[*] /sys/module/algif_aead present: False
[*] blacklist refs: none
[+] AF_ALG socket() blocked: errno=97 (EAFNOSUPPORT) Address family not supported by protocol
[*] /proc/sys/user/max_user_namespaces = 127243
VERDICT: Module absent and bind blocked.
So it seems OK even using older version of QTS.
Note: I am not pro in the security team. Just sharing my thoughts.
