Skip to Content
OpenVAPTAlpha
Pre-release. Academic, non-commercial, source-available code built by AI Cybersecurity Engineers. Free for your own use, 100% at your own risk. This website collects no data. Read MoreRead Less
Pre-release. OpenVAPT has no Stable release yet. Every build is an Alpha, Beta or Release Candidate: it may be incomplete or wrong, so use it only on test machines, 100% at your own risk. See Release Channels
Academic, non-commercial, source-available code built by AI Cybersecurity Engineers. These are AI systems, not people or registered engineers. Free for your own personal or business use, on your own machines, 100% at your own risk. Not for sale. Not for providing services. This website collects no data. Read the Acknowledgements
Pre-release. OpenVAPT has no Stable release yet. Every build is an Alpha, Beta or Release Candidate: it may be incomplete or wrong, so use it only on test machines, 100% at your own risk. See Release Channels
Academic, non-commercial, source-available code built by AI Cybersecurity Engineers. These are AI systems, not people or registered engineers. Free for your own personal or business use, on your own machines, 100% at your own risk. Not for sale. Not for providing services. This website collects no data. Read the Acknowledgements
LowHOST-KRN-028Secure ConfigurationServer

Unprivileged userfaultfd Allowed

Checks that only privileged processes can use userfaultfd.

Why It Matters
userfaultfd lets a process pause the kernel at a chosen moment, which has turned many kernel race conditions into reliable local exploits.
How It Detects
Reads /proc/sys/vm/unprivileged_userfaultfd directly (no command is run) and compares it with the hardened value 0.
How to Fix
Create /etc/sysctl.d/60-openvapt-hardening.conf containing: vm.unprivileged_userfaultfd = 0 then apply with: sysctl --system. Caution: unprivileged live migration tools (for example QEMU post-copy migration or CRIU run as a normal user) need userfaultfd; test those first.
How to Verify
sysctl vm.unprivileged_userfaultfd should print 0.
CVSS v3.1 Vector
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N
Control Category
Secure Configuration: CIS Controls v8 4; NIST CSF 2.0 PR.PS
Fix Effort
Low
CIS Controls v8 Safeguard
4.1
NIST CSF 2.0 Subcategory
PR.PS-01
CWE
CWE-749
NIST SP 800-53 Rev. 5
AC-6, CM-6
Benchmark Topic
Kernel parameters: vm.unprivileged_userfaultfd (topic only, no benchmark text)
References
Linux kernel documentation, /proc/sys/vm
Learn the theory behind this check: P1 Linux Server Hardening, P17 Kernel, Filesystem and Boot Hardening.

Source Code

The complete file that defines this check. Run it with openvapt scan --only HOST-KRN-028, or read it offline with openvapt explain HOST-KRN-028.

