Kahibaro
Discord Login Register

7.1.2 Kernel hardening

Why Kernel Hardening Matters

The Linux kernel controls everything that happens on the system. If an attacker gains kernel level control, they can bypass file permissions, hide processes, read memory, and defeat almost any other security control. Kernel hardening focuses on making it much harder to exploit kernel bugs, to load malicious code into the kernel, or to abuse kernel interfaces.

Kernel hardening does not remove the need for other security layers such as firewalls, application hardening, or strong authentication. It is one layer in a defense in depth strategy and specifically targets attacks that attempt to escalate to full system compromise.

Kernel compromise usually means complete system compromise. Kernel hardening aims to reduce both the likelihood and impact of such compromise.

Built‑in Kernel Security Frameworks

Modern Linux kernels include several security frameworks that can significantly improve resilience if configured correctly. Hardening here is mostly about turning on and tuning existing protections, not writing kernel code.

Two major kernel security modules, or LSMs, are particularly relevant.

SELinux and AppArmor

SELinux and AppArmor are both implemented inside the kernel, but are configured from user space. They provide mandatory access control, which restricts what even root owned processes can do, based on policies.

From a kernel hardening point of view, the key ideas are:

The kernel enforces fine grained permissions on system calls, files, IPC, network operations, and more.

Unexpected behavior, for example a web server executing a shell, can be blocked even if the web server is running as root.

The attack surface of privileged processes is reduced because their access is limited to what the policy allows.

SELinux policies tend to be more expressive and complex. AppArmor policies are often simpler and path based. Choosing between them is a distribution level decision. Hardening work focuses on enabling the chosen framework in enforcing mode, removing permissive exceptions, and tuning noisy rules while still blocking suspicious actions.

For strong kernel level isolation of services, keep SELinux or AppArmor enabled and enforcing, and avoid disabling them permanently to "fix" misconfigurations.

Memory Corruption Mitigations

Many kernel vulnerabilities are memory safety issues such as buffer overflows, use after free bugs, or integer overflows leading to memory corruption. The kernel includes multiple mitigations that aim to make exploitation of such bugs much harder, even if bugs are present.

Kernel Address Space Layout Randomization (KASLR)

KASLR randomizes the base address of the kernel in virtual memory at boot. This means that important kernel structures and code addresses change on every boot. Exploits that rely on knowing exact addresses, such as return oriented programming chains, become much more difficult.

You generally control KASLR with a kernel parameter such as kaslr on architectures where it is supported. Many distributions enable it by default. You can check the current configuration by reviewing the kernel command line in /proc/cmdline.

KASLR is not a complete defense on its own. Information leaks, like bugs that reveal kernel addresses, can defeat it. However, as part of a layered approach, it significantly raises the bar for reliable kernel exploits.

Kernel Stack Protection

Several mechanisms aim to protect kernel stacks from corruption.

Compilers can insert stack canaries. These are special values placed on the stack before return addresses. If a buffer overflow overwrites the canary, the mismatch is detected before the function returns, and the kernel can react by killing the offending operation instead of executing corrupted control flow.

You can see whether stack protector options are compiled into your kernel by examining the kernel build configuration, often available at /boot/config-$(uname -r) on many distributions. Options such as CONFIG_CC_STACKPROTECTOR_STRONG indicate that the kernel was compiled with stronger stack protection.

Some architectures and kernel builds also support features that reduce the mapping of kernel stacks in address space, or mark them in ways that prevent arbitrary writes from user space.

Kernel Memory Protection

Hardening also focuses on protecting different regions of kernel memory from being misused.

Read only kernel data. The kernel can mark certain data structures as read only after initialization. This prevents runtime modification of critical tables and function pointers. Configuration symbols like CONFIG_DEBUG_RODATA or its successors implement this behavior, and are usually enabled by major distributions.

Non executable memory. The kernel uses hardware features such as NX or XD bits to mark memory regions non executable. Combined with separate mappings for code and data, this makes it more difficult to execute injected shellcode in kernel memory.

Strict user and kernel memory separation. Flags such as CONFIG_STRICT_KERNEL_RWX enforce stricter read, write, and execute permissions for different kernel sections. This reduces the risk that a bug in one part of the kernel can trivially change executable code in another.

Memory corruption mitigations do not remove vulnerabilities. They reduce the success rate of exploitation and often turn reliable exploits into crashes that can be detected and fixed.

Kernel Self‑Protection Project Concepts

The Kernel Self Protection Project, KSPP, is an effort to make the upstream Linux kernel more resilient, by making entire bug classes unexploitable or less useful to attackers. Many of its ideas arrive in mainline over time and then trickle into distribution kernels.

Typical self protection features include:

