Kernel Hardening¶
The kernel has a set of switches under /proc/sys/kernel/ that decide what an ordinary user can learn about the kernel and what an attacker with a shell can do next. These are the Linux equivalent of the exploit-protection settings on Windows. An image may have relaxed them, or they may simply be at old defaults.
The values¶
| Setting | Value | Why |
|---|---|---|
kernel.randomize_va_space (CIS 1.5.1) |
2 | Full address space layout randomization. Every program's memory layout is shuffled, so an exploit can't know where to jump. 0 or 1 make memory-corruption bugs easy to exploit. |
kernel.kptr_restrict |
2 | Kernel addresses in /proc are shown as zeros to everyone, root included. Leaked addresses defeat randomization. |
kernel.dmesg_restrict |
1 | Only root reads the kernel log, which contains addresses, hardware details, and sometimes credentials |
kernel.yama.ptrace_scope (CIS 1.5.2) |
1 (not 0) | A process can only be debugged by its parent. At 0, any process a user owns can attach to any other process the user owns and read its memory, including a password in a running program. |
kernel.unprivileged_bpf_disabled |
1 | Ordinary users can't load BPF programs into the kernel, which has been a repeated source of privilege escalation |
kernel.perf_event_paranoid |
3 | Ordinary users can't use performance counters, which leak kernel information |
kernel.sysrq |
0 | The magic SysRq key combination is disabled; at the console it can reboot, kill processes, or dump memory without logging in |
kernel.ctrl-alt-del (STIG UBTU-22-211015, CAT I) |
0 | Ctrl+Alt+Del at the console goes to systemd (which by default does a clean reboot, and can be masked entirely) instead of triggering an immediate reset |
kernel.core_uses_pid |
1 | Core dumps get the PID in the filename so they can't overwrite each other |
kernel.modules_disabled |
1 (with care) | No kernel modules can be loaded or unloaded until reboot, which blocks rootkits that load as modules. It can't be turned off without a reboot; set it last, after everything else works, and only if the README doesn't describe hardware that needs a module loaded later. |
Apply¶
sudo nano /etc/sysctl.d/60-kernel-hardening.conf
kernel.randomize_va_space = 2
kernel.kptr_restrict = 2
Add the rest from the table (not modules_disabled yet), save, and sudo sysctl --system. Each key is echoed back as it's applied; one that isn't was misspelled.
For Ctrl+Alt+Del, also stop systemd from acting on it at all, and on a desktop stop the graphical session catching it too (STIG UBTU-22-271030):
sudo systemctl mask ctrl-alt-del.target
sudo systemctl daemon-reload
printf '[org/gnome/settings-daemon/plugins/media-keys]\nlogout=\x27\x27\n' | sudo tee /etc/dconf/db/local.d/00-disable-CAD; sudo dconf update # Debian GNOME
On Mint the equivalent Cinnamon shortcut is in Keyboard → Shortcuts → System → Log out; clear it.
Then, if appropriate, modules_disabled as the very last step of the whole image:
echo 'kernel.modules_disabled = 1' | sudo tee /etc/sysctl.d/99-modules.conf
sudo sysctl -w kernel.modules_disabled=1
Check /etc/sysctl.conf and the other files in /etc/sysctl.d/ for the same keys set to weaker values; the highest-numbered file wins, and sysctl.conf is loaded last of all.
Core dumps, prelink, and crash reporting¶
Three more from the same section of the benchmark:
| CIS | What | Do |
|---|---|---|
| 1.5.4 | Core file size 0 | printf '%s\n' '* hard core 0' \| sudo tee /etc/security/limits.d/60-core.conf, and comment out any hard core line with a larger number in /etc/security/limits.conf. A core dump is a program's memory on disk. |
| STIG UBTU-22-213015 | Kernel crash dumps off | sudo systemctl mask --now kdump-tools 2>/dev/null if the package is present; a kernel dump is all of memory written to disk |
| STIG UBTU-22-213025 | NX (no-execute) memory | dmesg \| grep -i nx should say "NX (Execute Disable) protection: active". It's a CPU and firmware feature; on a VM it's on unless the host disabled it. |
| Debian CIS 1.5.11, 1.5.12 | systemd-coredump off | printf '[Coredump]\nProcessSizeMax=0\nStorage=none\n' \| sudo tee /etc/systemd/coredump.conf.d/60-coredump.conf, then sudo systemctl daemon-reload. The limits file stops shells writing dumps; this stops systemd collecting them. |
| 1.5.5 | prelink not installed |
sudo apt purge prelink. Prelinking rewrites binaries, which defeats randomization and breaks integrity checks. |
| 1.5.6 | Automatic error reporting off | Mint: enabled=0 in /etc/default/apport and sudo systemctl mask --now apport. Debian has no apport; skip. Crash reports contain memory contents. |
Verify¶
sysctl kernel.randomize_va_space kernel.kptr_restrict kernel.dmesg_restrict kernel.yama.ptrace_scope kernel.unprivileged_bpf_disabled kernel.perf_event_paranoid kernel.sysrq
grep -rE 'hard\s+core' /etc/security/limits.conf /etc/security/limits.d/
dpkg -l prelink 2>&1 | tail -1; systemctl is-enabled apport 2>/dev/null
Example¶
sysctl kernel.randomize_va_space returns 0, and /etc/sysctl.d/10-nolimit.conf contains kernel.randomize_va_space = 0 with a comment about a game server. The README describes no such thing. Delete that file, write the hardening file, sysctl --system, and confirm 2.
Try it¶
- Read
/proc/sys/kernel/randomize_va_space, set it to 0, runcat /proc/self/mapstwice and compare addresses, set it back to 2 and compare again. - Set
dmesg_restrictand rundmesgas a normal user.
Build it¶
Add the kernel keys to your sysctl-check.sh expectations file.