Skip to content

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.

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

  1. Read /proc/sys/kernel/randomize_va_space, set it to 0, run cat /proc/self/maps twice and compare addresses, set it back to 2 and compare again.
  2. Set dmesg_restrict and run dmesg as a normal user.

Build it

Add the kernel keys to your sysctl-check.sh expectations file.

Next

Filesystem Hardening