DV1460 / DV1492:
Realtime- (and) Operating-Systems
08:15-10:00 Thursday October 20th, 2016
Security Part I
Table of Contents
Exploitation by Primitives
Last time we saw exploit and defences of increasing complexity.
The arms race between defenders and attackers is an evolutionary struggle.
Return-oriented programming and format-string attacks.
The attacker is building read-, write- and execute- primitives from different bugs in the code.
As defender plug up old holes, the attack vectors become more obscure.
Interesting economic perspective: have we passed "peak exploit"?
The final few exploits we look at dig into the Byzantine layers of software in a modern OS.
Dangling pointers: heap manipulation.
Command Injection: using shellshock to attack the process model.
2. Dangling pointers
Dangling pointers are a basic type of bug.
Program allocates some memory.
After using it the program frees the memory.
Later (for reasons) the pointer is used
the memory is free.
char *x = new char; ... do lots of stuff ... delete x; ... some time later ... x = ...
Why would the programmer do this?
Mismatched assumptions, harder to spot in different procedures / classes / libraries...
Conditional code - some paths free the memory and some do not.
Like buffer overflows there are many of these kinds of bugs in large programs.
3. Building a write-primitive
Unlike a buffer-overflow: it is unclear where the dangling pointer will write.
We don't know what memory it targets.
Ideally writing through the pointer would cause an access violation exception.
But, recall the interface between the program and the memory management.
Simple, dense, contiguous address ranges.
Memory frees do not propagate to the memory subsystem.
The memory handler inside the process libc is a performance hack (avoid syscalls).
New heap objects will occupy the old addresses...
4. Object Orientation
Interesting things happen in the runtime of OO languages.
When objects are stored on the heap:
A piece of memory is allocated for the data storage.
A V-table is filled in for the methods.
These are function pointers...
Overwriting a location on the heap can corrupt a V-table.
Calling a method on that object will now jump to an arbitrary address (execution-primitive).
OO language + dangling pointers = replacement for buffer overflows.
5. Browser exploits
Browser exploits are very valuable.
Vulnerable system only has to load a page.
Advertising is pervasive.
It sits on-top of the heap-management provided by the host OS.
Any dangling pointers in the runtime can lead to control of the browser.
[I was somewhat distracted writing this as someone is trying this technique in twitter adverts...]
6. NULL pointers
NULL is used as a sentinel in many languages.
No data or code can live at address 0.
This is a basic assumption.
But some systems allow a slightly richer interaction with the memory subsystem.
Recall the interesting tricks that mmap allows.
Sharing memory with other processes.
Avoiding copy overhead in file I/O.
And until it was patched out...
...mapping addressable pages onto NULL!
Target was a program with two bugs:
Jump to NULL (e.g. uninitialised function pointer), exploitable mmap().
7. Command Injection
We introduced system() as a dangerous vector in the standard library.
But many programs have legimate uses for it.
cron - scheduler for unix systems.
User can run arbitrary commands at specific times of day.
Lazy programmers ("rapid application development"), embedding bits of shell: we never intend for scaffolding to become permanent.
The system() call takes a string to execute.
So a vulnerable program has some string processing depending on user input.
If the attacker can control the contents of the string - run arbitrary code.
e.g. if the user supplies filenames that are sprintf'd into a string.
8. Command Injection
(C) Randall Munrow (
Securing an OS is significantly more complex than a database.
All inputs to any privileged program must be sanitised.
Great care must be taken to avoid exposing usable primitives.
9. Security Threats
We've seen a lot about the mechanisms for breaking in.
It all boils down to finding / building / using:
Read-, Write- and Execute-primitves.
With this "hacker mindset" lets take a step back and review some features.
Convenience vs Security
Every feature is a potential read-, write- or execute-primitive. Securing this attack surface means breaking composabiity of features.
ActiveX - removes the inconvenience of sandboxing by giving full access to arbitrary websites.
HTML email - replaces a "transparent" data format with a layer of abstraction that may include unknown primitives (e.g. unique outgoing image links, cookie access).
10. Review of security threats
Logic bombs - running close source software is an opportunity to live in "interesting times".
Back door - running un-audited source is a form of execute-primitive.
NFS - mounting external storage is highly convenient.
Enables sharing of storage space and scaling beyond sizes available in a desktop.
Assumes the server is trusted.
Any compromise between client and server (LAN, router, server itself) enable read- and write-pritives to any file in the filesystem.
If executables are stored in NFS then it becomes too easy.
Normal NFS configurations keep binaries on local volumes (contrast with NX "protection").
11. Review of security threats
Trojan horse - running an unknown program is handing an execute-primitive to the attacker.
e.g. helpful "generators" (serials, credit cards etc), cracks, pirated games, "custom VPNs", wifi authentication, disk "cleaners"...
Spoofing logins - giving credentials to an unaudited entity...
...you wouldn't just give your credit card to a waiter would you?
The purpose of credentials is to guard access to primitives.
Viruses rely on the user supplying credentials to them.
If I can't make a program that executes itself, perhaps the user will do it for me?
Start with a limited execute-primitive (e.g. a trojan horse).
Use that execution to rewrite and enable further execute-primitives.
Other programs, boot-sectors, device-drivers...
12. Review of security threats
Exploits in programs that read from the network are highly valuable.
e.g Send data to trigger buffer-overflow, ..., Profit!
No loner necessary to trick the user into supplying an execute-primitive.
Automating the infection process:
The Morris Worm (1988), best explained by Michael Bolton in Office Space.
"I always do that. I always mess up some mundane detail."
Three expoits: rsh (guest acounts!), finger exploit, sendmail exploit.
Of course this led people to wonder, how could we really improve this?
Warhol worm - hidden propagation phase followed by mass infection.
Worm propagation strategies are similar to human epidemics.
Roughly speaking: Plague Inc strategies work well.
13. Review of security threats
Once root access has been achieved, keeping it means hiding from view.
Hijack any syscalls that would reveal the kit, rewrite output.
Needs an execute-primitive in the boot sequence for the OS.
a) Firmware b) Blue pill c) device driver / module d) standard library e) application (e.g. systemd)
Final summary: security is a cross-cutting concern.
Controlling access to primitives cannot be logically decomposed into nice clean box - it impacts every aspect of OS design and implementation.
15. Note to self
Delivery speed vs slide contents is off.
Started to wander a little during FS block.
Slipped about 1/2 lecture during I/O
Security stuff is about 14.5 and 15.
shrink I/O back to 2, expand either Security or 1/2 on VM.
expand explanations so numbering matches running order.