Stricter pointer bounds checking and use of helper functions that verify access to structures.

Hardened reference counting, which reduces overflow related bugs in reference counters.

Randomization of sensitive kernel structures and layout beyond basic KASLR.

Control flow integrity mechanisms that validate that indirect branches and function pointers go to expected targets, depending on architecture support.

As a system administrator you do not implement these features yourself, but you can choose kernels and distributions that track security focused configurations and enable these options, and you can avoid disabling them for short term performance gains.

Hardening Kernel Configuration

Kernel configuration has a major impact on security. Even if you do not compile your own kernels, you can often influence which configuration profile your distribution uses, for example by choosing a hardened kernel package.

Key principles for secure configurations are:

Enable only hardware, filesystems, and protocols that you truly use. Each enabled feature expands your attack surface.

Prefer secure defaults. For example, choose configuration options that require explicit opt in of risky behaviors instead of opt out.

Enable security and debugging helpers that support rapid response to future vulnerabilities, provided they do not leak excessive information in production.

Minimizing Attack Surface

A large part of kernel hardening is simply not including code that you do not need.

If you build a custom kernel, avoid including rarely used filesystems, obsolete protocol stacks, or drivers for hardware you do not have. Every driver or subsystem can contain bugs that an attacker might trigger remotely or from an untrusted user space program.

Even without custom kernels you can:

Disable unused network protocol modules from loading automatically, for example by blacklisting modules in /etc/modprobe.d/.

Avoid installing out of tree kernel modules that are not actively maintained or that have unclear provenance.

Prefer minimal kernels for dedicated appliances, such as routers or embedded devices, where you have tight control over which features are present.

Unprivileged Operations and Sysctl

The kernel exposes many tunable parameters via sysctl, for example in /proc/sys. Some of these significantly affect security. Hardening often means restricting what unprivileged users can request from the kernel.

On many systems, important parameters include:

kernel.unprivileged_bpf_disabled where nonzero values restrict unprivileged programs from using eBPF, which has been a source of kernel vulnerabilities. Some distributions disable unprivileged BPF by default.

kernel.kptr_restrict which controls how freely kernel pointers are exposed through interfaces such as /proc. Stricter settings reduce information leaks that would otherwise help attackers defeat KASLR.

kernel.dmesg_restrict which limits unprivileged reading of kernel logs via dmesg. Kernel logs may contain sensitive addresses and error details that aid exploitation.

fs.protected_hardlinks and fs.protected_symlinks which defend against common privilege escalation tricks with hard links and symlinks.

You adjust these values with commands like sysctl -w kernel.kptr_restrict=2 for temporary changes, or by placing configuration in files under /etc/sysctl.d/ for persistent settings.

