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.
  • 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 at request_irq)
  • If the handler is going to service the interrupt, it should return IRQ_HANDLED.
    • Otherwise it should return IRQ_NONE

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 at request_irq)
  • If the handler is going to service the interrupt, it should return IRQ_HANDLED.
    • Otherwise it should return IRQ_NONE

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 of notifier_calls

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
  • 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
  • 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.)

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.)

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!