ENPM809V
Kernel System Hijacking
What We Will Cover
- Shared IRQs
- Notifiers
- Tracers
Announcements
Announcements
- Next week we may have pre-recorded lecture again due to work conflict.
- Will confirm by Wednesday
- The rest of the lectures will be in-person
Overview
Events
- Events give us information about what is happening in the kernel
- Subsystem communicates through this mechanism (and shared memory)
- Two primary methods:
- Shared IRQs
- Notification events
Tracers
- Designed to figure out what is happening in the kernel
- Useful for: debugging, Vulnerability Research, Hooking functions, finding out how things work
- Multiple technologies including: ftrace, kprobes, ebpf
What they have in common?
- Can execute certain behavior based on something that happens
Shared IRQs
Shared IRQs
- Remember
request_irq
?- It has multiple flags including
IRQF_SHARED.
- It has multiple flags including
- This allows a particular interrupt to be handled by multiple handlers/devices.
- What do you think we can do with this?
Shared IRQs
- Iterates through all the handlers with that IRQ
- The handler passes it's own
dev_id
(based on what it was registered with atrequest_irq
)
- The handler passes it's own
- If the handler is going to service the interrupt, it should return
IRQ_HANDLED
.- Otherwise it should return
IRQ_NONE
- Otherwise it should return
Shared IRQs
- Iterates through all the handlers with that IRQ
- The handler passes it's own
dev_id
(based on what it was registered with atrequest_irq
)
- The handler passes it's own
- If the handler is going to service the interrupt, it should return
IRQ_HANDLED
.- Otherwise it should return
IRQ_NONE
- Otherwise it should return
Why is this important
- IRQ Snooping!
- If you find a handler that is registered as a shared handler for a given IRQ you can register your own handler for the same IRQ
- Caveat: Return IRQ_NONE to not intefere with the other shared handler
Notifiers
Notifier Chains
- An publish-subscribe model for event notifications
- A callback with another device, module, etc. that is invoked based on an event triggered
- Multiple implementations of notifier chains (each behaving differently based callback execution context)
Notifier Chains Type
- Atomic
- Blocking
- SRCU - Sleepable Read-Copy-Update - Think blocking with different tradoffs
- Raw - Managing your own locking/blocking
- The API is defined
include/linux/notifier.h
Notification Callbacks
struct notifier_block
{
notifier_fn_t notifier_call;
struct notifier_block __rcu *next; /* What does RCU mean? */
int priority;
};
typedef int (*notifier_fn_t)(struct notifier_block *nb,
unsigned long action, void *data);
Notification Callbacks
- Important things to note:
- When events are generated - subsystem calls for
notifier_call_chain
- Call to event registration are requests to add the
notifier_call
function to a list ofnotifier_call
s
- When events are generated - subsystem calls for
Tracing
Tracing Mechansims
- Linux Kernel provides us with a few ways to trace
- Kprobes/Uprobes
- Tracepoints
- Ftrace
- eBPF - Extended Berkley Packet Filter
- Each has it's own benefits and drawbacks
Why Does This Exist?
- Debugging and analyze the Linux kernel
- Performance analysis
- Finding bugs
- Targeted logs and messaging
How are we going to use it?
- Bug hunting
- Figuring out how things work
- Hook/inject our code/shellcode into the kernel
- Learning tool for our own hooking mechanism
Kprobes
- An API that allows developers to trace via hooking into the kernel
- Uses hardware breakpoints to dynamically insert code
- Inserts breakpoints on a specified instruction address
- When the system hits the breakpoint, th eexception handler calls the function defined by the user of Kprobe
Basics of Kprobes
- Creating a kprobe - register a
struct kprobe
with the kprobe system- This is generally set by the
register_kprobe
function
- This is generally set by the
- The User Must specify
- symbol_name - the function to hook
- pre_handler - any code that should be run before executing the function
- post_handler - any code to be run after the function is finished executing
- handler_fault - code to run when a fault occurs
Basics of Kprobes
- Creating a kprobe - register a
struct kprobe
with the kprobe system- This is generally set by the
register_kprobe
function
- This is generally set by the
- The User Must specify
- symbol_name - the function to hook
- pre_handler - any code that should be run before executing the function
- post_handler - any code to be run after the function is finished executing
- handler_fault - code to run when a fault occurs
Basics of Kprobes
- Other useful functions
- unregister_kprobe
- kretprobe
- See
/include/linux/kprobes.h
Tracepoints
- Programmer declared points in code that callout to registered probes (tracers)
- Defined usually by macros TRACE_EVENT
- Needs to be defined at compile time.
- If not present, then can't trace the event.
- Traces are more reliable than kprobes because their address is consistent
Ftrace
- Another tracing framework specific to the Linux Kernel
- Uses GCC Profiler (-pg flag) to make calls to mcount within each function call
- mcount is a trampoline function
- These calls generally cause a lot of overhead
- Space is also allocated at boot for all tracepoints
Ftrace
- Another tracing framework specific to the Linux Kernel
- Uses GCC Profiler (-pg flag) to make calls to mcount within each function call
- mcount is a trampoline function
- These calls generally cause a lot of overhead
- Space is also allocated at boot for all tracepoints
Extended Berkley Packet Filters - eBPF
- eBPF is an extension of the original BPF (found in most Unix systems)
- Allows developers to run sandboxed programs in a privilged context (a VM within the kernel)
- Extends functionality to things like kprobes, uprobers, seccomp, XDP, and much much more
EBPF
- Usable on most Linux architectures X86_64, ARM64, etc.
- Allows full tracability within the Linux kernel without installing a full kernel module
- Especially the fact that you can attach kprobes to eBPF programs
EBPF Bytecode
- Remember: eBPF is a VM inside of the Linux Kernel
- Has it's own bytecode language (think like Java byte code, but in the kernel)
- Registers are near 1:1 matching to machine instructions for performance
- Before running, it is sent to the eBPF Verifier to ensure the code is safe
- Lots of rules that the verifier checks (instruction limit, tailcall limit, control flow restrictions, etc.)
- Before running, it is sent to the eBPF Verifier to ensure the code is safe
EBPF Bytecode
- Remember: eBPF is a VM inside of the Linux Kernel
- Has it's own bytecode language (think like Java byte code, but in the kernel)
- Has it's own bytecode language (think like Java byte code, but in the kernel)
- Registers are near 1:1 matching to machine instructions for performance
- Before running, it is sent to the eBPF Verifier to ensure the code is safe
- Lots of rules that the verifier checks (instruction limit, tailcall limit, control flow restrictions, etc.)
- Lots of rules that the verifier checks (instruction limit, tailcall limit, control flow restrictions, etc.)
- Before running, it is sent to the eBPF Verifier to ensure the code is safe
EBPF Bytecode
- eBPF has 11 64-bit registers and a handful of instructions
- R0 is return value
- R1 – R5 are arguments
- R6 - R9 general purpose callee saved
- R10 is the frame pointer, and is read-only
- R1 contains the “context” for the program, which is similar to an argv
EBPF Bytecode
- eBPF instructions are fixed-width: 64-bits
- Opcode: 8 bits
- Destination: 4 bits
- Source: 4 bits
- Offset: 16 bits
- Immediate: 32 bits
- Unused fields are zeroed
- There is only 16-byte instruction that performs a 64-bit immediate load
eBPF Is just like this:
https://www.youtube.com/shorts/P8-gYWaMUqs
eBPF Classwork
Setup
- You need to do this in an Ubuntu 20.04 VM
- git clone https://github.com/libbpf/libbpf-bootstrap
- If you don't have git, do sudo apt install git
- Follow the directions for installing libbpf (inside the libbpf folder) or do sudo apt install libbpf-dev libbpf0
eBPF, KPROBE/UPROBE Exercise
1. Download and install bpftrace
2. Have bpftrace hook on write.
3. Have it print "Hello Write" when a write is triggered
4. Test it out!
5. Repeat this, but create a C program instead of bpftrace
Directions
- Inside of the libbpf-bootstrap/examples/c folder, there are examples of eBPF programs.
- If you run the command make it will generate them for you
- Demonstrates what you can do (such as kprobes, uprobes, XDP, etc.).
Directions
- git clone https://github.com/ENPM809V/libbpf-keylogger
- Copy the keylogger.c and keylogger.bpf.c file into libbpf-bootstrap/examples/c
- Open the Makefile, and append KEYLOGGER to APPS =
Directions
- Fill out the eBPF keylogger template to capture a keypress and key-depressing using a kprobe on input_handle_event
- You need to fill out the BPF_KPROBE with the definitions of input_handle_events parameters.
- Capture key presses then key-depresses!
ENPM809v - Linux Kernel Events
By Ragnar Security
ENPM809v - Linux Kernel Events
- 84