ENPM809V
Introduction to Kernel Security
Agenda
- Introduction to the Linux Kernel
- Building a Kernel Module
- Character Devices
- Basic Privilege Escalation
The Kernel

What is the Kenrel?
- Code in the operating system that interfaces between hardware and higher-level applications.
- The Linux kernel is a free-open source operating system in Linux Distributions
- Modular, monolithic, multitasking, Unix-Like
Application
Application
Application
Application
System Call Interface/Interrupt Handling
Kernel Subsystem
Device Drivers
Application
Application
Application
Application
System Call Interface/Interrupt Handling
Kernel Subsystem
Device Drivers

x86 Protection Rings
- An protection mechanism in x86_64 CPUs to prevent unauthorized access to the kernel.
- 3 protection rings (but mostly use level 0 and 3)
- Level 0 = Kernel and Drivers
- Level 3 = Applications
x86 Protection Rings
- At ring 3, the CPU can
- Use most x86 instructions
- Access unprivileged memory
- At ring 0, the CPU can
- Do almost everything at ring 3
- Access Privileged memory
- Use Special instructions

Switching Protection Rings
- Userspace programs can ask the kernel to execute something through a few vectors:
- System calls - occurs by calls directly from userspace applications
- Interrupts - occurs indirectly through the use of instruction that cause exceptional conditions
System Calls
- A userspace program executes the syscall instruction
- How does this happen?
- The address of the instruction following the syscall is placed in to RXC
- RIP is now the Kernel's System call handler
- Provided by the OS at boot time
- Generally stored in the LSTAR register on x86 machines
- Ring level is set to 0 (CPL)
- After the kernel finishes, RIP is set to whatever is in RCX, transferred back to ring 3
Privileged Instructions
- Ring 0 Code has access to privileged instructions
- Reacts to how the system reacts to interrupts/exceptions
- LIDT - Load Interrupt Descriptor Table Register
- LLDT - Load Local Descriptor Table
- LGDT - Load Global Descriptor Table Register
- LTR - Load Task Register
- Reading/Writing Mahcine-specific registers
- RDMSR, WRMSR
- Virtual machine opcodes
- VMCALL, VMLAUNCH, VMRESUME, VMXON, VMXOFF
- Others too...
- Reacts to how the system reacts to interrupts/exceptions
Working with Linux Kernel
What do you need?
- Linux Kernel Source
- Obtainable from apt
- Get it from kernel.org
- Elixir - https://elixir.bootlin.com - Helpful source lookup
Compiling the Kernel
- Why would one want to compile the kernel themselves?
- Enabling debugging Features
- Add functionality
- Change Functionality
- Building for a new architecture
- The virtual machine has a customized kernel
- We will compile it once, but not more than that because it takes. a long time to do
Creating our debugging Environment
- We will be spending some time creating our debugging environment
- We will be using VMWare Workstation/Fusion to do this
- We will create a virtual serial port to communicate over
- We will also use dmesg for print statements (quickest way to debug)
- You might need to continue to this at home
- Alternatively, you can use pwn.college in practice mode
Creating our debugging Environment
- The quick and dirty way of doing it - just use dmesg and printf
- The not-so-quick way - compiling a kernel and enabling kernel gdb
- https://phoenixnap.com/kb/build-linux-kernel
Kernel Modules
- The primary way for extending kernel functionality
- Allows for various different functionality within the Linux kernel
- Support a new filesystem
- Implement a device/driver
- Implement a new protocol
- New Scheduling algorithm
Kernel Modules
#include <linux/module.h>
static int __init start(void)
{
printk(KERN_INFO "Hello World!\n");
return 0;
}
static void __exit mod_stop(void)
{
printk(KERN_INFO "Goodbye World\n");
return;
}
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michael Wittner");
MODULE_DESCRIPTION("Simple Demo.");
module_init(start);
module_exit(mod_stop); Defines which functions called on load/removal of a kernel module
Macros for licensing and defining init and exit
Where can you find printk messages?
Kernel Modules
# Basic Makefile for Kernel Modules - Kernel module with one C file
obj-m := example.o # Your C file should match the H file
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
# Inserting kernel modules
insmod example.ko optparam1="param" optparam2=2
#Removing modules
rmmod
#If on pwn.college practice mode, do this instead
vm build /path/to/.c/file
vm start
vm connect
#Look at vm --help and vm <command> --help for more detailsWhen a kernel module is inserted...
- The system call sys_init_module is invoked
- The code is copied into memory
- The license is checked
- The symbols used by the module are checked in the kernel symbol table (and resolved if found)
- That symbol must be exported, can find it in /proc/kallsyms
- The module's init function is invoked
Note: to export symbols, use macro EXPORT_SYMBOL
How do I lookup API Calls
- https://elixir.bootlin.com
- Clone the repository - https://github.com/torvalds/linux
- Checkout the tag of the version you are working with.
- E.g. if working with kernel version 5.4 -
git checkout tags/v5.4 - Grep for the API call you are looking for
- Google/ChatGPT
Time to build your own Kernel Module!
Classwork
- You are going to build your own Hello World kernel module!
- Source code is provided to you via pwn.college
- Use either your own environment or the pwn.college environment to build a hello world kernel module.
Kernel Data Structures
Many Many Structures
- Structures contain data for the majority of kernel data
- Tasks
- Kthreads
- Audit
- Files
Many Many Structures
- Tend to be generalized so that it can be applied anywhere without sacrificing performance
- Linked lists - /include/linux/list.h
- Queues - /include/linux/kfifo.h
- Hash maps - /include/linux/hashtable.h
- Radix trees - /include/linux/generic-radix-tree.h
- RB trees - /include/linux/rbtree.h
Slightly Different Than Traditional Datastructures
DATA
Prev
Next
DATA
Prev
Next
DATA
Prev
Next
Slightly Different Than Traditional Datastructures
DATA
Prev
Next
DATA
Prev
Next
typedef struct list_head
{
struct list_head *prev;
struct list_head *next;
};
struct some_other_struct
{
char *data1;
int data2;
struct list_head *head;
}https://www.oreilly.com/library/view/linux-device-drivers/0596000081/ch10s05.html
Embedding Structures
typedef struct example_struct
{
struct example_struct *prev;
struct example_struct *next;
};
struct some_other_struct
{
char *data1;
int data2;
struct example_struct *head;
};- Embedding structures is quite common in the Linux Kernel
- task -> file
- task -> audit
- task -> another task
- Structures can also be randomized
- Security Feature __randomize_layout
Struture Randomization
typedef struct example_struct
{
struct example_struct *prev;
struct example_struct *next;
};
struct some_other_struct
{
char *data1;
int data2;
struct example_struct *head;
} __randomize_struct;- Many structures are randomized at compile time
- Difficult to attack based on offset
- This is where macros come in
offsetof()
- Finds the offset of a member given a structure type
- This is defined as a standard part of C
#define offsetof(a,b) ((int)(&(((a*)(0))->b)))container_of
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) ); })
- Built-in Macro to determine who the parent structure is
- Takes a pointer to the member (child) structure
- Subtracts the pointer of the member to the offset it is located in the parent definition.
- End result = address to parent
task_struct
- The task_struct is used to manage tasks
- A task is the kernel's way of managing processes/execution context
- Contains MANY data fields ranging from memory, CPU Usage, or other data structures (such as the audit_context)
- Also contains information like UID and EUID
- Access the task_struct of the currently running process by using the macro - current
- Located in /include/linux/shed.h
Kernel Threads
What are they?
- Kernel threads are tasks. As such they run in their own context
- API can be found in /include/linux/kthread.h
- Has functions like kthread_create
- Kernel threads can only be created by other kernel threads
- We can track kernel threads through the task_struct
- Can you figure out how/why?
API Calls
- kthread_create - creates a new kernel thread
- wake_up_process - start a kernel thread (or other task)
- do_exit - terminate a kernel thread
- kthread_stop - Flag the kernel thread that it should stop
- It will wake up a sleeping kthread if necessary to set the flag
- kthread_should_stop - check to see if the kernel thread should stop
- allow_signal - indicates that the particular kthread can recieve the indicated signal
- set_current_state - sets the state (TASK_INTERRUPTABLE) makes it interruptable
- schedule/ssleep - give up the CPU
Synchronization
What the Kernel Provides
- Wait Queues - FIFO based on sleep
- Completionn Variables - Sleep until a certain condition is met
- Spinlocks - Very similar to POSIX Spinlocks
- If you don't know what it is man pthread_spin_lock
- Semaphores - Similar to POSIX Semaphores
- man sem_overview
- Atomic Operations
- Mutexes - Similar to POSIX Mutexes
What the Kernel Provides
- Wait Queues - /include/linux/wait.h
- Completion Variables - include/linux/completion
- Spinlocks - /include/linux/spinlock.h
- Semaphores - /include/linux/semaphore.h
- Atomic Operations
- /include/linux/types.h (for types)
- /include/asm-generic/atomic-instrumented (operations)
- Mutexes - /include/linux/mutex.h
- We are not going to go over these in depth, you need to do your homework on this.
Devices
What is it?
- They are just files on the filesystem...like any other file
- But have different properties
- File operations are implemented by the kernel module implemented
What is it?
- The kernel decides what read/write/open means
- Each device has it implemented differently
- Each device is defined by a major/minor number
- ls -l /dev, cat /proc/devices
- C for character devices
- B for block devices
- Inside the kernel, major/minor uses dev_t, an unsigned 32 bit number
- 12 bits for major
- 20 bits for minor
- Helper macros for assignment
We will focus primarily on character devices today.
Creating the Device
- register_chrdev_region
- registers a set number range with the kernel
- Starting at (maj/min) and requesting a given number of devices
- alloc_chrdev_region
- Request for a free region in the kernel
- Starting at (maj/min) and requesting a given number of devices
- Mknod - Creates the character device file in userspace
- sudo mknod ./dev c <maj> <min>
- Also a system call
What happens when we call mknod
- We create an inode in the VFS. (What's an Inode)?
- Contains a dev_t to specify the device associated
- i_rdev
- For character devices - contains a struct c_dev
- i_cdev
- Contain a pointer to the file_operations associated
- i_fops
- Contains a dev_t to specify the device associated
Character Devices
- A type of device that operates character by character
- Unlike block devices, which work with multiple characters at a time
- Information about it in the kerenl is contained in a cdev
- Has pointer to its owner (struct module)
- a dev_t field
- and a file_operations structure as a field
- Allocate it with cdev_alloc
- Free with kfree
- Can be embedded within another structure, but needs to be initialized with cdev_init
- Register the device with cdev_add
File Operations
struct file_operations my_fops =
{
.owner = THIS_MODULE,
.read = read_func,
.write = write_func,
.open = open_func,
.ioctl = ioctl_func,
.release = release_func,
};File Operations
- A structure of function pointers, which will be the operations for interacting with the device
- Implementation dependent, all based on how the developer wants the behavior to occur
- Common file operations implemented include the ones listed above and close (try to find file_operations struct on Elixir)
File Structure
-
struct fileis a kernel structure associated with an open file.- Goes away when all references are closed
- Can be found in the processes
struct file_structs- Found in
current->files->fd_array[]
- Found in
- Contains references to its inode, file operations, mode, and more.
- Why is this important?
- All file operations take this structure as a parameter. Why?
- So they know what file they are operating on.
Character Devices
- A kernel I/O method that uses a stream of data
- All operations (reading, writing, etc.) are performed on a per-byte/character basis.
- Accessed through the Linux FS (/dev/ttyXX)
- Acts like a file (have to implement open, read, write for interaction)
Block Device
- Similar to a character device, but performs operations on chunks of data
- Typically powers of two (128, 256, etc.)
- Linux allows block devices to be accessed as a stream of bytes by applications; thus, very similar to character devices
- The kernel interface must be a full block
- Accessed through /dev (e.g. /dev/sda)
- Examples: Disk drive
Network Devices
- Not accessible by the file system - provides interfaces to various networks instead
- Facilitates the transmission and reception of data packets
- Implement a backend for kernel requests for sending and receiving data
Your First Privilege Escalation
Goal
- We want to execute a root shell
- We are given the ability to load any arbitrary kernel module
- We have no vulnerability to exploit
What we need to do?
- We need to elevate a userspace process to root
- We need to ensure that when we call /bin/bash, we are root
- We need to create a kernel module that any userspace process elevate themselves to root.
How do we do this?

API to Change cred in the Kenrel
-
prepare_kernel_cred(struct task_struct *daemon)- Prepare a set of credentials for a kernel service- If we make daemon NULL - it will create root credentials
-
commit_creds(struct cred *creds)- set the credentials for the particular process
What if we combine these?
commit_creds(prepare_kernel_creds(NULL));
Homework - Part 1
You are going to create a character device and interact with it.-
On pwn.college I added a character device challenge in Kernel Internals.Follow the directions in the README and template.Get the flag and submit your code!
Homework - Part 2
Second homework contains a kernel driver loaded with privilege escalation.-
You are going to figure out how to utilize it to elevate your process to root!Then read the flag
Introduction to Kernel Security
By Ragnar Security
Introduction to Kernel Security
- 177