Hardened sysctl settings should be persistent. Use /etc/sysctl.d/*.conf so your rules survive reboot.

Restricting /dev/mem and /dev/kmem

Historically, /dev/mem and /dev/kmem provided direct access to physical or kernel memory. This is extremely powerful and undermines almost all security if widely accessible.

Modern kernels and distributions typically restrict these devices. Features such as CONFIG_STRICT_DEVMEM limit the regions of memory that /dev/mem can access, often to hardware related areas instead of full kernel memory. Many systems do not even create /dev/kmem anymore.

For kernel hardening, ensure that:

Such devices exist only if needed for specific low level maintenance tasks.

Permissions are highly restricted, usually root only.

Any tooling that legitimately needs access to /dev/mem runs on tightly controlled maintenance systems, not general multi user servers.

Kernel Module Security

Kernel modules are pieces of code that can be loaded into the running kernel. They provide drivers, filesystems, and other functionality without rebooting. They are extremely powerful, since malicious or buggy modules run with full kernel privileges.

Limiting Module Loading

One core strategy is to limit who can load modules and under what circumstances.

The kernel parameter module_sig_enforce=1 can require that modules be cryptographically signed and rejected otherwise. Combined with Secure Boot, this means only modules signed by trusted keys can be loaded. You can confirm enforcement by checking the kernel command line in /proc/cmdline and the related configuration flags in your kernel config.

You can also disable module loading entirely after boot by writing to /proc/sys/kernel/modules_disabled. For example, some hardened systems set this value to 1 late in the boot process, after all required modules are loaded, so that no further modules can be added, even by root.

In user space, tools such as modprobe and insmod should be usable only by administrators and should be monitored through audit logs to detect unexpected module loads.

Once kernel.modules_disabled is set to 1, no new modules can be loaded until the next reboot. Plan carefully before enabling this in production.

Blacklisting and Removing Unused Modules

Even if you allow modules in general, you can prevent specific ones from loading.

Blacklisting through configuration files under /etc/modprobe.d/ tells modprobe not to load particular modules automatically. This can be used to disable:

Deprecated filesystems such as cramfs or jffs2 if you do not use them.

Obscure network protocols that you do not need.

Potentially risky features such as USB storage on kiosk systems or locked down servers.

On some systems, you may go further and remove the module files from /lib/modules/$(uname -r)/ entirely. This reduces the risk of them being loaded accidentally, though it can complicate kernel updates.

Module Signing and Secure Boot

Module signing uses public key cryptography to ensure that only modules built and approved by a trusted party can be loaded into the kernel.

The key points are:

The kernel maintains a set of trusted keys, either compiled in or loaded at boot.

Modules are signed with the private key that corresponds to one of these trusted keys.

At load time, the kernel verifies the signature. If verification fails and signature enforcement is enabled, the load is rejected.

Secure Boot in UEFI environments extends this chain. If Secure Boot is active and configured to require signed kernels, you can align kernel and module signing with the platform keys. This helps prevent offline attackers from booting a manipulated kernel or loading arbitrary modules, although proper key management becomes critical.

System Call and BPF Hardening

Userspace interacts with the kernel primarily through system calls. Reducing what is available or restricting how it can be used makes exploitation harder.

System Call Filtering

Mechanisms such as seccomp, configured via user space, allow applications to restrict the system calls they are allowed to make. Although configuration happens in user space, enforcement is by the kernel.

From the kernel hardening perspective:

Encouraging or requiring seccomp profiles for exposed services significantly reduces the potential impact of kernel bugs triggered via unusual system calls.

Distributions may provide default seccomp profiles for container runtimes, web servers, or other services.

While you usually manage seccomp from user space, understanding that the kernel enforces it is important for coherent hardening.

eBPF Restrictions

Extended Berkeley Packet Filter, or eBPF, allows user space to load small programs into the kernel. These programs can attach to various hooks for networking, tracing, and observability.

eBPF is immensely powerful but has historically been a significant source of security issues because it provides a programmable interface inside the kernel.

Hardening measures focus on:

Disabling unprivileged eBPF, typically via kernel.unprivileged_bpf_disabled=1 or by build time options.

Using strict eBPF verifiers that analyze loaded programs for safety.

Limiting where eBPF programs can attach and which helpers they can use on hardened systems.

On systems that do not rely on eBPF based observability or networking, completely disabling unprivileged eBPF is a common and effective hardening step.

Kernel Integrity and Runtime Protection

Beyond compile time and boot time protections, some mechanisms monitor the runtime state of the kernel for anomalies.

Integrity Measurement and Verification

Integrity subsystems such as IMA and EVM allow the kernel to measure and verify files, including modules and critical binaries, at load or execution time.

The kernel can store hashes of files, compare them to expected values, and apply policies that deny access if measurements do not match. While much of the configuration is in user space, the enforcement of these policies is a kernel responsibility.

When integrated with technologies such as trusted platform modules, or TPMs, these measurements can be anchored in hardware, making tampering more difficult to conceal.

Runtime Exploit Detection

Some hardened kernels include runtime exploit detection heuristics. These may track unusual patterns such as sudden changes in control flow, suspicious sequences of syscalls, or unexpected memory writes.

Examples include:

Optional patches or features like Kernel Address Sanitizer for debugging and development, not for production, but which inform later hardening efforts.

Architecture specific features that detect stack smashing or invalid return addresses.

While these tools may not prevent all exploits and can sometimes trigger false positives, they add another layer of difficulty for attackers attempting to move from a single bug to a reliable kernel exploit chain.

Practical Trade‑offs and Deployment

Kernel hardening is always a balance between security, stability, performance, and operability. Highly aggressive hardening can break legitimate workloads or make troubleshooting difficult. Under hardening leaves the system exposed.

Key practical points include:

Test hardened kernels and settings in staging before deployment to production. Some protections, such as strict seccomp profiles or aggressive module disabling, can cause rare but critical failures.

Document all non default kernel parameters and sysctl settings. This is vital when debugging or when handing over systems to new administrators.

Monitor logs for denials from SELinux, AppArmor, and integrity subsystems. Adjust policies so that legitimate behavior is allowed but suspicious patterns remain blocked.

Use distribution supported hardened kernels when available, for example kernels with names or flavors that emphasize security, instead of trying to reassemble all protections yourself.

Every kernel update is a security event. Apply security updates promptly, then confirm that your hardening settings and policies are still enabled and effective.

By combining built in kernel security frameworks, careful configuration, module controls, restricted kernel interfaces, and integrity mechanisms, you can significantly reduce both the opportunities and rewards for kernel level attacks, even in the presence of yet undiscovered vulnerabilities.

Views: 6

Comments

Please login to add a comment.

Don't have an account? Register now!