From Primitives to Exploit

Memory
Corruption 101
From Primitives to Exploit
Created by Nick Walker @ MWR Infosecurity / @tel0seh
What is it?
A result of
Undefined Behaviour
Undefined Behaviour
A result of executing computer code which does not have a
prescribed behaviour
Compilers are completely legal to do anything
Undefined Behaviour
#include <limits.h>
#include <stdio.h>
int main (void)
{
printf ("%d\n", (INT_MAX+1) < 0);
return 0;
}
Undefined Behaviour
#include <limits.h>
#include <stdio.h>
int main (void)
{
printf ("%d\n", (INT_MAX+1) < 0);
return 0;
}
$ cc test.c ­o test
$ ./test
1
valid
Undefined Behaviour
#include <limits.h>
#include <stdio.h>
int main (void)
{
printf ("%d\n", (INT_MAX+1) < 0);
return 0;
}
$ cc test.c ­o test
$ ./test
0
valid
Examples
Not returning a value from a non-void method
Modification of a string literal
Incrementing INT_MAX
Decrementing INT_MIN
Dereferencing a NULL pointer
Using Pointers who's life has ended
Copying outside the bounds of an array
and many more...
Undefined Behaviour
#include <limits.h>
#include <stdio.h>
int main (void)
{
printf ("%d\n", (INT_MAX+1) < 0);
return 0;
}
$ cc test.c ­o test
$ ./test
42
valid
Undefined Behaviour
#include <limits.h>
#include <stdio.h>
int main (void)
{
printf ("%d\n", (INT_MAX+1) < 0);
return 0;
}
$ cc test.c ­o test
$ ./test
FORMATTING ROOT PARTITION, NOM NOM NOM
valid
Examples
Not returning a value from a non-void method
Modification of a string literal
Incrementing INT_MAX
Decrementing INT_MIN
Dereferencing a NULL pointer
Using Pointers who's life has ended
Copying outside the bounds of an array
and many more...
Impact
Though any behaviour is legal - that doesnt mean something
random happens.
A program performing undefined behaviour will always do the same
thing, dependent on initial conditions
same processor architecture
exactly the same code flow
exactly the same input
and we can start to reliably (read: with certainty) determine
behaviour in a given circumstance
Impact
INT_MAX + 1 = INT_MIN
INT_MIN -1 = INT_MAX
Writing outside the bounds of a buffer will overwrite directly
adjacent structures
This is exploit development.
The reliable and predictive abuse of undefined behaviour.
Primitives
The goal is to obtain:
Arbitrary read = Information leakage
Arbitrary write = code execution
certain bug classes allow both
Others don't quite fit into either, but they allow code execution
Arbitrary Read
Abuse mechanisms to read arbitrary data from memory
Array out of bounds access
Non terminated strings
Type Confusion
#include <stdio.h>
int main(int argc, char **argv){
char foo[3] ="AAA";
printf("%s",foo);
return 0;
}
$ cc test.c ­o test
$ ./test
AAA��​
%
Arbitrary Write
Abuse mechanisms to write arbitrary data from memory
Array out of bounds assignment / Buffer overflow
Double Free
Type Confusion
Format String
#include <stdio.h>
int main(int argc, char **argv){
char foo[3] ="AAA";
foo[4]='B' //This is bad.
return 0;
}
Arbitrary Write - Targets
The goal is to use the primitive to overwrite a critical function
pointer and point it at our own code.
Overwrite return address
Overwrite a Vtable pointer in C++ programs
overwrite a method pointer in a struct
Modify a libc pointer in GOT - swap atoi with a pointer to
system()?
Pointer Misuse
Memory management is complex.
Doing it wrong means that the program can look in the wrong place
for critical values
use of an incorrect value leads to modification of code flow
Use After Free
Null Pointer Dereference
use of uninitialised memory
...type confusion
Complexities
Sometimes it's not straight forward
Undefined behaviour can chain
Consider:
correctly bounds checked buffer write (no overflow)
Math in the malloc
Security review sees bounds checked buffer = secure
but what if overflow in the math?
less memory allocated than expected
integer overflow -> buffer overflow
The Bug Classes
Lets explore how undefined behvaiour in each class leads to a
primitive
Buffer Overflow
The most basic
Occurs when a buffer is too small for a copy operation
Can occur on the stack or the heap
Stack target is to write beyond bounds and overwrite return
address
Heap target is to overwite heap metadata which leads to 4 byte
write on free()
Heap - "Smashing the stack for fun and profit - Phrack, 1998
Heap - "Once upon a free() - Phrack, 2001
culprits
strcpy/strncpy
memcpy
memset
bzero
Format String
Abuse of printf()'s argument parsing
printf() converts c types into string representation (%s, %d , %x,
%n)
variable number of arguments
int printf(const char *format, ...);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *str, const char *format, ...);
When no format string is passed, and the argument is attacker
controlled, we can write arbitrary data with %n
printf(somevar) //no format provided!
Integer Overflow/Underflow
Not directly memory corruption in itself
Can be abused to trigger other bugs when performing:
Allocations - less memory than expected?
Performing writes in a "for" loop
Reference counting - more on this later
Double Free
The heap stores all data in a doubly linked list structure
Calling Free() on a heap allocated pointer triggers an unlink() macro
unlink uses metadata values stored around the buffer to remove the
item from the linked list
This involves "stitching" the gap closed if you remove an item from
the middle of the list
Closing the gap means writing the metadata values at each "open
end" of the list
Doing this twice on the same value causes unlink to write these
values to places that dont require stitching
Normally just DOS, but can lead to a use after free
Use After Free
Occurs when you try and use a heap object that has been free()'d
Is exploitable because the heap tries to be efficient with similarly
sized objects
Exploitation path to primitive is as follows:
Program allocates complex FOO object of size A
Program free()s FOO, but keeps a reference to it
Attacker causes code flow which allocates objects BAR also of size
A
Efficient heap stores BAR in the same place that FOO was
Attacker causes program to call a function on what it thinks is FOO
Function pointer is attacker controlled because he created BAR
Type Confusion
The most versatile of memory corruption bugs.
Occurs when application thinks a object is of one type, but it is really
another
Exploitation path to primitive is as follows:
Program has objects FOO and BAR:
struct FOO {
char * somestring;
void * somefunction;
}
struct BAR {
void * somefunction
char * somestring;
}
Type Confusion
struct FOO {
char ** somestring;
void * somefunction;
}
struct BAR {
void * somefunction
char ** somestring;
}
Attacker uses bug to get two references to the same object
One as FOO, and one as BAR they can read and write arbitrarily
eg: execute arbitrary memory:
set memory address by using FOO->somestring = XXXX
This will set BAR->somefunction value to XXXX
BAR->somefunction()
Examples
Questions?