internal/checks/host/kernel.go · Lines 1-362View on GitHub at This Commit
1package host
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "io/fs"
8 "sort"
9 "strings"
10
11 "github.com/openvapt/openvapt/internal/check"
12 "github.com/openvapt/openvapt/internal/checks/collect"
13 "github.com/openvapt/openvapt/internal/sys"
14)
15
16// sysctlRule describes one kernel parameter and its hardened value.
17type sysctlRule struct {
18 id, title, key, want, summary, rationale string
19 sev check.Severity
20 cvss string
21 ref check.Ref
22 cwe string
23 allowed []string // values that also pass
24}
25
26// One table, one loop: each row becomes its own registered check, so every
27// kernel setting has its own catalogue page, id and result.
28var sysctlRules = []sysctlRule{
29 {"HOST-KRN-001", "Address Space Layout Randomisation Not Fully Enabled", "kernel.randomize_va_space", "2",
30 "Checks that the kernel randomises memory layout for stack, libraries, heap and VDSO.",
31 "ASLR makes memory-corruption exploits much harder because attackers cannot predict where code lives.",
32 check.Medium, "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N", refKernelDocs, "CWE-344", nil},
33 {"HOST-KRN-002", "Kernel Pointers Exposed to Unprivileged Users", "kernel.kptr_restrict", "1",
34 "Checks that kernel memory addresses are hidden from normal users.",
35 "Leaked kernel addresses defeat kernel ASLR and are the first step of most local kernel exploits.",
36 check.Low, "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N", refKernelDocs, "CWE-497", []string{"2"}},
37 {"HOST-KRN-003", "Kernel Log Readable by Unprivileged Users", "kernel.dmesg_restrict", "1",
38 "Checks that only privileged users can read the kernel log (dmesg).",
39 "The kernel log can reveal addresses, hardware and driver details useful for local exploitation.",
40 check.Low, "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N", refKernelDocs, "CWE-497", nil},
41 {"HOST-KRN-004", "Setuid Programs May Dump Core", "fs.suid_dumpable", "0",
42 "Checks that privileged programs do not write core dumps.",
43 "A core dump of a setuid program can contain password hashes or keys and may be readable by the user who crashed it.",
44 check.Low, "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N", check.Ref{Title: "Linux kernel documentation, /proc/sys/fs", URL: "https://docs.kernel.org/admin-guide/sysctl/fs.html"}, "CWE-528", nil},
45 {"HOST-KRN-005", "IP Forwarding Enabled", "net.ipv4.ip_forward", "0",
46 "Checks that the server does not route packets between networks.",
47 "A server that forwards packets can be used to pivot between networks. Only routers, VPN gateways and container hosts need this.",
48 check.Low, "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N", refKernelNet, "CWE-1188", nil},
49 {"HOST-KRN-006", "ICMP Redirects Accepted", "net.ipv4.conf.all.accept_redirects", "0",
50 "Checks that the kernel ignores ICMP redirect messages.",
51 "Forged ICMP redirects let an attacker on the local network reroute the server's traffic through themselves.",
52 check.Low, "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N", refKernelNet, "CWE-940", nil},
53 {"HOST-KRN-007", "Source-Routed Packets Accepted", "net.ipv4.conf.all.accept_source_route", "0",
54 "Checks that the kernel drops packets that dictate their own route.",
55 "Source routing lets an attacker bypass network filtering and spoof trusted addresses.",
56 check.Low, "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N", refKernelNet, "CWE-940", nil},
57 {"HOST-KRN-008", "TCP SYN Cookies Disabled", "net.ipv4.tcp_syncookies", "1",
58 "Checks that SYN cookies protect the server from SYN flood attacks.",
59 "Without SYN cookies a simple flood of half-open connections can make every service unreachable.",
60 check.Low, "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L", refKernelNet, "CWE-770", nil},
61 {"HOST-KRN-009", "Unprivileged eBPF Allowed", "kernel.unprivileged_bpf_disabled", "1",
62 "Checks that normal users cannot load eBPF programs into the kernel.",
63 "Unprivileged eBPF has been the entry point for many local privilege-escalation bugs.",
64 check.Medium, "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:L", refKernelDocs, "CWE-749", []string{"2"}},
65 {"HOST-KRN-010", "Process Tracing Not Restricted", "kernel.yama.ptrace_scope", "1",
66 "Checks that processes cannot attach a debugger to other processes of the same user.",
67 "Unrestricted ptrace lets any compromised process read the memory of others, for example to steal SSH agent keys or browser sessions.",
68 check.Low, "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N", check.Ref{Title: "Linux kernel documentation, Yama LSM", URL: "https://docs.kernel.org/admin-guide/LSM/Yama.html"}, "CWE-749", []string{"2", "3"}},
69 {"HOST-KRN-011", "Symlink Protection Disabled", "fs.protected_symlinks", "1",
70 "Checks that the kernel blocks following symlinks owned by other users in world-writable sticky directories.",
71 "Without it, classic /tmp symlink races let local users trick root-run programs into overwriting files.",
72 check.Low, "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N", check.Ref{Title: "Linux kernel documentation, /proc/sys/fs", URL: "https://docs.kernel.org/admin-guide/sysctl/fs.html"}, "CWE-59", nil},
73 {"HOST-KRN-012", "Hardlink Protection Disabled", "fs.protected_hardlinks", "1",
74 "Checks that users cannot create hard links to files they do not own.",
75 "Hard links to privileged files enable time-of-check races and keep vulnerable setuid binaries alive after updates.",
76 check.Low, "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N", check.Ref{Title: "Linux kernel documentation, /proc/sys/fs", URL: "https://docs.kernel.org/admin-guide/sysctl/fs.html"}, "CWE-59", nil},
77 {"HOST-KRN-013", "Reverse Path Filtering Disabled", "net.ipv4.conf.all.rp_filter", "1",
78 "Checks that the kernel drops packets whose source address could not be routed back through the receiving interface.",
79 "Reverse path filtering blocks many spoofed-source packets used in attacks and amplification.",
80 check.Low, "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N", refKernelNet, "CWE-940", []string{"2"}},
81 {"HOST-KRN-014", "Martian Packets Not Logged", "net.ipv4.conf.all.log_martians", "1",
82 "Checks that packets with impossible source addresses are logged.",
83 "Logging martian packets helps detect spoofing and misrouting attempts.",
84 check.Low, "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:L/A:N", refKernelNet, "CWE-778", nil},
85 {"HOST-KRN-015", "Server Sends ICMP Redirects", "net.ipv4.conf.all.send_redirects", "0",
86 "Checks that the server does not send ICMP redirects, which only routers should do.",
87 "A non-router sending redirects can be abused to influence other hosts' routing on the local network.",
88 check.Low, "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N", refKernelNet, "CWE-1188", nil},
89 {"HOST-KRN-016", "Broadcast ICMP Echo Answered", "net.ipv4.icmp_echo_ignore_broadcasts", "1",
90 "Checks that the server ignores ping requests sent to broadcast addresses.",
91 "Answering broadcast pings makes the server a participant in Smurf-style amplification attacks.",
92 check.Low, "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L", refKernelNet, "CWE-406", nil},
93 {"HOST-KRN-017", "IPv6 ICMP Redirects Accepted", "net.ipv6.conf.all.accept_redirects", "0",
94 "Checks that the kernel ignores IPv6 ICMP redirect messages.",
95 "Forged IPv6 redirects let an attacker on the local network reroute traffic through themselves.",
96 check.Low, "CVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N", refKernelNet, "CWE-940", nil},
97 {"HOST-KRN-018", "Performance Events Open to Unprivileged Users", "kernel.perf_event_paranoid", "2",
98 "Checks that unprivileged users cannot use kernel performance events to observe the system.",
99 "Unrestricted perf events expose kernel and other processes' activity and have been used in local exploits.",
100 check.Low, "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N", refKernelDocs, "CWE-749", []string{"3", "4"}},
101}
102
103func init() {
104 for _, r := range sysctlRules {
105 registerSysctl(r)
106 }
107}
108
109// registerSysctl turns one kernel parameter rule into a registered check.
110// kernel_depth.go registers its own table through the same function.
111func registerSysctl(r sysctlRule) {
112 {
113 ok := append([]string{r.want}, r.allowed...)
114 check.Register(&check.Check{
115 ID: r.id, Title: r.title, Domain: check.DomainHost, Severity: r.sev, CVSS: r.cvss,
116 Summary: r.summary,
117 Rationale: r.rationale,
118 Detection: sysctlDetection(r),
119 Fix: "Create /etc/sysctl.d/60-openvapt-hardening.conf containing: " + r.key + " = " + r.want + " then apply with: sysctl --system" + sysctlFixCaution[r.id],
120 Verify: "sysctl " + r.key + " should print " + r.want + ".",
121 Refs: []check.Ref{r.ref},
122 Standards: check.Standards{CWE: []string{r.cwe}, NIST: sysctlNIST(r.id, r.key), CISTopic: "Kernel parameters: " + r.key},
123 Run: func(ctx context.Context, s sys.System) []check.Result {
124 res := runSysctlRule(ctx, s, r, ok)
125 if note := sysctlPassNote[r.id]; note != nil && hasFail(res) {
126 if msg, ev, pass := note(ctx, s); pass {
127 return []check.Result{check.PassR(r.key, msg, append(failEvidence(res), ev...)...)}
128 }
129 }
130 return res
131 },
132 })
133 }
134}
135
136// runSysctlRule evaluates one kernel parameter rule.
137func runSysctlRule(ctx context.Context, s sys.System, r sysctlRule, ok []string) []check.Result {
138 if combinedSysctl[r.id] != nil {
139 return runCombinedSysctl(s, r.id, r.key, r.want)
140 }
141 if perInterfaceSysctl[r.id] {
142 return runPerInterfaceSysctl(s, r.key, r.want, ok)
143 }
144 v, err := collect.Sysctl(s, r.key)
145 if err != nil {
146 return []check.Result{check.SkipR(r.key, "Parameter not available on this kernel")}
147 }
148 for _, good := range ok {
149 if v == good {
150 return []check.Result{check.PassR(r.key, r.key+" = "+v)}
151 }
152 }
153 if r.id == "HOST-KRN-005" {
154 return []check.Result{runIPForward(ctx, s, r.key, v)}
155 }
156 return []check.Result{check.FailR(r.key, r.key+" = "+v+" (hardened value: "+r.want+")")}
157}
158
159func slash(k string) string {
160 b := []byte(k)
161 for i := range b {
162 if b[i] == '.' {
163 b[i] = '/'
164 }
165 }
166 return string(b)
167}
168
169// perInterfaceSysctl lists the rules whose kernel parameter also exists per
170// network interface. The kernel combines "all" with each interface value, so
171// every one of all, default and each interface must hold the hardened value.
172var perInterfaceSysctl = map[string]bool{"HOST-KRN-006": true, "HOST-KRN-015": true, "HOST-KRN-017": true, "HOST-KRN-023": true, "HOST-KRN-024": true, "HOST-KRN-025": true}
173
174// combinedSysctl lists IPv4 rules where the kernel combines the "all" value
175// with each interface value in a rule-specific way (include/linux/inetdevice.h):
176// rp_filter uses the larger of the two, log_martians is on when either is on,
177// and accept_redirects needs both on when the interface forwards packets but
178// either one otherwise. good reports whether the combined result is hardened.
179var combinedSysctl = map[string]func(all, iface int, forwarding bool) bool{
180 "HOST-KRN-006": func(all, iface int, fwd bool) bool {
181 if fwd {
182 return all == 0 || iface == 0
183 }
184 return all == 0 && iface == 0
185 },
186 "HOST-KRN-013": func(all, iface int, _ bool) bool { m := max(all, iface); return m == 1 || m == 2 },
187 "HOST-KRN-014": func(all, iface int, _ bool) bool { return all == 1 || iface == 1 },
188}
189
190var combinedDetection = map[string]string{
191 "HOST-KRN-006": "The kernel ignores redirects on an interface when both values are 0 or, on an interface that forwards packets (net.ipv4.ip_forward or the interface's forwarding setting), when either one is 0; any interface where redirects remain accepted fails.",
192 "HOST-KRN-013": "The kernel uses the larger of the two values, so an interface passes when that larger value is 1 (strict) or 2 (loose).",
193 "HOST-KRN-014": "The kernel logs martians on an interface when either value is 1, so an interface passes when all or the interface value is 1.",
194}
195
196func sysctlDetection(r sysctlRule) string {
197 return sysctlDetectionBase(r) + sysctlDetectionNote[r.id]
198}
199
200func sysctlDetectionBase(r sysctlRule) string {
201 if r.id == "HOST-KRN-005" {
202 return "Reads /proc/sys/net/ipv4/ip_forward directly and compares it with the hardened value 0. When a container runtime or Kubernetes node is detected read-only (runtime sockets such as /var/run/docker.sock, /run/containerd/containerd.sock or /run/crio/crio.sock, /etc/kubernetes, or bridge interfaces such as docker0, cni0, cbr0 or flannel.1 in /sys/class/net), forwarding is required: the check then passes with context when the FORWARD path is filtered (iptables or nftables forward policy drop, or DROP/REJECT rules in DOCKER-USER), fails when forwarded traffic is accepted by default, and is incomplete when the policy cannot be read."
203 }
204 if d, ok := combinedDetection[r.id]; ok {
205 g := strings.Replace(slash(r.key), "/all/", "/*/", 1)
206 return "Reads /proc/sys/" + g + " directly (all, default for future interfaces, and every interface except loopback; no command is run) and combines the all value with each interface value as the kernel does. " + d
207 }
208 if perInterfaceSysctl[r.id] {
209 g := strings.Replace(slash(r.key), "/all/", "/*/", 1)
210 return "Reads /proc/sys/" + g + " directly (all, default and every interface; no command is run) and fails if any of them differs from the hardened value " + r.want + "."
211 }
212 return "Reads /proc/sys/" + slash(r.key) + " directly (no command is run) and compares it with the hardened value " + r.want + "."
213}
214
215// sysctlNIST maps a kernel rule to NIST SP 800-53 Rev. 5 controls: SI-16 for
216// memory protection, SC-7 for network boundary settings, SC-5 for denial of
217// service protection and AC-6 for least privilege on local information.
218func sysctlNIST(id, key string) []string {
219 switch id {
220 case "HOST-KRN-001", "HOST-KRN-002", "HOST-KRN-009", "HOST-KRN-018":
221 return []string{"CM-6", "SI-16"}
222 case "HOST-KRN-008", "HOST-KRN-016":
223 return []string{"SC-5", "CM-6"}
224 }
225 if strings.HasPrefix(key, "net.") {
226 return []string{"SC-7", "CM-6"}
227 }
228 return []string{"AC-6", "CM-6"}
229}
230
231// runPerInterfaceSysctl evaluates a ".conf.all." parameter together with its
232// "default" and per-interface siblings.
233func runPerInterfaceSysctl(s sys.System, key, want string, ok []string) []check.Result {
234 base := "/proc/sys/" + slash(key)
235 pat := strings.Replace(base, "/all/", "/*/", 1)
236 paths, _ := s.Glob(pat)
237 seen := map[string]bool{}
238 for _, p := range paths {
239 seen[p] = true
240 }
241 for _, p := range []string{base, strings.Replace(base, "/all/", "/default/", 1)} {
242 if !seen[p] {
243 paths = append(paths, p)
244 seen[p] = true
245 }
246 }
247 sort.Strings(paths)
248 var bad []string
249 read, unreadable := 0, 0
250 for _, p := range paths {
251 // Read the path directly: interface names may contain dots
252 // (VLANs such as eth0.100), which a dotted sysctl key cannot express.
253 k := strings.TrimPrefix(p, "/proc/sys/")
254 rr, err := s.ReadFile(context.Background(), p)
255 if err != nil || !rr.Complete {
256 if !errors.Is(err, fs.ErrNotExist) {
257 unreadable++
258 }
259 continue
260 }
261 v := strings.TrimSpace(string(rr.Data))
262 read++
263 good := false
264 for _, g := range ok {
265 if v == g {
266 good = true
267 break
268 }
269 }
270 if !good {
271 bad = append(bad, k+" = "+v)
272 }
273 }
274 if read == 0 {
275 return []check.Result{check.SkipR(key, "Parameter not available on this kernel")}
276 }
277 if len(bad) > 0 {
278 return []check.Result{check.FailR(key, fmt.Sprintf("%d of %d setting(s) differ from the hardened value %s", len(bad), read, want), bad...)}
279 }
280 if unreadable > 0 {
281 return []check.Result{check.IncompleteR(key, fmt.Sprintf("%d setting(s) could not be read; the readable ones hold the hardened value", unreadable))}
282 }
283 return []check.Result{check.PassR(key, fmt.Sprintf("%s = %s for all, default and every interface (%d settings)", key, want, read))}
284}
285
286// runCombinedSysctl evaluates an IPv4 ".conf.all." parameter the way the
287// kernel applies it: the all value combined with each interface value (and
288// with default, which new interfaces inherit). Loopback is skipped: these
289// settings concern packets arriving from a network.
290func runCombinedSysctl(s sys.System, id, key, want string) []check.Result {
291 good := combinedSysctl[id]
292 base := "/proc/sys/" + slash(key)
293 name := base[strings.LastIndex(base, "/")+1:]
294 read := func(p string) (int, bool, bool) { // value, ok, unreadable
295 rr, err := s.ReadFile(context.Background(), p)
296 if err != nil || !rr.Complete {
297 return 0, false, !errors.Is(err, fs.ErrNotExist)
298 }
299 var n int
300 if _, err := fmt.Sscan(strings.TrimSpace(string(rr.Data)), &n); err != nil {
301 return 0, false, true
302 }
303 return n, true, false
304 }
305 all, ok, _ := read(base)
306 if !ok {
307 return []check.Result{check.SkipR(key, "Parameter not available on this kernel")}
308 }
309 ipFwd, _, _ := read("/proc/sys/net/ipv4/ip_forward")
310 paths, _ := s.Glob(strings.Replace(base, "/all/", "/*/", 1))
311 def := strings.Replace(base, "/all/", "/default/", 1)
312 seen := map[string]bool{base: true}
313 ifaces := []string{}
314 for _, p := range append(paths, def) {
315 dir := strings.TrimSuffix(p, "/"+name)
316 ifn := dir[strings.LastIndex(dir, "/")+1:]
317 if seen[p] || ifn == "lo" {
318 continue
319 }
320 seen[p] = true
321 ifaces = append(ifaces, p)
322 }
323 sort.Strings(ifaces)
324 var bad []string
325 checked, unreadable := 0, 0
326 for _, p := range ifaces {
327 v, ok, unr := read(p)
328 if !ok {
329 if unr {
330 unreadable++
331 }
332 continue
333 }
334 checked++
335 dir := strings.TrimSuffix(p, "/"+name)
336 fwd := ipFwd == 1
337 if f, ok, _ := read(dir + "/forwarding"); ok && f == 1 {
338 fwd = true
339 }
340 if !good(all, v, fwd) {
341 note := fmt.Sprintf(" (all = %d", all)
342 if id == "HOST-KRN-006" && fwd {
343 note += ", interface forwards packets"
344 }
345 bad = append(bad, strings.TrimPrefix(p, "/proc/sys/")+fmt.Sprintf(" = %d", v)+note+")")
346 }
347 }
348 if checked == 0 && unreadable == 0 {
349 // No interface or default value is visible: judge all by itself.
350 if !good(all, all, ipFwd == 1) {
351 return []check.Result{check.FailR(key, fmt.Sprintf("%s = %d (hardened value: %s)", key, all, want))}
352 }
353 return []check.Result{check.PassR(key, fmt.Sprintf("%s = %d", key, all))}
354 }
355 if len(bad) > 0 {
356 return []check.Result{check.FailR(key, fmt.Sprintf("%d interface setting(s) are not hardened once combined with all (hardened value: %s)", len(bad), want), bad...)}
357 }
358 if unreadable > 0 {
359 return []check.Result{check.IncompleteR(key, fmt.Sprintf("%d setting(s) could not be read; the readable ones are hardened", unreadable))}
360 }
361 return []check.Result{check.PassR(key, fmt.Sprintf("%s = %d; hardened on every interface once combined (%d checked)", key, all, checked))}
362}

