The Kernel Self-Protection project aims to make Linux more secure
Kernel Keeper
Security vulnerabilities in the kernel often remain undetected. The kernel hacker initiative, Kernel Self-Protection, promotes safe programming techniques to keep attackers off the network, and, if they do slip through the net, mitigate the consequences.
Any Black Hat who finds a previously unknown vulnerability in the Linux kernel has hit the jackpot. Potentially millions of servers and embedded devices are suddenly open to attack, and the attacker can usually gain root privileges. Users clearly don't want this to happen, and kernel makers try to prevent such events.
Based on pure theory, strict coding standards and a sophisticated software quality management system ensure that loopholes are found immediately and eliminated before the release. A shining and rare example is OpenBSD, which in 20 years' time has only had two significant security breaches [1]. Although I am certainly a supporter of the vigilant approach followed by OpenBSD, we have to be realistic: The Linux kernel contains mountains of code that no one can review with the required depth; dependencies vary, and thus so do the possible attack vectors. (See the box entitled "Harmless Start" for a complex example.)
Harmless Start
Some security breaches are well hidden. CVE-2015-7547 started with an innocuous-sounding bug report on Glibc 2.20 [2]: A programming error that had probably existed since Glibc 2.9 caused a program crash. Six months later, it turned out that a skillful combination of access turned the error into an attack.
The Glibc programmers had provided a buffer of 2048 bytes on the stack for a DNS response, which they wanted to put back on heap if the response was larger. But this sometimes went wrong, because the function queried an IPv4 and an IPv6 address and tried to use the rest of the buffer in the second answer. Only if that didn't work was the bigger buffer requested on heap. However, the variable that held the pointer for this buffer was not updated, so that access to the stack continued. A stack overflow was made possible by the fact that the size check no longer fit the situation.
Taking advantage of this (now remedied) error situation involves some complications for the attacker: For example, they would have to send DNS packets larger than 2KB. According to the vulnerability investigators, the attacker therefore either needs control over a DNS server or at least the ability to spoof DNS packets as the man-in-the-middle [3]. Alternatively, an attack could also be triggered by clever timing. To do this, the second response has to arrive after the time out and the first response needs to be larger than 2KB. For the second response, Glibc then incorrectly sets the buffer size to "large," while it only allocates a small buffer to the stack. This allows a stack overflow.
Because the attack requires an unusual combination of parameters and complex dependencies, it is difficult to detect. One measure that could help detect such an attack is a canary (described later in this article). If a check reveals the canary value has changed, a buffer overflow has occurred. Other measures include address space layout randomization. A non-executable stack would have made the attack more difficult and thus reduced the risk.
The complexity of the Linux kernel means that it is likely to carry legacy ballast and bugs for an indefinite period of time. At the end of 2010 [4], Jonathan Corbet checked how long the safety-relevant bugs eliminated in that year had existed until discovered: 22 of the 80 loopholes examined had been in the code for more than five years!
Practical experience leads to an approach that simultaneously makes attacks more difficult and reduces the consequences of exploitable code weaknesses. This two-pronged approach is the goal of the Kernel Self-Protection [5] project.
Break-In Technology for Everyone
Viewed through the looking glass with sufficient hindsight, most attacks on programs work in a similar way. An attacker tries to add new program code to a running process, which the hijacked process then executes with its privileges. The added code can be SQL or shell commands, or typically, binary code in kernel attacks. In order to inject this code, attackers exploit programming errors that allow them to determine memory contents and manipulate the program counter.
The program counter is a CPU register that points to the next instruction to be executed. For Intel processors, the 16-bit programs had the Instruction Pointer (IP), the 32-bit world had the Extended IP (EIP), and the 64-bit world has a somewhat-morbid Relative Instruction Pointer (RIP). Almost all attacks aim to change the contents of this register in such a way that it points to one of the attacker's commands instead of the next command intended by the programmer.
Direct write access to this register is virtually impossible, which makes a small detour necessary. When a program calls a function, the program copies the return address (i.e., the point at which the program will continue running after calling the function) to the stack. Since the stack also contains local function variables that the attacker may be able to manipulate through input, this return address is a popular destination.
A Leap Back to Ruin
The attacker has several options for changing the address. A buffer overflow is a bit rough: The attacker simply overfills a variable with excessive amounts of data and floods the return address (Figure 1). Slightly more subtle attacks rely on format string vulnerabilities, which make it possible to both read and manipulate the stack.
Integer overflows are another possibility; for example, an attacker might exploit the fact that signed and unsigned integers have different value ranges. The unsigned number 128 could become a -128. If the program only checks whether a certain upper limit is complied with, for example, because there is a maximum of 100 values on the stack, -128 is technically OK. Instead of occupying memory locations 0 to 100 as the programmer intended, the range now extends from -128 to 100, which can trigger an overflow situation.
Run for Cover
An attacker can attempt to manipulate the stack or heap. Both are storage areas in which data is normally stored. Therefore, a popular avoidance strategy, which Kernel Self-Protection also advocates, is to mark memory areas as either executable or non-executable (in other words, to declare some areas as code and others as data). Newer CPUs have a bit for this in the page descriptor. AMD refers to this as the No Execute (NX) and Intel as the Execute Disable (XD) bit. Linux kernel supports write protection in this context.
Although these access rights do not provide 100 percent protection, they at least make the attack more difficult. For example, advanced attackers could still gain control of the system with a Return to Libc attack, which writes the entry address of execve()
to the stack along with suitable parameters [6] [7].
Successful attackers first only manage to influence the memory of a hijacked process. It's a good thing that modern processors contain functions that separate the kernel memory from application memory. In this case, a kernel function must not execute any commands that have been injected into the user space. This protection also makes attacks more difficult.
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Juno Tab 3 Launches with Ubuntu 24.04
Anyone looking for a full-blown Linux tablet need look no further. Juno has released the Tab 3.
-
New KDE Slimbook Plasma Available for Preorder
Powered by an AMD Ryzen CPU, the latest KDE Slimbook laptop is powerful enough for local AI tasks.
-
Rhino Linux Announces Latest "Quick Update"
If you prefer your Linux distribution to be of the rolling type, Rhino Linux delivers a beautiful and reliable experience.
-
Plasma Desktop Will Soon Ask for Donations
The next iteration of Plasma has reached the soft feature freeze for the 6.2 version and includes a feature that could be divisive.
-
Linux Market Share Hits New High
For the first time, the Linux market share has reached a new high for desktops, and the trend looks like it will continue.
-
LibreOffice 24.8 Delivers New Features
LibreOffice is often considered the de facto standard office suite for the Linux operating system.
-
Deepin 23 Offers Wayland Support and New AI Tool
Deepin has been considered one of the most beautiful desktop operating systems for a long time and the arrival of version 23 has bolstered that reputation.
-
CachyOS Adds Support for System76's COSMIC Desktop
The August 2024 release of CachyOS includes support for the COSMIC desktop as well as some important bits for video.
-
Linux Foundation Adopts OMI to Foster Ethical LLMs
The Open Model Initiative hopes to create community LLMs that rival proprietary models but avoid restrictive licensing that limits usage.
-
Ubuntu 24.10 to Include the Latest Linux Kernel
Ubuntu users have grown accustomed to their favorite distribution shipping with a kernel that's not quite as up-to-date as other distros but that changes with 24.10.