Hardening Linux-based IoT systems

TL;DR: In Linux kernel and as part of the Kernel Self Protection Project we are pushing for new lightweight security mechanisms. On top of that, in systemd we are implementing new lightweight container mechanisms that target Embedded Linux and IoT. Our goal is to make it easy to deploy Secure Embedded Linux and IoT systems.

Working with Embedded Linux systems, device drivers, system and kernel security, allows me to inspect what will be deployed in production. I noticed a common pattern for some devices, either they contain simple kernel vulnerabilities within third party drivers, or deployed applications run with higher privileges, they lack common sandbox and security features. In this blog post, I will present a brief introduction on some mechanisms that allow you to improve your Linux-based IoT security, before discussing new plans that are in development from systemd to Linux kernel hardening.

Linux Kernel Hardening and security

Linux is one of the most popular operating systems for Embedded and IoT systems. Linux is a beast, it solves most embedded world use cases, however, supporting lot of technologies has its own code complexity cost. Security hardening measures allow to hide this complexity, to reduce the attack surface, to contain apps, and to defeat some kernel or user space exploitation techniques. The Linux kernel Self Protection project [1] tries to take this further by offering Kernel Self Protection features, the aim to protect against known and unknown Linux kernel bugs and vulnerabilities.

Some Embedded Kernel Hardening features:

CONFIG_DEFAULT_MMAP_MIN_ADDR=32768 Disallow allocating the first 32k of memory to protect against kernel null pointer dereference and related exploits.

CONFIG_STRICT_KERNEL_RWX=y Make kernel text and rodata read-only. Kernel version of W^X.

CONFIG_STRICT_DEVMEM=y and CONFIG_IO_STRICT_DEVMEM=y Restrict physical memory access.

CONFIG_SECCOMP=y and CONFIG_SECCOMP_FILTER=y Allows userspace to reduce the attack surface.

STATIC_USERMODEHELPER=y Force all usermode helper calls through a single binary

CONFIG_HARDENED_USERCOPY=y performs extra usercopy checks.

For more security features, please check the following Kernel Self Protection Project guide [2]. There are lot of features that needs to be disabled on production systems. You should also consider trusted boot and TPM support from the beginning, they are not perfect but it is an extra layer to measure the boot process and allow TPM to act on that.

Current/Future plans:

  1. Modernization of proc file system

I have been working on modernizing Linux proc API [3]. The procfs file system is an old virtual file system that exposes lot of kernel information, some of these files can be used as a source for information leaks to get kernel addresses and construct complex attacks. We have came to the conclusion that procfs needs to be updated and it is blocking lot of other Linux features. Together with Alexey Gladkov and based on Andy Lutomirski feedback, we have a branch here: https://github.com/legionus/linux/commits/pidfs-v4 [4] that allows to improve Linux procfs and protect exposed kernel files, only /proc// files will be available.

It also allows to have separate procfs instances per App, this means you are not forced anymore to use PID namespaces to hide some processes, simply mounting a new separate procfs instance with “hidepid=” will allow to hide at some degree other processes that belong to other users, and without using PID namespaces. This is useful for Linux-based IoT, as we may not need to have multiple PID namespace managers. Another introduction RFC is planed soon to try to make the transition as smooth as possible.

  1. Automatic module loading protection

historically, Linux was always able to transparently load kernel modules to satisfy user functionality when needed, this is a crucial point for a better user experience. However, this feature can be abused to load vulnerable modules and exploit some kernel bugs. Several examples come to mind:

The 11 year old DCCP double free vulnerability CVE-2017–6074 [5]

CVE-2017–2636: exploit the race condition in the n_hdlc Linux kernel driver bypassing SMEP [6]

kernel: Local privilege escalation in XFRM framework CVE-2017–7184 [7] it was advertised that maybe this vulnerability was used to break Ubuntu systems on security contests.

Most Embedded Linux systems should not allow module loading at all, or only a subset of signed modules, however there are products that need unrestricted module support. Inspired by Grsecurity MODHARDEN [8] feature, we have tried to implement a new generic module autoloading restriction that can be used by sandbox tools. The result is here: https://lkml.org/lkml/2017/5/22/312 [9]. Thanks to Kees Cook, Solar Designer and Andy Lutomirski for the feedback, I am planning v5.

  1. Generalizing YAMA LSM

Yama LSM [10] is a Linux security module with a clear purpose of protecting processes from being controlled by other processes using ptrace functionality. Recently, I have been experimenting on how to generalize this behaviour to other kernel interfaces and system calls. The aim is to have a simple interface to restrict some operations from operating on arbitrary processes, or processes that run under a different User ID. This should be a default kernel hardening measure on Embedded Linux.

References:

  1. https://kernsec.org/wiki/index.php/Kernel_Self_Protection_Project

  2. https://kernsec.org/wiki/index.php/Kernel_Self_Protection_Project/Recommended_Settings

  3. https://lkml.org/lkml/2017/4/25/282

  4. https://github.com/legionus/linux/commits/pidfs-v4

  5. http://seclists.org/oss-sec/2017/q1/471

  6. https://a13xp0p0v.github.io/2017/03/24/CVE-2017-2636.html

  7. http://www.openwall.com/lists/oss-security/2017/03/29/2

  8. https://grsecurity.net/

  9. https://lkml.org/lkml/2017/5/22/312

  10. https://www.kernel.org/doc/Documentation/security/Yama.txt