Linux Kernel Exploitation 101
Spawn your uid=0 like ninja
Indice
- Introduzione teorica al kernel
- Kernel Debugging
- Vulnerabilità comuni
- Mitigation & Exploitation
Cos’è il kernel
Know your enemy
Comunicazione con HW e SW
HARDWARE KERNEL SOFTWARE
Esempio: Vuoi sentire musica da spotify
HARDWARE KERNEL SOFTWARE
linux-5.14
SOFTWARE KERNEL HARDWARE
curl https://target.com/
linux-5.14
WiFi/ethernet/..
SOFTWARE KERNEL HARDWARE
linux-5.14
Kernel-mode User-mode
HARDWARE KERNEL SOFTWARE
User-Mode vs Kernel-Mode
- User-Mode (aka User-Land / User-Space)
- Applicazioni software (Spotify, Browser (Chrome, Safari,..), Teams, ..)
- Privilegi limitati (ring 3)
- Comunica costantemente con il kernel (volontariamente e non)
- root user (root != kernel)
- Kernel-Mode (aka Kernel-Land / Kernel-Space)
- Ring 0
- Driver
- Interazione con l’hardware
- Schede grafiche/rete, chip Bluetooth/4G/, mouse, tastiere, periferiche audio
- Gestione dei privilegi
- Operazioni piú comuni
- Allocazione memoria per applicazione user-mode
- Comunicazione tra processi (IPC)
- Comunicazione network (TCP/IP, …)
- Ring 1 e 2 nativamente non utilizzati
- Ring 1 utilizzato da virtualizzazione (VirtualBox, VMWare, ..)
Kernel-mode User-mode
(ring 0) (ring 3)
HARDWARE KERNEL SOFTWARE
Come ??
User-mode => Kernel-Mode
- syscall
- Wrappers
- read, write, open, close
- socket, bind, connect, listen
- mmap, mprotect
- ioctl (Input Output ConTroL)
- https://filippo.io/linux-syscall-table/
Kernel-Mode => Hardware (Driver)
- Comunica direttamente con l’hardware tramite driver
- Un driver è un “modulo” aggiuntivo che stabilisce una comunicazione tra hardware e kernel
- Il driver viene scritto seguendo il “datasheet” della periferica
- Può fare da “ponte” per l’interazione tra user-mode e hardware
- Per esempio per la riproduzione di file musicali (sequenza di bit).
Kernel-Mode => Hardware - Esempio MRF24J40
- Implementazione del driver per il microchip MRF24J40 802.15.4
- Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/39776C.pdf
- Driver: /drivers/net/ieee802154/mrf24j40.c
Hardware => Kernel-Mode
- L’HW si fa “notare” dal kernel utilizzando gli Interrupt
- Una periferica (e.g. una scheda di rete) manda un interrupt alla CPU
- Il kernel gestisce l’interrupt (Interrupt Handling) ed instrada l’informazione dove necessario
- Per esempio, se si tratta di un pacchetto arrivato ad una scheda di rete, questo dovrà essere elaborato in base
al tipo di protocollo.
- I tasti che premiamo sulla tastiera mandando un Interrupt all’Interrupt Handler
- Diversi tipi di Interrupt
- Maskable/Non Maskable
- Classificati per priority
Kernel Memory
Stack, Context, Heap (SLA|O|UB)
Stack vs Heap
Stack Heap
- Statico - Dinamico
- Le allocazioni sono determinate a - Allocazioni determinate runtime
compile-time - alloc/free manuali
- Size-limited - kmalloc/kfree
- Piú veloce - Frammentazione
- Salva variabili locali alla funzione
- I registri RSP & RBP delimitano lo stack
- Ogni funzione ha il proprio stack
Heap allocations / Memory Management
- SLAB
- Precursore
- EOL (verrà rimosso)
- Termine generico anche nell'implementazione di SLOB/SLUB
- SLOB
- Ottimizzato per sistemi Embedded
- SLUB
- Linux > 2.6.12
SLUB
- PAGE_SIZE allocations (4096)
- Divisione per grandezza
- 8, 16, 32, 64, 128 … 4k, 8k (x86_64)
- 128, 256, 512, .. 6k, 8k (ARM64)
- Divisione per tipo
- GFP_KERNEL / GFP_KERNEL_ACCOUNT
- kmalloc-16 / kmalloc-cg-16 / special caches
- Chunk
- Un oggetto in memoria
- slab/cache
- Un insieme di allocazioni dello stesso tipo
- e.g. kmalloc-8 puó avere N slabs che contengono ognuno (PAGE_SIZE / 8) chunks
SLUB / partial slabs
slab 1
slab 2
slab 3
SLUB / partial slabs
slab 1
slab 2
slab 3
SLUB / partial slabs
partial slab 1
slab 1
partial slab 2
slab 2
slab 3
SLUB / partial slabs
slab 1
partial slab 2
slab 2
slab 3
SLUB / partial slabs
slab 1
partial slab 2
slab 2
slab 3
SLUB / partial slabs
slab 1
partial slab 2
slab 2
slab 3
SLUB
- APIs
- kmalloc()
- flags: GFP_KERNEL | GFP_KERNEL_ACCOUNT | …
- kfree()
- Special-purpose caches
- kmem_cache_create()
- kmem_cache_alloc()
- struct kmem_cache / struct kmem_cache_cpu
- Struttura che descrive la cache
SLUB - Useful files
- /proc/slabinfo
- /sys/kernel/slab/
- /sys/kernel/slab/<CACHE>/partial
- /sys/kernel/slab/<CACHE>/objects_size
- /sys/kernel/slab/<CACHE>/objs_per_slab
Pointers: Kernel vs User
- Documentation/x86/x86_64/mm.rst
- 4-level page tables
- Più utilizzato su x86_64 (in base alla CPU)
- Userspace
- 0x0000000000000000 => 0x00007fffffffffff
- Kernel
- 0xffff800000000000 => 0xffffffffffffffff
- 5-level page tables
- Documentation/x86/x86_64/mm.rst#L75
- Configuration file: arch/x86/Kconfig
Kernel mapped on users process
0xffffffffffffffff
KERNEL-SPACE
0xffff800000000000
0x00007fffffffffff
USER-SPACE
0x0000000000000000
Kernel memory mapped on user-space
- È possibile dunque accedere alla memoria del kernel tramite user-mode?
- NO!
- Se si, è una vulnerabilità (o post-exploitation).
- Anche le operazioni da kernel-mode a user-mode sono controllate.
- https://elixir.bootlin.com/linux/v5.19-rc7/source/lib/usercopy.c#L10
- https://elixir.bootlin.com/linux/latest/source/include/asm-generic/access_ok.h#L31
copy_[from|to]_user
- copy_from_user
- unsigned long copy_from_user (void * to, const void __user * from, unsigned long n);
- “Copy a block of data from user space”
- __copy_from_user
- “Copy a block of data from user space, with less checking” (no access_ok)
- copy_to_user
- unsigned long copy_to_user (void __user * to, const void * from, unsigned long n);
- “Copy a block of data into user space.”
- __copy_to_user
- “Copy a block of data into user space, with less checking” (no access_ok)
copy_[from|to]_user checks
- Puntatori
- Che i due parametri “to” e “from” siano validi (tramite access_ok())
- copy_from_user
- from: user pointer ( < 0x00007fffffffffff )
- copy_to_user
- to: user pointer ( < 0x00007fffffffffff )
- CONFIG_HARDENED_USERCOPY
- Controlli sulla grandezza dei chunk
Common vulnerabilities
Common vulnerabilities (memory corruptions)
- Stack Overflow
- Heap Overflow/Underflow
- Large overflows / off-by-one / Integer Overflow...
- Use-After-Free
- UAF read/write
- UAF self-referencing pointer (linked lists)
- Double Free
- Race Conditions / refcount
- => UAF/Hep ovf/..
Weird machine
- Che cos’è una corruzione di memoria?
- Si accede (r|w|x) ad una zona di memoria in maniera non prevista dal programma
- Mancati controlli (e.g. di una size)
- Errati controlli (assunzioni non corrette)
- Come si sfrutta?
- GIOCANDO e MANIPOLANDO la memoria ed il programma
- Creando una Weird Machine
- Far fare ad un programma ció per cui non è stato progettato (e.g. eseguire uno shellcode/ROP =>
RCE).
- Bypass mitigazioni correnti
Common Mitigations
- Comuni
- kASLR
- SMAP
- SMEP
- Extra
- CONFIG_FG_KASLR
- CONFIG_STATIC_USERMODEHELPER
- CONFIG_SLAB_FREELIST_RANDOM/HARDENED
- CONFIG_DEBUG_LIST
- CONFIG_HARDENED_USERCOPY
- CONFIG_ARM64_UAO
- CONFIG_FORTIFY_SOURCE
- CONFIG_MEMCG
- ..
Mitigazioni == Impossibile scrivere exploit ?
- NO !
- Ma ci sono dei cambiamenti
- Ogni singola vulnerabilità fa caso a se
- Stack Overflow => Overwrite RIP => shellcode => PWN
- Heap Overflow => Unlinking => RIP control => PWN
- Sempre piú difficile (nuove mitigation appaiono ogni anno)
- Ma anche piú divertente =) (e stressante)
Heap Overflow
Heap Overflow
- Concetto base
- Oggetto A sovrascrive oggetto B (allocato successivamente all’oggetto A)
- In memoria, oggetto prende il nome di chunk
…
Heap Overflow
A = kmalloc(32);
chunk A
Heap Overflow
A = kmalloc(32);
B = kmalloc(32);
chunk A chunk B
Heap Overflow
A = kmalloc(32);
B = kmalloc(32);
.. kmalloc(...)... // Altre allocazioni
chunk A chunk B
… …
Heap Overflow
A = kmalloc(32);
B = kmalloc(32);
.. kmalloc(...)... // Altre allocazioni
memset(A, 0x41, 32);
chunk A chunk B
… AAAAAAAAAAAAAAA …
Heap Overflow
A = kmalloc(32);
B = kmalloc(32);
.. kmalloc(...)... // Altre allocazioni
memset(A, 0x41, 32);
memset(A, 0x41, 64);
chunk A chunk B
… AAAAAAAAAAAAAAA AAAAAAAAAAAAAAA …
Heap Overflow
A = kmalloc(32);
B = kmalloc(32);
.. kmalloc(...)... // Altre allocazioni
memset(A, 0x41, 32);
memset(A, 0x41, 64);
B->function() // => Corrupted from chunk A => RIP = 0x4141414141414141
chunk A chunk B
… AAAAAAAAAAAAAAA AAAAAAAAAAAAAAA …
A => Target object Target object
B => Victim object - Oggetto dove si presenta la vulnerabilità
Victim object
Heap Overflow - Oggetto scelto arbitrariamente dall’attaccante
(per essere corrotto ad-hoc)
Use-After-Free
- Concetto base
- Un oggetto, precedentemente de-allocato (free), viene riutilizzato
…
Use-After Free
A = kmalloc(32);
chunk A
Use-After Free
A = kmalloc(32);
free(A);
chunk A
Use-After Free
A = kmalloc(32);
free(A);
B = kmalloc(32);
chunk B
Use-After Free
A = kmalloc(32);
free(A);
B = kmalloc(32);
A->function(32); // Access undefined values => RIP: ????
chunk B
Use-After Free
A = kmalloc(32);
free(A);
B = kmalloc(32);
A->function(32); // Access undefined values => RIP: ????
A => Target object
chunk B
B => Victim object
Target object
- Oggetto dove si presenta la vulnerabilità
Victim object
- Oggetto scelto arbitrariamente dall’attaccante
Use-After Free (per essere corrotto ad-hoc)
Common Mitigations
KASLR, SMAP, SMEP
KASLR
- Randomizzazione degli indirizzi
- Richiede, quasi sempre, la necessità di una Information Disclosure
- Per ottenere altri indirizzi
- Esempio
- Un leak di un pointer nella sezione .text del kernel permette di ottenere tutti gli altri indirizzi
- Scenario
- Pre-ASLR
- Arbitrary Write => Write ad un indirizzo conosciuto => uid=0
- ASLR
- Arbitrary Write
- Se possibile trasformarlo in un Arbitrary/OOB read =>uid=0
- Oppure necessità di un’altra vulnerabilità che permette Information Disclosure => uid=0
SMAP / SMEP (x86_64)
- SMAP (Supervisor Mode Access Prevention)
- Genera un “fault” se si accede alla memoria user-space
- “Allows supervisor mode programs to optionally set user-space memory mappings so that access to those
mappings from supervisor mode will cause a trap” (Wikipedia)
- SMEP (Supervisor Mode Execution Prevention)
- Genera un “fault” se si esegue dalla memoria user-space
- Gestito dal registro CR4
KERNEL-SPACE
USER-SPACE
Come fa copy_from|to|user a funzionare?
- Disabilitando temporaneamente SMAP (dal registro CR4)
STEPS (esempio)
1. copy_from_user()
2. Disable SMAP (ASM_STAC)
3. Interazione con user-space
4. Enable SMAP (ASM_CLAC)
SMEP / Supervisor Mode Execution Prevention
Kernel-land
User-land
SMEP / Supervisor Mode Execution Prevention
Kernel-land
RIP control
User-land
SMEP / Supervisor Mode Execution Prevention
Kernel-land
RIP control
User-land
Shellcode
SMEP / Supervisor Mode Execution Prevention
Kernel-land
RIP control
RIP
User-land
Shellcode
SMEP / Supervisor Mode Execution Prevention
Kernel-land
RIP control userspace_address()
RIP
User-land
SMEP
Shellcode
SMEP / Supervisor Mode Execution Prevention
Kernel-land
RIP control userspace_address()
RIP
User-land
SMEP
Shellcode Conosciuto come ret2usr
SMAP / Supervisor Mode Access Prevention
Kernel-land
RIP control
0xffffff5eaa7bc000
0xffffff5eaa7bc034
User-land
0xffffff5eaa7bc983
0xffffff5eaa530f10
ROP chain
SMAP / Supervisor Mode Access Prevention
Kernel-land
RIP control
0xffffff5eaa7bc000
0xffffff5eaa7bc034
Stack Pivot User-land
0xffffff5eaa7bc983
0xffffff5eaa530f10
ROP chain
SMAP / Supervisor Mode Access Prevention
Kernel-land kernel_ptr = * (user_ptr)
RIP control
copy_from_user(kernel_ptr, user_ptr, size);
0xffffff5eaa7bc000
0xffffff5eaa7bc034
SMAP Stack Pivot User-land
0xffffff5eaa7bc983
0xffffff5eaa530f10
ROP chain
SMAP/SMEP
- SMAP/SMEP sono mitigation Intel
- e.g. anche Windows
- ARM ha il corrispettivo PXN/PAN
- Stesso concetto, implementazione kernel/cpu diversa
Exploitation techniques
Attack kernel structures
Exploitation strategies
- Dipende dalla vulnerabilità
- L'obiettivo (di solito e se possibile) è costruirsi una primitiva R/W
- Se hai r/w sul kernel, la root é (generalmente) solo questione di tempo (e di bypass di mitigation)
- Caso comune
- Si parte da una vuln con r/w limitato per arrivare ad una arbitrary R/W
- Da un primitiva write è possibile costruirsi una R/W
- Se controlli il RIP (e.g. tramite la corruzione di una funzione kernel)
- Possibile costruire una ROP/JOP a seconda delle mitigation
- Si ha quasi sempre bisogno di un information leak
- Per bypassare KASLR
- Chaining di piú vulnerabilità
A = kmalloc(32);
free(A);
B = kmalloc(32);
A->function(32); // Access undefined values => RIP: ????
A => Target object
chunk B
B => Victim object
Target object
- Oggetto dove si presenta la vulnerabilità
Victim object
- Oggetto scelto arbitrariamente dall’attaccante
Use-After Free (per essere corrotto ad-hoc)
A = kmalloc(32);
B = kmalloc(32);
.. kmalloc(...)... // Altre allocazioni
memset(A, 0x41, 32);
memset(A, 0x41, 64);
B->function() // => Corrupted from chunk A => RIP = 0x4141414141414141
chunk A chunk B
… AAAAAAAAAAAAAAA AAAAAAAAAAAAAAA …
A => Target object Target object
B => Victim object - Oggetto dove si presenta la vulnerabilità
Victim object
Heap Overflow - Oggetto scelto arbitrariamente dall’attaccante
(per essere corrotto ad-hoc)
Victim Object
- Un oggetto che viene utilizzato per ottenere altre primitive
- R/W/X
- link 1 / link 2
- shm_file_data
- msg_msg
- Tty_struct
- …
- Utilizzi
- Heap Overflow
- Oggetto che viene allocato adiacente all’oggetto vulnerabile per essere sovrascritto
- UAF
- Oggetto che viene rimpiazzato
Victim Object
- Un oggetto che viene utilizzato per ottenere altre primitive
- R/W/X
- link 1 / link 2
- shm_file_data
- msg_msg
- Tty_struct
- …
- Utilizzi
- Heap Overflow
- Oggetto che viene allocato adiacente all’oggetto vulnerabile per essere sovrascritto
- UAF
- Oggetto che viene rimpiazzato
A = kmalloc(32);
B = kmalloc(32);
.. kmalloc(...)... // Altre allocazioni
memset(A, 0x41, 32);
memset(A, 0x41, 64);
B->function() // => Corrupted from chunk A => RIP = 0x4141414141414141
chunk A chunk B
… AAAAAAAAAAAAAAA AAAAAAAAAAAAAAA …
A => Target object Target object
B => Victim object - Oggetto dove si presenta la vulnerabilità
Victim object
Heap Overflow - Oggetto scelto arbitrariamente dall’attaccante
(per essere corrotto ad-hoc)
Victim Object
- Un oggetto che viene utilizzato per ottenere altre primitive
- R/W/X
- link 1 / link 2
- Shm_file_data / msg_msg / tty_struct / ..
- Utilizzi
- Heap Overflow
- Oggetto che viene allocato adiacente all’oggetto vulnerabile per essere sovrascritto
- UAF
- Oggetto che viene rimpiazzato
- Prerequisito
- L’oggetto deve essere allocato nella stessa cache
- Target obj = kmalloc-32
- Victim obj = kmalloc-32 (must)
- Altrimenti cross-cache attacks
- https://duasynt.com/blog/linux-kernel-heap-feng-shui-2022
- Non essere de-allocati prima di sfruttare la vulnerabilità
kmalloc-32
Heap Overflow (Read)
kmalloc-32
chunk A shm_file_data
Heap Overflow (Read)
kmalloc-32
read(A, 64); // Read chunk A and shm_file_data
// => leak vm_ops (kernel .data) => ASLR DEFEATED
chunk A shm_file_data
… READING
Heap Overflow (Read)
kmalloc-32
read(A, 64); // Read chunk A and shm_file_data
// => leak vm_ops (kernel .data) => ASLR DEFEATED
chunk A shm_file_data
… READING
Stesso concetto per Write Overflow
Heap Overflow (Read)