software fault isolation with api integrity and multi-principal modules yandong mao, haogang ...
DESCRIPTION
Software fault isolation with API integrity and multi-principal modules Yandong Mao, Haogang Chen ( MIT CSAIL ), Dong Zhou ( Tsinghua University IIIS ), Xi Wang, Nickolai Zeldovich , Frans Kaashoek ( MIT CSAIL ). Kernel security is important. Kernel is fully privileged - PowerPoint PPT PresentationTRANSCRIPT
Software fault isolation with API integrity and multi-principal modules
Yandong Mao, Haogang Chen (MIT CSAIL), Dong Zhou (Tsinghua University IIIS),
Xi Wang, Nickolai Zeldovich, Frans Kaashoek (MIT CSAIL)
Kernel security is important
• Kernel is fully privileged
• Kernel compromises are devastating• Remote attacker takes control over the whole
machine• Local user gains root privilege
Linux kernel is vulnerable
• Vulnerabilities in Linux are routinely discovered• CVE 2010: 145 vulnerabilities in Linux kernel
• Many exploits attack kernel modules• 67% of Linux kernel vulnerabilities (CVE 2010)
• This talk focuses on vulnerabilities in kernel modules
Threat
Module
• Module programmer makes mistake• Attacker exploits mistake to mount attacks• Example: buffer overflow, set current UID to root
Kernel memory Module memory
UID
Privilege escalation!
One approach: type safe languages
• Write kernel and modules in Java, C#• No reference to UID object => cannot directly change
UID• Attacker cannot synthesize references
Module
UID
Most kernels are not written in type safe language!
Software Fault Isolation (SFI[SOSP93])
char *p = 0xf7;sfi_check_memory(p);*p = 0;
ModuleSFI Runtime
Can not bypass SFI check
Module memory
UID
void sfi_check_memory(p) { if p not in “Module memory” stop_module();}
Memory safety is insufficientfor stopping attacks!
spinlock_t mylock;spin_lock_init(&mylock);
Spin_module
void spin_lock_init(spinlock_t *lock) { lock->v = 0;}
Core Kernel
Module memory
UID
• Challenge: module needs to call kernel functions
Problem: API abuse
• Attacker tricks fully-privileged kernel code to overwrite UID
spin_lock_init(&cur_proc->uid);
Spin_module
void spin_lock_init(spinlock_t *lock) { lock->v = 0;}
Core Kernel
Module memory
UID
Privilege escalation!
Challenge: lack of API integrity• Kernel APIs are not written defensively
• Assume the calling module to obey implicit rules
• Do not check arguments, permissions, etc
• Problem: modules cannot be trusted to follow rules• Module can trick kernel into performing unexpected
actions
• Ideal system would enforce rules for kernel API• Analogy: system call code assumes nothing about caller,
checks every assumption
State of the artfor protecting APIs
• SFI[SOSP93]: memory safety
• XFI[OSDI06]: no argument checks
• BGI[SOSP09]: manually wrap functions, make kernel defensive when kernel code invokes callbacks
• Error-prone and time-consuming
• Works if kernel code is well-structured (not Linux)
Our approach: annotation language
• Helps enforce two types of API integrity:• Argument integrity: programmer controls what
arguments a module can pass to functions
• Callback integrity: kernel invokes callback only if the module could have invoked callback directly
• Allows programmers to specify principals for privilege separation within a module
• Less error-prone than manual wrapping, applicable to complex APIs such as those in Linux
Contributions• LXFI: software fault isolation system for Linux kernel
modules• Annotation language for
• Argument integrity
• Callback integrity
• Privilege separation within a module
• Evaluation• Few annotations for 10 Linux kernel modules
• Stop three real exploits
• 2-4X CPU overhead for netperf
Goals for annotation language
• Enforce argument integrity, callback integrity and privilege separation within a module
• Minimize programmer effort, e.g.:• Few annotations• Avoid data structure and API changes
• Compatible with C
Preventing module exploitsProgrammer
annotates core kernel
LXFI translates annotations
to runtime checks
LXFI performs checks
Using compiler plugins;Provide safe default: reject a module if it calls an unannotated API
If annotations capture all implicit rules, compromised module cannot violate rules to gain additional privileges.
Consulting a dynamic table of capabilities for each module
Compile time
Runtime
Design of annotation language
• Argument integrity annotations• Using the spin_lock_init example
• Callback integrity annotations• Not discussed; see paper
• Privilege separation annotations• Using dm_crypt (real Linux kernel module)
Enforce argument integrity
• spin_lock_init: three annotations are required
Part Syntax DescriptionCapability write(ptr,size) Write [ptr,ptr+size]Capability Action check(cap) Checks cap
Location pre(action) Perform action before function call
Example: enforce argument integrity for spin_lock_init
Core Kernel Spin_modulevoid spin_lock_init(spinlock_t *lock) pre(check(write(lock, sizeof(spinlock_t)))
lxfi_check_write(&cur_proc->uid, 8);spin_lock_init(&cur_proc->uid)
capability table
write(mylock, 8)
Module memory
UID
LXFI
Run
tim
e lxfi_check_write(mylock, 8);spin_lock_init(mylock)
Privilege escalation prevented
……
……
Where does the capability come from?
Part Syntax DescriptionCapability write(ptr,size) Write [ptr,ptr+size]
Capability Action
check(cap) Check cap
copy(cap) Grant a copy of cap
Locationpre(action) Perform action before function call
post(action) Perform action after function return
• Granted on allocation• Two more annotations are required
Core Kernel Spin_module
spinlock_t *mylock = kmalloc(8);lxfi_copy_write(mylock, 8);
void *kmalloc(size) post(copy(write(return, size))
capability table
write(mylock, 8)
Example: grant spinlock
LXFI
Run
tim
e……
What happens when memory is freed?
• Need to revoke capability to safely reuse memory• Strawman: revoke capability from caller
• Insufficient! Other modules may have copies of capability
Part Syntax DescriptionCapability write(ptr,size) Write [ptr,ptr+size]
Capability Action
copy(cap) Grant a copy of cap
check(cap) Check cap
transfer(cap) Revoke cap from all modules, and grant
Location pre(action) Perform action before function call
post(action) Perform action after function return
No other copies of the capability remain
Core Kernel Spin_modulevoid kfree(void *p) pre(transfer(write(p, no_size)))
capability table
write(mylock, 8)
Example: safely free a spinlockLX
FI R
unti
me
capability table
write(mylock, 8)
other_module
lxfi_transfer_write(mylock, -1);
kfree(mylock);……
Why is spin_module able to call spin_lock_init, kmalloc, kfree?
• Call capability• Granted initially according to the module’s symbol table
• Trust module author not to call unnecessary functions
• Dynamically granted when a callback function is passedPart Syntax Description
Capabilitywrite(ptr,size) Write [ptr,ptr+size]
call(a) Call a
Capability Action
copy(cap) Grant a copy of cap
check(cap) Check cap
transfer(cap) Revoke cap from all modules, and grant
Locationpre(action) Perform action before function call
post(action) Perform action after function return
Core Kernel Spin_modulevoid *kmalloc(size) post(copy(write(return, size))void spin_lock_init(spinlock_t *lock) pre(check(write(lock, sizeof(spinlock_t)))void kfree(void *p) pre(transfer(write(p, no_size))
capability table
call(kmalloc)call(spin_lock_init)call(kfree)
LXFI
Run
tim
e spinlock_t *mylock = kmalloc(8);lxfi_copy_write(mylock, 8);
lxfi_check_write(mylock, 8);spin_lock_init(mylock)l
lxfi_check_write(&cur_proc->uid, 8);spin_lock_init(&cur_proc->uid);
lxfi_transfer_write(mylock, -1);kfree(mylock);
……
……
……
……
• SFI ensures memory safety• Call capabilities ensure only 3 functions are
allowed• None of the functions can modify UID because:
• kmalloc never modifies allocated memory• spin_lock_init can only be called with
writable memory (from kmalloc)• kfree ensures no capabilities remain after
free• spin_module can not modify UID!
No way for compromised spin_module to gain root privilege
Part Syntax Description
Capabilitywrite(ptr,size) Write [ptr,ptr+size]call(a) Call aref(a, t) Pass a as t
Capability Action
copy(cap) Grant a copy of cap
check(cap) Check cap
transfer(cap) Revoke cap from all principals, and grant
Locationpre(action) Perform action before function call
post(action) Perform action after function return
Privilege separation within a module• dm_crypt: transparent encryption service for block devices
• This example requires a third type of capability
Pass argument a as type t
Privilege separation
Core Kernel
User space
Kernel space
write(“/etc/secret.txt”, “foo”)
int bdev_write(block_device *dev, const char * data, …) pre(check(ref(block_device), dev)
dm_crypt
write(enc_disk, “foo”, …)
LXFI
Run
tim
e
capability table
ref(block_device, enc_disk->bdev)
lxfi_check_ref(block_device, enc_disk->bdev)bdev_write(enc_disk->bdev, E(“foo”), …)
Writing block device does not require writing to memory of enc_disk->bdev.
Privilege separation
Core Kernel
User space
Kernel spaceint bdev_write(block_device *dev, const char * data, …) pre(check(ref(block_device), dev)
dm_crypt
LXFI
Run
tim
e
capability table
ref(block_device, enc_disk->bdev)ref(block_device, enc_usb->bdev)
lxfi_check_ref(block_device, enc_disk->bdev)bdev_write(enc_disk->bdev, “/etc/pwd”, “foo”)
Decrypt
capability table
ref(block_device, enc_disk->bdev)
capability table
ref(block_device, enc_usb->bdev)
/etc/pwd: rootpwd=foo
read(…)
How to define principals• Associate a principal with every instance a
module supports (e.g. block device in dm_crypt)• Problem: how to specify and name principals?
• Recall goal: minimize changes to existing data structures
• Idea: re-use address of data structure as the name of the principal
• Can typically identify principal from one of the function arguments
Specifying principals
Part Syntax Description
Capabilitywrite(ptr,size) Write [ptr,ptr+size]ref(a, t) Pass a as tcall(a) Call a
Capability Action
copy(cap) Grant a copy of cap
check(cap) Check cap
transfer(cap) Revoke cap from all principals, and grant
Locationpre(action) Perform action before function call
post(action) Perform action after function returnPrincipal principal(ptr) Run with privileges of principal ptr
Privilege separation
Core Kernel
User space
Kernel space
dm_crypt
LXFI
Run
tim
e
lxfi_check_write(enc_disk->bdev, 100)bdev_write(enc_disk->bdev, “/etc/pwd”, “foo”)
Decrypt
capability table
write(enc_disk->bdev, 100)
capability table
write(enc_usb->bdev, 100)
struct dm_type { int (*map)(struct dm_target *di); principal(di)};
lxfi_set_princ(enc_usb)dm_crypt.map(enc_usb)
/etc/pwd: rootpwd=foo
Principal name aliasing• Problem: Kernel identifies a LXFI principal by multiple
addresses
• Insert code into module to create alias• The same principal now has multiple names
int e1000_probe(struct pci_dev *pcidev) { struct net_device *ndev = alloc_etherdev(...); ndev->pcidev = pcidev; ...}int e1000_xmit(struct net_device *dev) { …}
lxfi_princ_alias(pcidev, ndev);
Other annotation language features
Part Syntax Description
Capability
write(ptr,size) Write [ptr,ptr+size]ref(a, t) Pass a as tcall(a) Call acap_iterator(obj) A function iterates all cap. of obj
Capability Action
copy(cap) Grant a copy of cap
if(c-expr) action Perform action only if c-expr
check(cap) Check cap
transfer(cap) Revoke cap from all principals, grant cap
Locationpre(action) Perform action before function call
post(action) Perform action after function return
Principal principal(ptr) Run with privileges of principal ptr(global, shared)
Global:principal with full priviligeShared:principal with minimal privilege
Save annotation effort for complex objects that need multiple capabilities
Express conditional action such as grant a privilege if return value is OK
Implementation
• Linux 2.6.36, x64, single-core
• gcc plugin: kernel rewriting for callback integrity
• Clang/LLVM plugin: module rewriting• Annotation propagation saves effort by inferring
annotations of module functions
Example: annotation propagation
//from linux/include/pci_driver.h
struct pci_driver { int (*probe)(struct pci_dev *pcidev) principal(pcidev) pre(copy(ref(struct pci_dev), pcidev)}
//linux/drivers/net/e1000/e1000_main.c
int e1000_probe(struct pci_dev *pcidev) { ….}struct pci_driver e1000_driver = { .probe = e1000_probe};//linux/drivers/net/ixgbe/ixgbe_main.c
int ixgbe_probe(struct pci_dev *pcidev) { ….}struct pci_driver ixgbe_driver = { .probe = ixgbe_probe};
LXFI propagates annotation on probe to modules
Evaluation
• Security
• Annotation effort
• Performance overhead
Security
• Test LXFI with three real privilege escalation exploits
• Stopping real attacks requires API integrity
Exploit CVE ID Violated Property
Unmodified Linux
LXFI
CAN_BCM CVE-2010-2959 Memory Safety
Econet
CVE-2010-3849
API IntegrityCVE-2010-3850
CVE-2010-4258
RDS CVE-2010-3904 API Integrity
Annotation effort
• Annotate kernel APIs for 10 modules, one at a time
• Count:• # of annotated core kernel functions a module
calls• # of function pointer declarations a module
exports to core kernel
Sharing reduces annotation effort
Category Module#Functions # Function Pointers
All Unique All Uniquenet device driver e1000 81 49 52 47
sound device driversnd-intel8x0 59 27 12 2snd-ens1370 48 13 12 2
net protocol driver
rds 77 30 42 26can 53 7 7 3
can-bcm 51 15 17 1econet 54 15 20 3
block device driverdm-crypt 50 24 24 14dm-zero 6 3 2 0
dm-snapshot 55 16 28 18Total 334 155
LXFI performance
• netperf, 1 Gigabit e1000 network card, LAN• Stresses LXFI
TestThroughput CPU %
Stock LXFI Stock LXFITCP_STREAM TX
UDP_STREAM TX
836 M bits/sec
3.1 M/3.1 M pkt/sec
828 M bits/sec
2.0 M/2.0 M pkt/sec
13%
54%
48%
100%
~30% decrease
• Room for improvement
Capability actionMem-write checkFunction EntryFunction ExitIndirect call check
80%
CPU time of LXFI actions for netperf
Future work
• Improve performance• Faster capability management such as BGI’s
• Extend annotation language to enforce other types of API integrity
• Perhaps based on Singularity’s contracts
Related work
• Type-safe kernels: Singularity [MSR-TR05]• LXFI provides similar guarantees in C• Good support for revocation (transfer) and
principals
• Software fault isolation• LXFI extends existing SFI systems (SFI, XFI,
BGI) with annotation language
Conclusion
• Extend SFI with annotation language for:• Argument integrity• Callback integrity• Principals
• LXFI: Prototype for Linux• Annotated 10 kernel modules• Prevented 3 real privilege escalation exploits• 2-4X CPU overhead when stressing with netperf
Q & A