Also available as plain Markdown for notes, scripts and AI tools. Same facts, same source, no styling.

Cite This Page

If this page helped your work, please cite it. The version and web address are filled in; the access date is today's date in your browser. Every other style, and when credit is required, is on How to Cite OpenVAPT.

APA 7
OpenVAPT. (2026). HOST-KRN-028 Unprivileged userfaultfd Allowed (Version 0.1.0-alpha.6). OpenVAPT Check Catalogue. Retrieved October 16, 2026, from https://openvapt.com/checks/HOST-KRN-028.html

In text: (OpenVAPT, 2026). Accessed: the date shown is today's date from your own browser (the site's build date without JavaScript); change it to the day you read the page.

BibTeX
@misc{openvapt_host_krn_028,
  author       = {{OpenVAPT}},
  title        = {{HOST-KRN-028 Unprivileged userfaultfd Allowed}},
  year         = {2026},
  howpublished = {\url{https://openvapt.com/checks/HOST-KRN-028.html}},
  note         = {OpenVAPT Check Catalogue, version 0.1.0-alpha.6. Accessed 16 October 2026}
}

Accessed: the date shown is today's date from your own browser (the site's build date without JavaScript); change it to the day you read the page.

Citing OpenVAPT does not mean OpenVAPT endorses, approves or has reviewed your work.