masterThesis
masterThesis
masterThesis
net/publication/365872100
CITATIONS READS
2 961
1 author:
Stefan Bavendiek
Hamburg University
2 PUBLICATIONS 3 CITATIONS
SEE PROFILE
All content following this page was uploaded by Stefan Bavendiek on 30 November 2022.
Submitted by
Stefan Bavendiek
[email protected]
1
List of Figures
1 Research Objectives . . . . . . . . . . . . . . . . . . . . . . . . . 10
2 Security Boundaries . . . . . . . . . . . . . . . . . . . . . . . . . 23
3 User space to Kernel Communication . . . . . . . . . . . . . . . 37
4 Kernel Subsystems . . . . . . . . . . . . . . . . . . . . . . . . . 38
5 Research Approach . . . . . . . . . . . . . . . . . . . . . . . . . 55
6 Test execution steps . . . . . . . . . . . . . . . . . . . . . . . . 62
7 Module Complexity . . . . . . . . . . . . . . . . . . . . . . . . . 66
8 Hardware dependent Complexity . . . . . . . . . . . . . . . . . 71
2
Contents
1 Introduction 6
1.1 Problem Statement . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2 Kernel Security . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1.2.1 Linux Kernel Architecture . . . . . . . . . . . . . . . . . 7
1.2.2 Interface and Exposure . . . . . . . . . . . . . . . . . . . 7
1.2.3 System Calls . . . . . . . . . . . . . . . . . . . . . . . . 8
1.3 Research Object . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.4 Approach and Organization . . . . . . . . . . . . . . . . . . . . 11
3 Security Metrics 27
3.1 Measuring Security . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.2 Vulnerabilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.3 Software Quality Metrics . . . . . . . . . . . . . . . . . . . . . . 30
3.3.1 SLOC . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
3.3.2 Cyclomatic Complexity . . . . . . . . . . . . . . . . . . . 31
3.3.3 Cognitive Complexity . . . . . . . . . . . . . . . . . . . . 32
3.4 Complexity and Security . . . . . . . . . . . . . . . . . . . . . . 33
4 Kernel Architecture 35
4.1 Kernel Interfaces - System Calls . . . . . . . . . . . . . . . . . . 36
4.2 Overview of Linux subsystems . . . . . . . . . . . . . . . . . . . 37
4.2.1 System call interface . . . . . . . . . . . . . . . . . . . . 39
3
4.2.2 Process management . . . . . . . . . . . . . . . . . . . . 39
4.2.3 Memory management . . . . . . . . . . . . . . . . . . . . 39
4.2.4 Virtual file system . . . . . . . . . . . . . . . . . . . . . 40
4.2.5 Network stack . . . . . . . . . . . . . . . . . . . . . . . . 40
4.2.6 Device drivers and device management . . . . . . . . . . 41
4.2.7 Architecture-dependent code . . . . . . . . . . . . . . . . 41
4.3 Kernel Modules . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.4 Hardware dependent code . . . . . . . . . . . . . . . . . . . . . 43
4.5 Source Code Structure . . . . . . . . . . . . . . . . . . . . . . . 44
4.6 Kernel Attack Surface . . . . . . . . . . . . . . . . . . . . . . . 46
5 Related Research 47
5.1 Attack Surface Analysis . . . . . . . . . . . . . . . . . . . . . . 47
5.2 Measuring Security . . . . . . . . . . . . . . . . . . . . . . . . . 47
5.3 System Call Exposure . . . . . . . . . . . . . . . . . . . . . . . 48
5.4 Measuring Vulnerabilities . . . . . . . . . . . . . . . . . . . . . . 49
5.5 Quantified Attack Surface Reduction . . . . . . . . . . . . . . . 50
5.6 Shortcomings of previous Research . . . . . . . . . . . . . . . . 52
5.6.1 Manual Attack Surface Analysis . . . . . . . . . . . . . . 52
5.6.2 Measuring known Vulnerabilities . . . . . . . . . . . . . 53
5.6.3 System Call Sandboxing . . . . . . . . . . . . . . . . . . 53
5.6.4 Compile-time Kernel Reduction . . . . . . . . . . . . . . 54
6 Approach Reasoning 55
6.1 Attack surface and complexity . . . . . . . . . . . . . . . . . . . 56
6.2 Linux Kernel Modules . . . . . . . . . . . . . . . . . . . . . . . 57
6.3 Hardware Dependent Complexity . . . . . . . . . . . . . . . . . 58
7 Testing Process 59
7.1 Sonarqube . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
7.2 Test Parameter and Environment . . . . . . . . . . . . . . . . . 60
7.3 Test Execution Process . . . . . . . . . . . . . . . . . . . . . . . 62
4
8.2 Complexity Difference . . . . . . . . . . . . . . . . . . . . . . . 64
8.3 Interpretation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
8.4 Impact of optional Kernel Modules . . . . . . . . . . . . . . . . 66
8.4.1 SELINUX . . . . . . . . . . . . . . . . . . . . . . . . . . 67
8.4.2 AMDGPU . . . . . . . . . . . . . . . . . . . . . . . . . . 67
8.4.3 KVM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
8.5 Impact of File Systems . . . . . . . . . . . . . . . . . . . . . . . 69
8.5.1 ext4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
8.5.2 xfs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
8.5.3 btrfs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
8.5.4 namespaces . . . . . . . . . . . . . . . . . . . . . . . . . 70
8.6 Impact of Hardware Dependent Subsystems . . . . . . . . . . . 71
8.6.1 Instruction Architecture . . . . . . . . . . . . . . . . . . 71
8.6.2 Hardware Drivers . . . . . . . . . . . . . . . . . . . . . . 72
9 Conclusion 73
9.1 Hardware Dependent Kernel Builds . . . . . . . . . . . . . . . . 73
9.2 Environmental Dependent Kernel Builds . . . . . . . . . . . . . 73
9.3 Install Time Kernel Build Reduction . . . . . . . . . . . . . . . 74
9.4 Comparison to Previous Research . . . . . . . . . . . . . . . . . 74
10 Further research 75
5
1 Introduction
The security of information systems is one of the most relevant topics for the
digital age and a lot of money is put into securing digital infrastructures [1].
Research regarding the current state of IT security, indicates an increasing
threat landscape[2]. As a result, cyber crime continues to increase [3]. As
the complexity in our software grows, the number of security vulnerabilities
increases as well[4]. While cyber security consists of many areas such as ap-
plication and software security, the basis of this infrastructure is provided by
the operating system. As one of the most common general purpose operating
systems, Linux is not immune to security risks [5]. However the Linux operat-
ing system is extensively used on both web servers [6] as well as mobile devices
[7]. With more then 2.5 billion active Android devices in the world [8], the
security of the Linux operation system is of great significance. However, the
Linux kernel itself is a complex piece of software with more then 27.8 million
lines in 2020 [9]. This results in a large attack surface to impact the security
of Linux based systems. The potential consequences of security vulnerabili-
ties in common software systems has been demonstrated by real world cyber
attacks such as Stuxnet [10], WannaCry [11], Mirai [12] and NotPetya [13].
The assessed damaged caused by NotPetya alone is estimated to amount to
more then 10 billion dollar, according to senior cybersecurity officials in the
US government [14]. Since our economy is increasingly dependent on a reliable
digital infrastructure [15] the potential damage caused by future cyber attacks
becomes even more relevant. Given this outlook on the consequences of ex-
ploitable vulnerabilities in common software products, it becomes clear that
critical software components like the Linux operating system, need to be pre-
pared for future security challenges. Mitigating the risk of widely exploitable
vulnerabilities in the Linux kernel requires a throughout analysis of the kernel
architecture. By researching the different components of the Linux kernel and
their included complexity, it is possible to illuminate which areas are most at
risk.
6
1.2 Kernel Security
The Linux kernel contains a large code base including components that have
different significance to the security of the system. The most relevant kernel
areas will be shortly introduced here to allow a better understanding of the
research objective.
The Linux kernel consists of a number of subsystems that form the operating
system. These different components build a complex software system with a
significant codebase [9] that presents a large attack surface for security vul-
nerabilities to be exploited [16]. To analyze and measure the attack surface
presented by the Linux kernel, it is substantial to understand and dissect the
architecture and codebase of Linux. The architecture of the Linux kernel has
already been analyzed in detail [17] [18] and the current kernel code is well
documented [19]. Based on these documented architectural description, it will
be possible to conduct an analysis of the attack surface. A significant part of
this analysis will include research into the role of the different Linux kernel
components in regards to the complexity of the kernel as a whole. Based on
an understanding of the different code areas of the Linux Kernel, an analysis
of the relevant code sections can reveal critical parts of the kernel code, that
significantly impact the complexity of the resulting software.
The Linux kernel exposes different functionality to users and processes, based
on their privileges [18]. This permission model is enforced throughout the
kernel, and exposed to a number of interfaces the kernel provides. These
interfaces themselves also expose different areas of the kernel to their users.
To analyze the attack surface of Linux, it is crucial to dissect the different
methods to interact with the kernel and break down the exposed codebase
and functionality these interfaces provide. The measurement of the exposed
codebase and functionality is used as the basis of the analysis. The specific
features provided by the kernel as well as their software implementation include
different risks, based on the data they handle and the privileges they may
7
provide, as well as the code quality of the implementation. These attributes
will require a throughout examination to measure the detailed risk areas with
the Linux kernel. Besides the detailed examination of code executions paths,
an attack surface analysis also requires a specific threat model. The exposed
and potentially exploitable code depends on this threat model and is commonly
defined by the location and privileges an attacker requires to reach the exposed
interfaces of the software system. The threat model differs significantly for a
remote unauthenticated attacker on the network, in comparison with a local
user, or even a privileged user.
8
abstracted interface that uses a system call indirectly. However in the end,
the data provided by the user space process is passed to the kernel, where it
is processed. This also means, that exploitable vulnerabilities in system calls
can impact the integrity of the kernel itself. The consequences depend on the
vulnerable code and the functionality the affected system call provides. Recent
examples like the DirtyCow vulnerability [20] show the significant impact that
kernel vulnerabilities can have. The number of exposed system calls from the
context of a user space application process, will therefore provide a significant
impact in regards to the attack surface of the kernel.
9
1.3 Research Object
Using this method to highlight more complex code areas, has the potential
to identify parts of the Linux kernel, that are more prone to software vulnera-
bilities. The results of this analysis will therefore provide system engineers with
a method to measure the attack surface of kernel features and make qualified
decisions regarding their use and their associated risks.
10
1.4 Approach and Organization
The attack surface of a complex software system like the Linux kernel requires
extensive research on various components. While there are some research ap-
proaches to analyze the detailed usage of kernel code by various features and
functions [23], the exact extent of used kernel code is difficult to assess and can
change rapidly, as the Linux kernel code is under fluent development. In order
to find new and improved ways to assess the kernel attack surface, previous re-
search on Linux kernel security assessments and general software attack surface
measurement was analyzed. Using common research sources like IEEE Xplore
[27] and Elsevir [28] as well as common search engines like Google Scholar
[29], existing research papers on related topics where explored to assess the
current state of research. Aside from attack surface analysis methodologies
and previous works on Linux kernel security, additional research regarding the
measurement of security attributes and their metrics was targeted. Based on
the identified previous research work, a new approach to improve the current
methodology for attack surface analysis was created. To provide a reason-
able assessment of the related risks regarding specific kernel components and
features, the approach used in this paper will focus on software quality assess-
ments, as an establish software engineering methodology. Instead of attempt-
ing to approach the security assessment of the Linux kernel using previously
used methods like attack vector identification and measuring their exposure
towards specific adversaries, this research project will use static measurement
of cyclomatic complexity related to different kernel subsystems and features.
With the previously established correlation between complexity and security
[25] this method will provide an approximated indicator based on a well es-
tablish complexity metric. While this process is not expected to produce an
exact measure of the attack surface, the results can provide a reliable indi-
cation of security sensitive code areas due to their complexity. To produce
comparable results, a number of common kernel features will be analyzed by
compiling and measuring the Linux kernel using different kernel configurations
to include the targeted subsystems. Based on the results, consequent measures
will be presented to allow the use of a kernel configuration that supports the
reduction of the available complexity and thereby increased security. The first
11
part of this paper will define core principles like attack surface definition and
common security metrics. Specific attention will be directed to the advantages
and issues of the previously established methods to assess software security
attributes. Subsequent chapters will present the structural overview of the
Linux kernel code to highlight the relevance of the different kernel components
for this research. Related research approaches in this field will be analyzed to
examine existing methods and their attributes. This is followed by a detailed
description of the used analysis approach and testing execution. The last chap-
ters will present the measurement results and provide an interpretation and
possible use cases for the results of this research.
12
2 Attack surface Analysis
Attack surface analysis are used to identify areas of a software product that
are vulnerable to attacks. This includes the finding of potential attack vectors
as well as their exposure. This chapter will describe the fundamental steps
related to the execution of attack surface analysis and the challenges related
to their measurement.
2.1 Objective
13
2.2 Attack Vectors
The attack surface is composed of the sum of all possible ways to interact
with the software during its operation, also known as attack vectors [21]. In
general that includes all kinds of input and output of data that the analyzed
software is involved in [23]. Every single communication path between the
software and outside systems can therefore be considered an attack vector
as well. Since communication requires the processing of data from external
sources, it presents a possible way to exploit vulnerable code in the targeted
software and adds to the attack surface of the application. This does not only
affect functions that are directly involved in the processing of external data,
but also all code that is indirectly involved in the support of those functions.
While a vulnerable parsing error in the data processing function is the most
direct way to exploit the targeted software, modern cyber attacks use more
complex exploit chains that demonstrate how even seemingly unrelated code
areas can influence the attack surface [34] [35]. It is therefore essential to
identify the attack vectors a software exposes and to assess the risk involved in
that specific attack vector, to measure the overall attack surface [23]. Since the
objective of an attack surface analysis is to assess the possible risks involved
in running a software application, it is necessary to consider all possible attack
vectors on the overall system. That includes not only the application itself,
but all other related services and system components required to run it. A web
application itself may have a seemingly simple attack surface that is mainly
composed of the exposed API functions. However it depends not only on a
web server for its use of the HTTP protocol, but also on a large amount of
underlying system function the operating system provides. Other services used
to manage and maintain the application platform will also affect the attack
surface of the system as a whole. While in same cases it may be intended
to analyze only the additional attack surface of a specific system component,
the objective of this paper is to analyze the attack surface of the Linux kernel
itself, which is a common part of the overall system of many modern software
applications.
14
2.3 Kernel Attack Vectors
The Linux kernel presents a large number of possible ways for userspace ap-
plications to interact with the services provided by the kernel. In general, all
kernel functions available to userspace processes are provided through system
calls. Through these system calls, the applications can make use of different
kernel subsystems such as process management, memory management, the vir-
tual file system or network interfaces. These subsystems also use architecture
dependent code as well as device drivers that are specific to the used system
hardware. The subsystems are also influenced by kernel extensions such as
provided by kernel modules. While only a small part of the kernel codebase is
directly accessible by user space applications through the provided system calls
[36], all kernel code is indirectly involved in providing the requested services of
the operating system. This means that even if a kernel function does not pro-
vide a direct interface to user space applications, the code will still provide a
functionality that the user space application relies on. Kernel subsystems that
are directly involved in processing data from user space application present the
primary attack vectors of the kernel, while other kernel code provides these
directly exposed subsystems with indirect functionality. Since all of the kernel
code lives in the same memory space, a single error in any part of the kernel
can affect the system as well [37] [38]. While a complete list of available at-
tack vectors significantly depends on the used kernel features, the following list
presents an overview of the some common attack vectors on Linux systems.
• Network Interfaces
– Shared Memory
– System V IPC
15
– Middleware IPC Services (DBus)
• Kernel Interfaces
While some of these listed attack vectors, like network and kernel interfaces,
directly expose kernel space functions that may lead to a direct compromise of
the kernel itself, other attack vectors like inter process communication channels
can indirectly expose additional attack vectors by exploiting higher privileged
user space processes [39]. For example, the exposure of kernel system calls is
different for user space processes depending on the inherited privileges [40].
Even kernel code that does not process user space data in any way, may have
an impact on the security of the system[41]. Measuring these indirect risks is
difficult and new attack vectors to expose previously security unrelated kernel
code are regularly found by security researchers [42]. The case of Linux user
namespaces [41] demonstrates how seemingly unrelated code that does not
process untrusted data, can indirectly lead to the exposure of additional attack
vectors with significant impact [43] [44] [45]. Therefore these common kernel
attack vectors present a subset of the current attack vectors presented by the
Linux kernel.
2.4 Exposure
Attack vectors depend on the exposure towards a specific attacker [23]. Some
attack vectors may not be reachable for specific adversaries, depending on
the capabilities, physical location and privileges of the attacker. The specific
attack surface from the perspective of an attacker, therefore depends on the
exposure of the reachable attack vectors. An unauthenticated, remote attacker
will have different attack vector options to target the software, then an authen-
ticated user, or even a user with local shell access [40]. The different kinds
of access are defined by the security boundaries a system implements. The
exposure also depends on the physical location and the privileges of a user.
16
Essential questions to be answered during the attack surface analysis focus
on the different possible ways an attacker can interact with the system [46]
[22]. Based on the kind of software that is being analyzed and the deployment
environment, the exposure of the software can change significantly.
2.4.1 Remote
The exposure of remote network services is one of the most relevant cases for
attack surfaces analysis [22]. Since network based services are often provided
to a large number of possibly untrustworthy systems and hostile adversaries,
assessing and restricting the exposure of these systems is an established se-
curity measure [47]. Most predominantly internet facing services are common
application that face this kind of exposure. By providing an interface to the
public internet that allows arbitrary users to interact with the software system,
a large exposure needs to be faced by this application. Keeping the number of
attack vectors to a minimum, is therefore a crucial task for system engineers
that build internet facing services, since any exploitable vulnerability in the
provided software interfaces will be available to attack for all internet con-
nected systems [48] [49]. Due to this, internet facing systems that are directly
reachable to the public, often provide limited functionality, while the more
complex functionality of these internet services are more restricted, for exam-
ple by authentication requirements [50]. Other remote services can be provided
to the systems of a specific network only, creating an intranet, thereby restric-
tion the number of possible attackers. Networks such as common intranets
usually provide a significantly smaller exposure to its internal services [51].
Common measures to reduce the exposure of a service, such as firewalls and
VPN services, have been around for several decades [47]. However, common
intranet services such as applications based on the RDP, SMB or NFS pro-
tocol may be designed to be reachable by trusted entities only [52] [53]. To
reduce the attack surface of these services, network based restrictions are an
important part of the architecture [54]. The effectiveness of network restric-
tion measures are however not only difficult to measure and enforce [55], the
increasingly dynamic applications environment of modern IT infrastructures
also make it difficult to implement the required security policies reliably [56].
17
Since attackers only need to compromise a single device on the intranet to
circumvent network based restrictions, the exposure of these services, while
not directly available, is still reachable by combining an attack with additional
steps, such as compromising an end user device by phishing [57].
Other measures to reduce the exposure of network based software applica-
tions can include authentication requirements to access specific features. By
requiring authentication for parts of the software functions, the attack vectors
of these parts are shielded from unauthenticated entities [58]. However, the
authentication mechanisms adds new attack vectors and can be significantly
complex itself, thereby adding more attack vectors [59]. In addition to the
additional attack surface that network restriction and authentication mecha-
nisms add to a system, they also add additional systems [60]. While a simple,
internet facing application with minimal attack vectors may face a significant
exposure, more complex systems that are shielded by additional measures to
reduce the exposed attack vectors also introduce new attack vectors presented
by the additional security measures [61]. The exact exposure presented by
more complex remote network based applications, is hard to measure [62] and
to enforce reliably [55], while additional restrictions to limit this exposure can
also add additional attack vectors and complexity to the overall system, thus
increasing the overall attack surface [61].
Linux kernel attack vectors that are most relevant to this kind of exposure
include network interfaces on the different OSI layers [63]. However, similar
to the network based restrictions discussed before, some parts of the kernel
may be indirectly accessible to a remote entity [35]. Since the remotely ex-
posed network application itself runs on the local system, it can directly or
indirectly provide access to the locally exposed attack vectors. While a well
designed network service may try to shield access to local kernel interfaces [32],
exploitable vulnerabilities in the remote application itself can expose the local
attack vectors of the operating system.
2.4.2 Local
While many services are made available to a large number of entities via net-
worked systems, some kinds of services and interfaces may only be accessed
18
locally. The exposure of a systems attack vectors from the perspective of a
local entity, changes significantly depending on the available services, their
privileges and configuration [40]. While the network interfaces of a system as
well as the network based applications are expected to face significant expo-
sure, local interfaces may be build with different threat models due to their
limited exposure [64]. Measuring the exposure of the local attack vectors de-
pends on more detailed assumptions regarding the possibilities of an attacker
[65]. Local software applications or services may be build with the expectation
that only trusted users and input data will interact with the running process.
The initial attack surface of a local application, can be reduced to its local in-
terfaces and parsing functions [66]. One possible way for an attacker to interact
with local applications is to indirectly send input data that is able to exploit
a parsing error within the local process. This can be achieved by tricking the
user into opening an attacker controlled file, or by exploiting design errors in
remotely reachable applications, that send data to local processes for further
processing [67]. This kind of indirect interaction with local applications is of-
ten implemented by utilizing phishing attacks or other kinds of psychological
tricks that lead a legitimate user into opening untrustworthy data using a local
software application [57]. Complex file types, such as PDF or office file formats
that are a established way of exchanging data on the internet, are especially
prone to be exploited using this method [68]. Vulnerabilities in these kinds of
applications are therefore a primary attack vector for local software.
Once an attacker has gained code execution on a local system, either by
exploiting a remote service or a local application, the available attack vectors
towards the local system change significantly. While the attack vectors of local
applications and remotely accessible services are defined by specific software
that the overall system is often designed for, a local attacker can access all
services and interfaces that are provided the the exploited process. Arbitrary
code execution in the context of a process means, that the attacker has gained
the permission privileged of the user, that is running the exploited process
[69]. A local user or process on a Linux system, is therefore permitted to access
significantly more resources and services provided by the kernel. Some attacks,
such as denial of service, are trivial for local users to execute in the absence of
additional hardening features. For example, any process on a Linux system can
19
fork itself indefinitely, until the available system resources are depleted [70]. If
the targeted process is running with limited user permissions, the attacker may
have gained access to only those resources that the exploited process requires
to provide the intended services. However, the next permission boundary that
can be interacted with by the attacker after having achieved local shell access,
is that of both other more privileged application as well as the operating system
kernel itself [39] [40] [36]. While the reachable attack vectors of the remote
network services is limited to specific functions intentionally provided by the
application, the interfaces for local processes, as presented by local services
and kernel interfaces is significantly larger. The number of available system
calls in version 3.7 of the Linux kernel already amounted to 393 [71]. These
system calls present the attack vectors that are directly exposed towards the
Linux kernel from the perspective of a local user space process.
20
An unauthenticated remote entity, is usually presented with the smallest
attack surface towards a software system. After being authenticated, a remote
entity is exposed to an increased amount of the application interfaces, depend-
ing on the specific authorized permissions that entity was granted. However
the exposed attack vectors are still limited to the application, unless the re-
mote service already includes significant vulnerabilities that allow the user to
execute code on the local system [77]. An application based authentication
boundary is therefore often exposed to a large number of systems, like in the
case of common web based applications that are provided to the public inter-
net with minimal identification of the remote users. Even with only a small
number of attack vectors, the significant exposure of these applications leads to
a large attack surface, due to easy exploitability of publicly accessible systems
[78]. For this reason, these kinds of applications are usually confined into a
DMZ network segment, thereby shielding other services from further, indirect
attacks [79]. In case a remote application interface is provided to a more lim-
ited network, like an intranet, the security boundary may be implemented by
a VPN service that controls the access to the local network itself. Restricting
access to a service by a physical proximity is an alternative boundary, that
includes less technical and more social attack vectors [80]. Overall, the expo-
sure of internal network services is significantly smaller then that of software
systems that are exposed to the public internet. This however can lead to
additional risks, since the exposure from the internal network service towards
other internal infrastructure systems is now a new factor, that becomes rele-
vant in case an attacker compromises a single system with intranet access [55].
While the initial exposure of a software system that is only accessible from an
internal network is significantly smaller compared to public internet services,
the indirect exposure of other critical systems can be greatly increased, making
an assessment of the overall attack surface of the infrastructure more difficult.
This phenomenon is even more significant if the security boundary towards a
local operating system is targeted. Local applications that are not provided to
other networked systems, include a very limited exposure towards attackers.
Since an attacker cannot interact with a local software without gaining first
access to a local user, the direct exposure of these local software interfaces is
nonexistent. However, indirect access is still possible, for example by exploiting
21
another network based service on the same host, or by using social engineering
methods to trick the user into parsing untrusted data, that exploits vulnera-
bilities in the local applications that are used to process this data. To access
the local attack vectors of a system, therefore requires additional attacks on
other parts of the system. However, once local code execution is achieved, the
available attack vectors are significant. After gaining local shell access with the
permission of either a restricted or normal user, the next security boundary is
presented by the authentication system that enforces the available permission
on the local system, with the objective of gaining full administrative control.
This can be achieved by exploiting vulnerabilities in privileged services that
are exposed to unprivileged users. Depending on the privileges an application
is running with, the attacker can already have gained privileged system access
at this point. Besides exposed privileged user space applications, an attacker
with local shell access has already access to a large number of system calls,
provided by the operating system kernel. Exploiting kernel vulnerabilities in
these system calls, can allow a local user to gain administrative privileges.
The last possible security boundary of a software system, is that between user
space and kernel space. This boundary is enforced by the different protection
ring implemented by the CPU itself. Gaining full kernel space access can be
achieved by exploiting critical vulnerabilities in system calls that the operat-
ing system provides. The availability of system calls, is also affected by the
permissions of a user [36]. While a large amount of system calls on Linux
systems is already available to unprivileged users, some system calls that pro-
vide security relevant functions are only exposed to privileged users. Kernel
features that extent the exposure of system calls to unprivileged users, have
consequently lead to security issues in the past [81]. As discussed, the secu-
rity boundaries affect the exposure of attack vectors. The visualization in the
graphic below, illustrates the effects of security boundaries in regards to the
exposure of different system components. While the directly reachable inter-
faces (red), indicate the immediate exposure, a number of additional attack
vectors are reachable indirectly (blue), either by user interaction [80] [57] or
through breaching previous security boundaries [55] [40].
Taking all these different security boundaries and their individual exposure
into account, makes an attack surface analysis significantly challenging. It can
22
Figure 2: Security Boundaries
23
objective. For some applications the data a system contains is the actual
target. This data may be available to even unprivileged users, thus making
further attacks on a system unnecessary [82]. The relevance of an attack surface
towards specific parts of a system, is therefore significantly influenced by the
data and services a software system provides [54]. One common example is a
database that is accessed by a web application to manage user data like credit
card information. If the objective on an attacker is to access and sell this data,
it may be enough to find and exploit a single vulnerability, accessed through
a publicly exposed attack vector. Given that attackers who aim for this kind
of data are usually profit driven, the direct accessibility of this data would
make the effort for such an attack worthwhile [3]. Since applications like these
are often used by a large number of organizations, a single vulnerability in a
publicly accessible interface that is commonly exposed to the public internet
will most likely result a large scale attack against public instances of this
software, since it only required the development and deployment of a single
exploit. The very limited attack surface of the application will not protect
itself due to the large exposure [78]. In other cases, the relevant data may
only be accessed by security hardened systems, located on an internal, highly
monitored network that can only be accessed by using strong authentication.
Taken into account the significant security boundaries required to access this
data, the indirect exposure of the target system is very small. However, if the
data is important enough to implement these extensive security measures, an
attacker interested in compromising the information may invest similar efforts
to attack the involved systems. A practical example of a cyber attack that
includes this kind of investment can be found in the stuxnet malware and
their use against strategic targets [10]. The potential gain of cyber attacks
increases with the development of digital infrastructures and governments have
less focus on economics but rather strategic objective value that makes usual
costs required for developing exploits less relevant [83]. The relevance of a
systems exposure in regards to an attack surface analysis, is therefore not
only influenced by the technical environment a system is implemented into,
but also nontechnical factors like the value of data, potential adversaries and
their capabilities or even political developments. The overall security risk
presented by software system can be assesses by a number of factors besides
24
the attack surface itself. The DREAD risk assessment model developed by
Microsoft uses five categories to determined the associated risks [84]. Damage,
reproducibility, exploitability, affected users and discoverability calculate a risk
rating for a specific software deployment according to this model. Other threat
models that are used today include the STRIDE model [84] that approach the
risk assessment by measuring the consequences of an attack. Similar to the
STRIDE model, the Common Vulnerabilities and Exposures (CVE) scheme
has seen a wide adoption for the assessment of risk in regards to software
vulnerabilities [85]. The exact measurement for calculating the risk associated
with a software system depend on a number of system attributes and priorities,
however risk is generally calculated by the likelihood times the impact of
a threat [86]. The attack surface of a software system presents the likelihood
of a successful attack, while the data value significantly determines the impact
of successful exploitation [87] [88].
25
exposed to unprivileged users by the introduction of this feature. A number
of kernel bugs were made exploitable for unprivileged users as a result [43]
[44] [45]. Related security risk are still present in modern container technology
[91]. An attack surface analysis that assesses the risk of an attack vector based
on its exposure, would be made incorrect with the introduction of this kind
of new software features as well as the discovery of new classes of vulnerabil-
ities like Spectre and Meltdown. Exposure changing software features as well
as newly discovered classes of vulnerabilities therefore make it obvious that
the exposure of an attack vector is very difficult to assess and unreliable in
light of changing threats and environments. Even worse, the introduction of
the previously mentioned CPU based vulnerabilities, did not only change the
exposure of vulnerable code, but introduced new attack vectors as well. Since
security researchers are regularly publishing newly discovered attack vectors
[92] [93], any attack surface analysis only presents a fragile assessment of the
current state of a software system, that can change by external factors easily
[62]. The question remains, how the attack surface of a software system can be
measured reliably, without depending on the environment, use cases, processed
data value, adversarial capabilities and newly discovered vulnerability classes.
26
3 Security Metrics
The assessment of the security attributes a software system possesses, requires
a objective metric to allow a comparison between different systems and com-
ponents. This chapter will describe the different challenges in measuring
security in software as well as establish metrics such as measuring identified
vulnerabilities and using software quality metrics due to their relevance
in previous research.
27
ing security assessments is not security but rather deviance from the intended
state, or insecurity. Counting the number of previously known vulnerabilities
a software product had is one way to compare such a deviation and compare
different products that provide similar functionality with each other. Software
quality metrics are also an establish method to measure common quality at-
tributes of a software source code. Given that security tightly connected with
software quality attributes, it may be possible to use some of these established
method to make an assessment about the software security attributes as well.
3.2 Vulnerabilities
28
ing vulnerabilities in the released software product by independent security
researchers or active exploitation discoveries in the field will also increase this
metric. However the chance of vulnerabilities being discovered in this way is
depending significantly on the circulation of the end product. Another factor
will be the average value of the systems and data influenced by the software
itself, as well as the environments the software is used in. A software that is
not deployed on large numbers of systems will decrease the chance of a profit
driven adversary to develop exploits for this product. If the data and sys-
tems connected to the software are of low value, it will also not give attackers
any significant incentive to attack the software, thus reducing the number of
discovered vulnerabilities by public exploitation. Comparing the number of
known vulnerabilities between different software product are therefore addi-
tional dependent on the comparability of their deployment condition. Only if
the exposure, data value, use case and effort put into finding vulnerabilities is
similar in the compared products, are the basic conditions for a comparison
provided. Other factors that need to be comparable are used secure coding
practices and development resources like number of developers involved. These
conditions are however very rarely met in real world software projects, as ev-
ery one of these factors can easily vary to a significant degree. The value of
counting the number of known and documented vulnerabilities to compare the
security of software products is therefore very limited and does not appear
to be a very useful metric to assess their security. The dependence towards
the effort put into discovering vulnerabilities as well as the discussed external
factors, make this metric unusable for assessing software security in general.
The number of known security vulnerabilities can however provide a value to
a security assessment in that it documents past issues that may or may not
indicate a overall software security state. Using this metric as an indicator
for vulnerable areas can still be useful to guide the focus for future security
reviews towards these specific areas. What this metric is not able to provide
is an indicator for the absence of vulnerabilities.
29
3.3 Software Quality Metrics
3.3.1 SLOC
Source lines of code (SLOC) is a simple software metric that is commonly used
to measure the extent of an applications source code. Since a larger codebase
has a higher potential of coding errors, the SLOC present an indicator of
potential quality and therefore security issues in program source code. However
the extent of the source code itself, does not make any statement in regards
to the actual quality of the source code and only allows a lose correlation with
the potential for errors in regards to the size of a software project. While it is
an intuitive measurement of source code attributes in regards to the extension
of the code base and logical code to comment line ratio, it is also an imprecise
measure. Difficulties in regards to comparability of this metric originates from
the different possible rules to count valid lines of code [98]. While there are
a number of available applications to measure the SLOC, the results can vary
significantly and make a comparison between software projects measured with
different tools even more difficult. Among the most relevant disadvantages of
this method to calculate source code quality, is its significant dependency on
the language, the programming style and its lose correlation with functionality.
While the comparison of projects written in the same programming language
can still serve a comparative value, different programming styles can distort
the results significantly, making the metric itself only a very simple and shallow
indicator of the actual extent of the code base. Yet the SLOC is one of the
most commonly used metrics to measure some aspects of source code in regards
to the expected quality.
30
3.3.2 Cyclomatic Complexity
31
3.3.3 Cognitive Complexity
While cyclomatic complexity does not measure the readability of source code,
the cognitive complexity metric was defined to include considerations regarding
the human effort required to understand the source code as well [100]. Cogni-
tive Complexity measures include code attributes that increases the difficulty
to understand the functionality from a programmers perspective. While it can
be argued, that Cyclomatic complexity indicates an exact metric in regards to
the code execution path complexity, as it is relevant during execution or static
code analysis, there are also use cases that require a profound understanding
and intelligent analysis of the code itself. Manual code reviews, especially in
security critical software like cryptographic libraries, are still required to find
certain classes of logical bugs and aberration from intended functionality, that
cannot possibly be found by static code analysis. To enable an efficient review
and maintenance of such code, it is crucial that the code is kept readable to
a reasonable degree. Cognitive complexity is not a substitution to cyclomatic
complexity but instead focuses on different complexity attributes. Using mul-
tiple complexity metrics to complement each other and nullify methodological
shortcomings, can therefore be a valid strategy to increase the informative
value of the complexity analysis.
32
3.4 Complexity and Security
33
guages will not result in any useful result as part of the resulting complexity is
moved from the source code to other parts of the system. Other programming
strategies that hide code complexity by using external libraries or abstractions
that provide external functionality will manipulate the results in similar ways,
limiting the value of the comparison. Measuring the complexity in imported li-
brary functions increases the difficulty and accuracy of the metric even further.
Additionally, the source code can include support for different operating sys-
tems, hardware or execution architectures. Since the end product is executed
on a specific platform only, these code areas cannot be added unconditionally.
Measuring the complexity for specific execution environments, would required
the exclusion of irrelevant code sections that remain unused.
If the base attributes of two different software projects are comparable, the
provided functionality also will additionally have a significant impact on the
complexity. Since additional features will increase the codebase and therefore
the complexity values, comparing software products that allow different use
cases, will make comparison in regards to complexity as a quality metric more
difficult. However, since the security of the software still correlates to the
overall complexity, additional features also need to be considered.
Comparing complexity metrics resulting from different versions of the same
software, can provide a easily comparable result that indicates the impact dif-
ferent features or changes between the two versions have on the resulting com-
plexity and therefore the overall approximate security. If the entire software
system, including all direct and indirect dependencies is measured, the re-
sulting complexity values represent the theoretical attack surface through the
potential of vulnerable code, without the need to consider currently known at-
tack vectors. While not all this vulnerable code may be exposed to be attacker
by typical adversaries in real world environments, it still allows an assessment
of the code security and quality attributes that is present in the system and
may potentially become exposed. The existing complexity of a software system
therefore serves as a measure for potential security issues, while non existing
complexity, can never result in vulnerable code. The reduction of software
complexity will therefore improve the software security and overall quality.
34
4 Kernel Architecture
The Linux operating system is based on a monolithic kernel that has been
under development since 1991. While Linux Torvalds is is the head devel-
oper of the Linux kernel, the development progress is driven by thousands of
developers without any significant connection towards each other [104]. The
development of the kernel is not the result of a strategic plan but rather an
evolutionary process [105]. While the monolithic nature of the Linux kernel
means that a significant amount of code is executed within the same mem-
ory space, there are some architectural design choices that make it possible to
limit the amount of kernel code that needs to be loaded at the same time. One
such feature is provided by loadable kernel modules (LKM), that allow the
addition of code into the kernel process during its execution. This allows to
use a significantly smaller operating system kernel that is still able to perform
the required tasks. Linux kernel modules can be used to add a number of
extensions to the currently running kernel, such as support for new hardware
devices and file systems. Many kernel features can be compiled as part of the
core kernel or as a loadable kernel module, and the compile time options that
regulate this behavior have significant impact on the resulting kernel. To un-
derstand the Linux kernel architecture it is required to consider the different
subsystems the kernel is comprised of. Additionally, understanding the role of
features such as loadable kernel modules and the impact of the build system
on the resulting kernel is crucial as well.
35
4.1 Kernel Interfaces - System Calls
At the highest abstraction level, the differentiation between user space and
kernel space is most apparent. Enforced by the CPU ring boundary, processes
in these two ares live in different address spaces that cannot interact with
each other directly. The only way for user space applications to communicate
with the operating system kernel is via system calls [38]. These can either be
addressed directly, or indirectly via user space libraries that provide functions
which use the kernels system calls directly instead [106] [107]. The system calls
process the requests from user space applications and execute the them using
different kernel subsystems. Some lower level parts of the kernel use hardware
dependent code that addresses the available hardware interfaces directly. The
graphic below describes the dependency flow of function calls from user space
to the different parts of the kernel until its execution by the actual hardware
platform.
36
Figure 3: User space to Kernel Communication
Before the actual approach to measure the attack surface of the Linux Kernel
is discussed, a general overview of the kernel architecture and source code
structure will help to highlight relevant code components that need to be
considered during the analysis.
The Linux kernel is comprised of a number of subsystems that provide
different operating system tasks [17]. Providing a high level abstraction of
the different kernel subsystems, the most relevant components are the sys-
tem call interface, process management, memory management, the virtual file
37
system, the network stack and the hardware depended code areas, comprised
mainly of the devices drivers and architecture dependent code [108]. A detailed
description of the different kernel functions is provided by the official Linux
documentation that is updated for every new kernel release [19]. To assess the
overall relevance of the different subsystems for the following attack surface
analysis, the tasks of the different architectural components that are visualized
in the graphic below, are described in this chapter for a high level abstraction.
38
4.2.1 System call interface
The System call interface is the part of the kernel that provides function calls
towards the user space process environment [18, p. 819]. As such it is the cen-
tral part of the Linux Kernel that is directly accessible from outside the kernel
memory space. While some architecture depended system calls are provided,
the majority of Linux system calls is an integral part of the core kernel code
and shared between different CPU architectures [109]. While the system calls
of the kernel define how the user space can interact with the kernel, the actual
functionality is provided by the underlying kernel subsystems. The system call
interface therefore provides the application program interface for the kernel.
The process management subsystem provides one of the most crucial core tasks
of an operating system [18, p. 35]. All functionality that regards the manage-
ment of processes is implemented in this part of the Linux kernel. Creating,
managing and stopping user space processes as well as kernel space threads is
controlled here. One of the more complicated kernel tasks involved the man-
agement of CPU execution time, also called scheduling, is implemented in this
subsystem and provides the different running processes with CPU resources.
Process signal management and the handling of interrupts is also handled by
this part of the kernel.
39
memory as well as the management of virtual memory, including paging and
swapping. In addition, this subsystem is responsible for the management of
the user address space. System call functionality such as provided through the
mmap system call are mainly implemented here.
The virtual file system is a significant abstraction for all file operations [18,
p. 519]. Instead of accessing different file system or even storage devices di-
rectly, this abstraction layer provides an API for file operations independent
of the used implementations. System calls related to file operations such as
open and close are implemented by this kernel subsystem, thereby providing a
standardized interface. Below the virtual file system interface are management
functions that implement the desired operations for the different file systems.
In combination with a common set of buffer operation functions, this code
manages the access to the device drivers itself that interact with the storage
hardware itself. A clear separation between this subsystem and other parts
of the kernel such as device drivers is difficult, just like a separation between
process and memory management towards the CPU architecture is not strictly
possible in all function areas.
The network stack is a crucial part of the Linux kernel that implement different
networking protocols [18, p. 733]. Following the layered architecture approach
implemented in common network protocols, the Network stack of the kernel
provided APIs for different network layers. If requested, the network stack can
provide access to raw network frames itself, although more commonly higher
level protocols like the internet protocol or TCP and UDP are used to commu-
nicate between data endpoints. Besides managing access to remote systems,
the socket layer of the network stack is also used for local inter process commu-
nication. Like the virtual file system of the kernel, the network stack has also
a close relation to the device driver subsystem regarding the communication
40
to network devices.
The device drivers and device are providing a significant part of the kernel
functionality by providing communication to the actual hardware devices [18,
p. 391]. Separated into different areas of device classes, this subsystem in-
cludes all required code to manage and use the supported hardware. The
subsystem provides another abstraction layer as well, by providing a unified
device model that can be addressed by other parts of the kernel such as the
virtual vile system or the network stack. Every device subsystem further has a
specific interface that is provided to allow easier communication with hardware
devices without the requirement to adjust the calling code for each specific de-
vice driver.
While most of the Linux kernel code is independent of the specific architecture
that is used to execute the code, some functions require architecture dependent
code [18, p. 1117]. One prominent reason is increased efficiency. Code sections
related to specific CPU architectures, may include additional functions that
provide support for tasks like process and memory management, allowing re-
source critical operations to be optimized for specific platforms. Additionally,
this kernel subsystem contains code that manages the boot process as well
as architecture specific initialization. It also handles various hardware related
tasks for execution on specific architectures, such as interfacing with interrupt
and BUS controllers, setup of exceptions and virtual memory handling.
41
4.3 Kernel Modules
The Linux kernel interacts with user space processes by providing system calls
that are implemented by the various kernel subsystems. These interfaces are
defined by the Linux kernel API and are considered fairly stable [110] as op-
posed to the binary kernel interface. However, there is another mechanic avail-
able that allows user space entities to influence the kernel space, resulting in a
changed implementation of the provided system calls. That mechanic is pro-
vided in the form of loadable kernel modules [18, p. 473]. Kernel modules allow
the extension of the running kernel to increase the available functionality with-
out requiring a reboot of the system or even a custom compiled kernel. Kernel
modules are an integral part of the Linux kernel and a significant amount of
the kernel code is implemented as kernel modules. Kernel modules can either
be compiled as part of the core kernel or as a loadable module. This allows to
keep the running kernel fairly small, while still providing the option to extend
the available kernel features during run time. Common use cases for kernel
modules are device drivers, file system and network protocol implementations,
as well as more specialized kernel functions. The concept of kernel modules
makes it possible to compile and run a very small version of the Linux kernel
that may be optimized for specific tasks only. This is often the case for em-
bedded systems that do not require a generic kernel [111]. Among the most
relevant use case for kernel modules are device drivers [112]. Since a lot of ker-
nel code is written to support specific hardware, the possibility to run only the
required driver code significantly reduces the size of a kernel that is running on
a specific hardware platform. Device drivers specifically are commonly loaded
automatically when a specific hardware device is attached and identified. As
part of different abstraction layer like the virtual file system, device drivers
also influence the implementation of specific systems calls that are used to
access hardware devices [112]. Other kernel modules are loaded on demand
by user space applications when they are required to fulfill a specific task like
the use of a specific network protocol. The Linux kernel also offers modules
implementing cryptographic algorithms for improved performance. Linux ker-
nel modules therefore present a significant part of the kernel architecture and
allow to extent and influence the running Linux kernel considerably.
42
4.4 Hardware dependent code
43
4.5 Source Code Structure
The Linux kernel source code is structured along its various subsystems as well
as major code functions that provide general features that are used by multiple
parts of the kernel. To allow a more detailed overview of the different kernel
features, the folder structure of the kernel source code is briefly explained in
this chapter.
Subfolder Implementation
arch Architecture related code,
low-level code for memory and process management,
hardware initialization and assembly routines
block block I/O layer and block devices management
certs certificate information for module signatures
crypto kernel crypto API
providing common cryptografic algorithms
Documentation Kernel source code documentation
drivers Kernel code of hardware device drivers
fs Virtual file system abstraction layer
code for various file system implementations
include kernel headers for include files to build the kernel
init Kernel initialization during the boot process
ipc Inter process communication channels
shared memory, pipes and signals
kernel Essential kernel functions
as well as the system call interface
lib Common function used by various parts of the kernel
mm Virtual memory management abstraction
and early boot memory management functions
net Network abstraction layer for
the high level network management
addressed by low-level network driver functions
.
44
Subfolder Implementation
samples Sample code for various kernel functions
scripts Support scripts for the build process of the kernel
security Linux Security Module framework
for optional access control policy modules
sound Sound subsystem including related driver code
tools Kernel development tools
including test modules for various components
usr Code for the root file system image initramfs
virt Kernel virtual machine hyper visor module
45
4.6 Kernel Attack Surface
The system call interfaces of the kernel seemingly expose very limited parts
of the kernel to the user space environment. However as the analysis of the
kernel architecture as shown, the implementation of the provided system call
functions is done by the different kernel subsystems. Additionally, these sub-
systems are tightly integrated with each other and have dependencies with
hardware dependent code, which makes a clear separation of kernel code that
are responsible for specific system calls difficult.
The attack surface of the kernel is defined by all possible ways to breach
security boundaries that are enforced by the kernel itself. That is mainly the
boundary between userspace and kernel space, but also the security boundaries
enforced by the access permissions that are implemented. That can include
the discretionary access control model that is used by default on Linux systems
or other types like mandatory access controls that can be enforced by kernel
extensions such as SELinux.
The Linux kernel is a monolithic system that executes all kernel code within
the same memory space. There do not exist any significant security boundaries
between different parts of the kernel code, which makes a separation of attack
surfaces even more difficult. Coding errors in kernel functions that are directly
or indirectly accessible for user space processes, provide a direct and complete
access to the entire kernel space. The resulting, indirect attack surface of the
kernel is composed of the entire kernel itself.
46
5 Related Research
A number of related research projects have approached to problem of assessing
the complex attack surface of large software systems like the Linux kernel
before. This chapter will describe some of the related works in this area and
their specific advantages and problems.
The reduction of the Linux kernel attack surface has already been subject to
previous research [24], that can be used as a baseline for further work in this
area. Measuring the general attack surface of software systems based on attack
opportunities has also been researched and helps to establish the previously
used methods for attack surfaces analysis [21][23]. To assess the outcome of
these different measurements, their results can be compared with previously
found vulnerabilities [114] to compare the theoretical assessments with real
world examples of security issues. Research regarding the potential of vulner-
abilities to be successfully exploited [88] can also provide additional incentive
to assess the criticality of exploitable coding errors and their associated risks.
These previous research projects have already provided a number of results
that can be used for attack surface assessment and reduction in the Linux
kernel. Compared to the available methodologies for attack surface analysis,
the complexity metric measurement used in this thesis will attempt to improve
the shortcomings of previous research approaches.
47
assumptions regarding the deployment environment and currently know at-
tack vectors, that make it difficult to measure an absolute metric. Promising
research regarding this kind of general security metric focus on the relative at-
tack surface, while comparing different versions of the same software [21]. This
allows a more relevant result due to the same code base that is compared with
each other, thus avoiding problems like the difficult comparability of programs
written in different programming languages.
The directly exposed areas of the kernel are accessed by using system calls.
The thereby provided kernel functions are one of the primary research objects
when it comes to assessing the kernel attack surface. The availability and ex-
posure of system calls to the user space stays relevant even when a process
does not actively use a specific system call. When the user space application
itself includes vulnerable code that may allow an adversary to execute code
in the context of the process, that attacker provided code can also access all
of the available system calls and thereby, large parts of the kernel. To miti-
gate this risk, recent software architectures have begun to include mechanisms
like system call sandboxing [115] and container technologies [116]. Related re-
search projects like [36] have shown significant difference in regards to the risk
associated with the availability of specific system calls, based on the provided
functionality. When it comes to attack surface reduction of software system,
the isolation of unused features and interfaces is a well established approach
[116][23]. In regards to the Linux kernel, the functionality of the available sys-
tem calls represents the most direct exposure of the kernel attack vectors, since
these are the only direct communication channels between the user and kernel
space. It is therefore crucial to investigate previous research regarding the evo-
lution and current state of Linux system calls [71]. An assessment of the risks
associated with specific system calls was already subject of previous research
[36] and can be used as a basis for a similar analysis on the available system
calls in the current Linux kernel. There has also been research regarding the
systematic reduction of system calls in specific server applications to reduce
the exposure of the kernel attack surface for specialized use cases [117] [30].
48
Applying these previous research methods and comparing their results to the
targeted research approach using complexity metrics, will provide a potential
correlation with the findings of this paper.
49
5.5 Quantified Attack Surface Reduction
One of the most relevant research projects in regards to the assessment and
reduction of Linux kernel attack surface is based on the active measurement of
used kernel functions [117]. This approach uses a modified version of the Linux
kernel to actively trace the code areas that are used during the execution of
specific user space software to automatically create a profile of utilized kernel
areas. Based on these results, a second modification to the Linux kernel denies
the execution of all function that have not been identified as required during
the analysis phase. The result is a version of the Linux kernel that reduces the
number of executable kernel code by a significant margin. Unlike previous at-
tack surface reductions implemented by container technology [116] or reduction
of available system calls [115], the approach implemented by [117] and [121]
has managed to reduce the executable code base. With the deployed changes
to the Linux kernel code including a very limited code additions, this measure
has managed to remove the usable kernel functions from the executable code
base. Based on these results, following research projects have managed to im-
prove the outcome by eliminating the requirement for additional kernel code
in the deployed kernel version altogether by using compile-time configuration
of the Linux kernel [122]. As the research based on this approach has shown,
a significant reduction of the compiled kernel function can be achived [123]
[24]. Hardening measures that reduce the kernel attack surface by removing
features have been used by other projects before [124], however the extent of
the reduction in kernel size is very limited for general purpose kernels. Other
approaches reduce the availability of kernel functions to limit their exposure
[125]. These previous kernel attack surface reduction strategies have focused
on hiding kernel ares from user space without reducing the actually executed
kernel in a significant way. The result of the measures researched by the listed
”Quantified Attack Surface Reduction” papers however, have managed to re-
duce the ”Trusted Computing Base” (TCB) of the overall system by excluding
source code in the compilation process of the kernel. As a result, new attack
vectors that may expose previously shielded kernel function cannot be used
against unavailable code ares that were removed at compile time. This ap-
proach is therefore significantly different then previous attempts to reduce the
50
accessibility of kernel function, without removing the actual kernel code. The
primary drawback of this approach to attack surface reduction is the signifi-
cant effort required to measure and maintain a profile of used kernel functions
for all systems and their different use cases. Maintaining and deploying a ker-
nel profile for major applications like web servers, may be an option for some
organizations, but still require a valid use care to justify the additional effort.
While the overall attack surface of the reduced kernel resulting from this re-
search approach is significant, the measurement of the exact exposure used in
this research still depends on exposure towards unprivileged user space. As has
been discussed before, measurements that depend on know exposure towards
specific parts of a system, can be unreliable due to changing environments [81].
51
5.6 Shortcomings of previous Research
The different research approaches to assess the attack surface of software like
the Linux kernel have shown different advantages and issues in regards to their
effects to measuring and improving the overall security of software systems.
This chapter will provide a summary of the discussed results from previously re-
search methodologies and provide an overview of the most relevant attributes.
Advantages:
• Easy Identification of attack vectors using data and control flow graphs
Disadvantages:
52
5.6.2 Measuring known Vulnerabilities
Advantages:
Disadvantages:
The reduction of available system calls from the perspective of user spaces pro-
cesses may provide a significant reduction in the exposure of directly accessible
kernel interfaces.
Advantages:
Disadvantages:
53
5.6.4 Compile-time Kernel Reduction
Advantages:
• Reproducible results
Disadvantages:
54
6 Approach Reasoning
In the previous chapters, different subjects regarding the analysis of software
attack surfaces were discussed. The core issues that have been identified,
revolve around the versatility of both analyzed system attributes like attack
vectors and exposure, as well as the difficulty to measure the identified security
attributes. Since attack vectors change during the software development life
cycle, its identification can only provide a temporary value. The dependence
of exposure measurement to the deployed environment, further prevents the
use of this attribute outside of very specific use case scenarios. A traditional
approach to attack surface analysis is therefore not feasible to assess the Linux
kernel in general. Instead, this research uses a number of measures listed
below, to improve the current state of attack surface measurement.
55
[25]. The existence of established tools and specifications [99] allow the identi-
fication of complexity within the Linux kernel source code. Previous research
on Linux kernel attack surface reduction has successfully used this metric [123].
However the question remains how different kernel features affect the result-
ing complexity. The following chapter will therefore present an approach to
identify the effects different kernel components have on the complexity and
thereby attack surface of the kernel.
56
6.2 Linux Kernel Modules
The Linux kernel provides a large number of optional features that are imple-
mented as kernel modules. This allows the flexible extension of the monolithic
Linux kernel. However, even a default kernel configuration does include a
large number of kernel modules [126] that are a common part of commodity
operating systems [127]. Generic Linux operating systems are therefore in-
cluding a number of optional features that provide common services. These
kernel modules are not necessarily loaded into kernel space by default during
the boot process, but may only become relevant when requested. As discussed
previously in subsection 4.3, this may be the case because a service has re-
quested a module to be loaded, or because a specific hardware component was
attached to the system during its operation. The code complexity included
in kernel modules therefore needs to be considered as part of the kernel com-
plexity itself, even if it may not be loaded by default. Loadable Linux kernel
modules come with a number of security implications aside the increases com-
plexity. A significant amount of kernel code can be loaded at request by user
space application [128]. This includes the implementation of complex protocols
that may be requested by an application. Allowing user space applications to
trigger the loading of additional kernel modules, makes it possible to exploit
vulnerable functions in rarely used kernel code, as has been demonstrated in
the past [129]. While it is possible to restrict the available range of loadable
kernel modules or disable this option completely, it would imply significant
drawbacks. Since user space applications may rely on specific kernel functions
to be available for legitimate purposes, the restriction may break the expected
compatibility [130]. As with attack vectors that are dependent on environ-
mental exposure, different kernel modules may not be directly loadable by
unprivileged user space entities. However, just like other parts of the kernel
code, they are a part of the general attack surface of the Linux kernel. It
may be possible for system engineers to reduce the number of available kernel
modules in controlled environments, like specific organizations or embedded
systems. However this requires a known set of use cases the targeted system
will be used for and reduces the usefulness of this measure. Finding kernel
modules that are significant in regards to their attack surface by measuring
57
the included code complexity, may help make design decisions for environ-
ments where a restriction may be a valid option however. Consequently, the
following complexity analysis will target a number of common kernel modules
to measure the additional complexity they add to the system. For this research
project, the targeted kernel modules will include the SELinux, AMDGPU
and KVM module. Additionally, the file systems ext4, xfs and btrfs are
scanned to further highlight the influence from file system implementations.
Since previous vulnerabilities were related to Linux name spaces, additional
analysis will target the name space modules. The kernel modules targeted
in the following analysis were selected in regards to their relevance in common
Linux systems, but are primarily intended to provide only an example for a
general procedure to measure attack surface in kernel modules. In the follow-
ing chapter, the approach used to analyze these modules will be described in
detail to allow further research to target arbitrary kernel modules for future
analysis.
The complexity analysis of specific kernel modules will provide a basis for risk
assessments that may determine their use in future system designs. However,
large parts of the kernel complexity are not expected to be included in spe-
cific kernel features, but may rather be in more general kernel subsystems like
hardware dependent code sections. Due to this, the following analysis will also
take a close look at the general distribution of kernel complexity within the
source code tree. Aside from kernel modules that serve a specific purpose, the
hardware related kernel code is another reasonable target for analysis. Since
only a very specific hardware platform is used to run a deployed Linux kernel,
it may be possible to reduce the available complexity significantly by remov-
ing significant parts of hardware related kernel code without impacting the
functionality. Based on the resulting complexity graph related to architecture
and driver related kernel code, new approaches to reduce the kernel attack
surface may be found that allow a reasonable and practicable improvement of
the deployed kernel security.
58
7 Testing Process
The Linux sources include extensive support for the creation of custom kernel
configurations that can be used to compile a specific kernel. A number of avail-
able make scripts allow the automatic creation of kernel build configuration
files [131]. In Linux 5.10 there are 12244 different kernel configuration options
available [132]. As a result, the kernel binaries generated by these options can
differ significantly in regards to implemented features and complexity. To mea-
sure the complexity impact of the targeted kernel modules and subsystems, a
number of build configuration files will be used to compile different versions of
the Linux kernel while measuring the included complexity using a static code
analysis tool. The following chapter will provide a detailed description of the
used tools, configurations and testing process.
7.1 Sonarqube
While there are a number of different static code analysis tools available to
measure code complexity, the exact results can differ depending on the imple-
mented specification. To provide a comparable and transparent result based
on documented metric specification [99], the public instance of SonarQube
(sonarcloud.io) will be used for the analysis process. The SonarQube toolkit
consists of a build wrapper and a scanner binary [133]. The build wrapper
is used to scan the C code during the build process and document the use
of source files, like include instructions and macros [134]. This step does not
change the resulting binaries and has therefore no effect on the accuracy of the
measurement. Based on the resulting source code information the SonarQube
scanner will scan the code and create a report about the source code attributes,
that is uploaded to the public Sonarcloud analysis tool. The code measure-
ment itself will take place within a local test environment, while the resulting
report will be analyzed on the Sonarcloud instance, where the results will be
displayed afterwards. While the SonarQube tool provides a static code anal-
ysis that includes a number of code attributes, the cyclomatic and cognitive
complexity will be included. The results presented by Sonarcloud show the
complexity attributes included in the different source code directories, which
allows additional insight into the complexity distribution.
59
7.2 Test Parameter and Environment
60
• xfs - high-performance journaling file system
The analysis of the listed features requires a modified kernel build con-
figuration that may require multiple depending option to be enabled. The
detailed list of tested kernel build configuration options have therefore been
documented [132]. In addition to the test execution of the listed modules, ad-
ditional tests were executed with a reduced source code to provide additional
indicators for the influence of hardware dependent code. For this purpose,
specified folders were removed after the successful compilation, but before the
static code analysis.
The ”nohw” analysis results were measured with the following kernel source
tree folders removed:
• arch
• driver
• fs
61
7.3 Test Execution Process
The test execution process described here, provides a step by step instruction
to reproduce the results of the research. The following instructions were used
with the different documented kernel build configuration [132] to produce the
results presented in the following chapter subsection 8.1.
tar xf linux-5.10.5.tar
cd linux-5.10.5/
make defconfig # alternatively allnoconfig or allyesconfig
make menuconfig # for modification of included kernel features
build-wrapper-linux-x86-64 --out-dir bw-output make -j 12
export SONAR_TOKEN=*** ## *** = sonarcloud authorization key
sonar-scanner -Dsonar.organization=linuxtest2020
-Dsonar.projectKey=linux-5.10.5-defconfig -Dsonar.sources=.
-Dsonar.cfamily.build-wrapper-output=bw-output
-Dsonar.host.url=https://sonarcloud.io -Dsonar.cfamily.threads=12
62
8 Results and Interpretation
The results from the conducted static code analysis have provided a number of
complexity metrics associated with different kernel features and subsystems. A
detailed overview of all analyzed kernel configurations can be found here [135],
while the next chapters will provide an overview of the complexity metrics as
well as an interpretation of their relevance.
63
8.2 Complexity Difference
64
8.3 Interpretation
While the complexity comparison with the default ”allnoconfig” kernel shows
the most significant differences, it needs to be noted, that this minimal kernel
build does not include the required code to be actually deployed. It can how-
ever serve as an additional measurement to compare with a deployable kernel
provide by the ”defconfig” build. Similar, the ”allyesconfig” build provides
a similar comparison in regards to the other extreme, by including all possi-
ble kernel features. Since the ”defconfig” builds reflect a practical kernel that
is included in many Linux based operating systems, the primary assessment
regarding the changes in attack surface will target the results from this base
configuration. The full kernel build configurations can be found here [132].
65
8.4 Impact of optional Kernel Modules
The inclusion or exclusion of the targeted optional kernel modules have re-
sulted in complexity changes of different magnitudes as shown in the graph
below. As the inclusion of optional kernel modules like these may not neces-
sarily depend on a required use case, the additional complexity may serve as
an additional factor to decide on the inclusion of these features.
66
8.4.1 SELINUX
Based on the default kernel configuration, the SELinux kernel module impacts
the systems complexity by only 1.15%, compared to the unmodified kernel.
Given the cyclomatic complexity reduction of only -4034 when the module is
removed from the default build, the impact on the available attack surface is
very limited. The significant complexity difference of +39210 or 65.2% by the
SELinux module inclusion into the minimal kernel build, mainly results from
the requirement of additional kernel features such as the network stack. The
complexity difference of -4151 resulting from the SELinux exclusion from the
full feature kernel build (allyesconfig), confirms the very limited impact on the
overall system complexity from the SELinux module. Based on the complexity
impact of SELinux, the additional attack surface is therefore insignificant for
most systems that already require common kernel subsystems like the network
stack. Additionaly, the SELinux module can provide a number of optional
use cases that may become a requirement during the systems life time, which
further reduced the value of its removal.
8.4.2 AMDGPU
The AMDGPU module increases the cyclomatic complexity of the default ker-
nel by +56562 or 16.09%, thereby indicating a significantly increase compared
to the unmodified kernel. The complexity difference of +74522 measured for
the minimal kernel build, includes only an addition of 17960 points, resulting
from additional requirements. The exclusion of the AMDGPU module from
the full feature build confirms the result from the default kernel, while mi-
nor differences are still present due to additional feature requirements for the
AMDGPU module inclusion into the default kernel. For use cases where the
AMDGPU module is not strictly required, the significant complexity resulting
from its inclusion can therefore be considered. Given that the relevance of
AMDGPU is bound to the availability of the related hardware devices, the
presented results are mainly significant for decisions regarding the systems
hardware design.
67
8.4.3 KVM
68
8.5 Impact of File Systems
To calculate the difference between the analyzed file systems, the resulting
complexity values were compared to a kernel build that only includes general
file system code that is unrelated to specific implementations. The complexity
listed as ”fs helper” is a result of a unmodified kernel configuration, measures
without specific file system source code. The complexity added by the im-
plementation of specific file systems show significant differences between the
analyzed implementations that are described in detail within the following
chapters.
8.5.1 ext4
8.5.2 xfs
The xfs file system increases the default kernel build by +15967 or +4.91% and
is thereby almost twice as complex as the ext4 implementation. The additional
complexity added to the minimal kernel build amounts to +22104 or +36.77%
as a result of additional dependencies that are also required by other analyzed
file system implementations. The significant increase in complexity compared
to the ext4 implementation may be justified if the intended system use cases
requires file system features not provided by ext4. In cases where this is not the
case, the complexity difference may provide a valid argument to exclude the
xfs filesystem from the kernel configuration to reduce potential attack surface.
69
8.5.3 btrfs
8.5.4 namespaces
70
8.6 Impact of Hardware Dependent Subsystems
In order to identify hardware related complexity within the Linux kernel, the
analyzed kernel builds were additionally measured while excluding affected
code sections. To indicate the overall effect of hardware related kernel code,
the analysis was executed on the compiled source code after removing the
source folders arch, driver and fs. The ”reduced” kernel complexity results
indicate a significant amount of kernel code is related to the use of specific
hardware.
The complexity impact related to the exclusion of the ”arch” kernel source
code, indicates a significant relevance to the overall system. With a cyclomatic
complexity reduction of -73076 or 20.79% the ISA implementation provides a
major part of the kernel complexity. In the case of the default kernel build, the
affected code reflect the x86 implementation in particular. While the specific
complexity may vary between different ISA implementations, a comparison
between these architectures is out of the scope of this research. Given that the
x86 architecture is one of the most commonly used instruction sets, a reduction
of architecture dependent code does not seem feasible. Future research may
71
however find that choosing hardware platform providing a different instruction
set, provides further options to reduce the resulting complexity.
Significant amounts of the kernel complexity are located within source code sec-
tions such as ”arch”, ”driver” and ”fs”. These areas contain mostly hardware
related code that is only relevant to a specific hardware platform. Therefore,
large parts of the included complexity will never be relevant on a deployed
system. The measured complexity differences in the ”arch” folder are a direct
result of code required by the x86 instruction set included in the default ker-
nel build configuration. As previously concluded, the specific complexity may
vary between ISA implementations. One specific implementation will however
be required by any operating system kernel and can therefor not be excluded
from a deployable kernel build. In regards to the complexity added by the
file system implementations, a reduction may be feasible depending on the
required use cases. At least one specific file system will however be required as
well, which will still include a significant amount of complexity. The majority
of hardware platform dependent complexity is however related to the source
code of the available drivers. For the default kernel build, the exclusion of
the driver code reduces the cyclomatic complexity by -126345 or -35.94% and
has therefore a significant impact on the overall kernel complexity. Since the
Linux kernel includes a large amount of code related to a significant number
of device drivers, while only a small subset of these drivers is required for a
deployment, a significant reduction of driver related complexity may be feasi-
ble. The inclusion of many device drivers by the default kernel build is related
to the possible requirement of common hardware drivers such as USB devices.
To reduce the extent of supported hardware devices, a throughout analysis of
required use case may therefore be necessary.
72
9 Conclusion
The conducted complexity analysis of the Linux kernel has provided a number
of results that allow a more detailed assessment of the included attack surface.
The described test process allows the complexity measurement of arbitrary
kernel features and can be used as a template for future assessments to make
more qualified decisions regarding the inclusion of kernel functions and their
associated risks. The complexity analysis of the targeted kernel features pro-
vide practical examples of attack surface analysis based on complexity metrics.
The presented overview of the cyclomatic complexity distribution within differ-
ent parts of the Linux kernel source code, additionally indicates the influence
different kernel subsystems have on the overall system attack surface. Besides
the assessment of kernel related code complexity that was presented, the basis
for new approaches to reduce the attack surface of Linux kernel deployments
has been provided by this research.
73
environments can exclude large amounts of hardware related code, such as
drivers for unavailable hardware platforms. Due to the central management in
these environments, additional kernel complexity reductions, such as the use
of specific file system implementations are feasible. Organizations that deploy
large amounts of Linux based systems, may therefore improve the security of
their systems by reducing the attack surface using the described approach.
While a number of research approaches have targeted the analysis of the Linux
kernel attack surface, the approach used by [121] is most closely related to the
analysis presented in this paper. The major differences can be found with
the use of common static code analysis tools for complexity measurement as
well as the wider applicability of the results. While [121] have provided a
74
more detailed and exact analysis and reduction of required kernel functions, it
required a throughout analysis of a specific use case and needs to be conducted
for every deployed application. This may be a feasible approach for high risk
environments that provide the resources needed to use this approach. The
results presented in this research however, while less effective, are able to
provide a significant attack surface reduction with minimal efforts due to the
more general applicability.
10 Further research
The presented research results provide a template for kernel attack surface
analysis based on complexity metrics. This allows practical security improve-
ments for specific use cases described in the previous chapter. To provide a
more detailed analysis of the required efforts and security improvements, addi-
tional research could analyze the described reduced complexity deployments in
practice to verify the presented finding. Besides the reduction of kernel com-
plexity, additional research may target effective kernel exposure reduction to
further increase the effort required to attack Linux based applications. Promis-
ing technologies that have been established are userspace sandboxing [141] and
containerization technologies [116].
75
References
[1] S. A. Christoph Krösmann, “Markt für IT-Sicherheit auf Allzei-
thoch.” https://www.bitkom.org/Presse/Presseinformation/
Markt-fuer-IT-Sicherheit-auf-Allzeithoch, Online; accessed
31-March-2021.
[2] BSI, “The state of it security in germany in 2019,” tech. rep., Federal
Office for Information Security, Bonn, Germany, 10 2019.
[10] P. R. Marie Baezner, “Stuxnet,” tech. rep., Center for Security Studies
(CSS), ETH Zürich, 10 2017.
[11] S.-C. Hsiao and D.-Y. Kao, “The static analysis of wannacry ran-
somware,” pp. 153–158, 02 2018.
76
[13] J. Aidan, H. Verma, and L. Awasthi, “Comprehensive survey on petya
ransomware attack,” pp. 122–125, 12 2017.
[23] P. Manadhata and J. Wing, “An attack surface metric,” Software Engi-
neering, IEEE Transactions on, vol. 37, pp. 371–386, 05 2011.
77
[25] M. Z. Mamdouh Alenezi, “On the relationship between software com-
plexity and security,” 2020.
[34] “A very deep dive into ios exploit chains found in the
wild.” https://googleprojectzero.blogspot.com/2019/08/
a-very-deep-dive-into-ios-exploit.html, year = Online; ac-
cessed 31-March-2021.
78
[36] M. Bernaschi, E. Gabrielli, and L. Mancini, “Remus: A security-
enhanced operating system,” ACM Trans. Inf. Syst. Secur., vol. 5,
pp. 36–61, 02 2002.
79
[49] G. McGraw, “Software security,” IEEE Security & Privacy, vol. 2, no. 2,
pp. 80–83, 2004.
[57] J. Hong, “The state of phishing attacks,” Commun. ACM, vol. 55,
pp. 74–81, 01 2012.
[58] S. Wiefling, M. Dürmuth, and L. Lo Iacono, “More than just good pass-
words? a study on usability and security perceptions of risk-based au-
thentication,” vol. abs/2010.00339, 10 2020.
80
[60] S. Kamara, S. Fahmy, E. Schultz, F. Kerschbaum, and M. Frantzen,
“Analysis of vulnerabilities in internet firewalls,” Computers & Security,
vol. 22, no. 3, pp. 214–232, 2003.
[64] A. Shostack, Threat modeling: Designing for security. John Wiley &
Sons, 2014.
[70] K. Shah and K. Patel, “Security against fork bomb attack in linux based
systems,” International Journal of Research in Advent Technology, vol. 7,
pp. 125–128, 04 2019.
81
[72] S. Z. Syed Idrus, E. Cherrier, C. Rosenberger, and J.-J. Schwartzma nn,
“A review on authentication methods,” Australian Journal of Basic and
Applied Sciences, vol. 7, pp. 95–107, 06 / 2013.
[79] H. Chen, J.-H. Cho, and S. Xu, “Quantifying the security effectiveness
of firewalls and dmzs,” pp. 1–11, 04 2018.
82
[83] Z. Baig and S. Zeadally, “Cyber-security risk assessment framework
for critical infrastructures,” Intelligent Automation and Soft Computing,
pp. –1, 01 2018.
[85] P. Mell and T. Grance, “Use of the common vulnerabilities and exposures
(cve) vulnerability naming scheme,” p. 6, 09 2002.
[86] R. Wolthuis and F. Phillipson, Quantifying Cyber security Risks, pp. 20–
26. 08 2019.
[91] D. Huang, H. Cui, S. Wen, and C. Huang, “Security analysis and threats
detection techniques on docker container,” pp. 1214–1220, 12 2019.
[94] J. A. Wang, H. Wang, M. Guo, and M. Xia, “Security metrics for soft-
ware systems,” in Proceedings of the 47th Annual Southeast Regional
Conference, pp. 1–6, 2009.
83
[95] R. Anderson, “Why cryptosystems fail,” in Proceedings of the 1st ACM
Conference on Computer and Communications Security, pp. 215–227,
1993.
[103] Y. Shin and L. Williams, “Is complexity really the enemy of software
security?,” in Proceedings of the 4th ACM workshop on Quality of pro-
tection, pp. 47–50, 2008.
84
[105] D. G. Feitelson, “Perpetual development: a model of the linux kernel
life cycle,” Journal of Systems and Software, vol. 85, no. 4, pp. 859–875,
2012.
[111] M. Kraeling and A. McKay, Linux for Embedded Systems, pp. 921–959.
12 2013.
85
[116] A. Grattafiori, “Understanding and hardening linux containers,” tech.
rep., NCC Group, Manchester, United Kingdom, 06 2016.
[119] P. Louridas, “Static code analysis,” IEEE Software, vol. 23, no. 4, pp. 58–
61, 2006.
86
[126] “Linux default kernel config v5.10.5.” https://github.com/
linuxTest2020/kernelConfigurations/blob/main/defconfig,
year = Online; accessed 31-March-2021.
87
[138] R. Scroggins, “Emerging virtualization technology,” Global Journal of
Computer Science and Technology, pp. 11–16, 08 2017.
88
View publication stats