Essential C Security 102
Offensive Computer Security
Florida State University
Spring 2014
Outline of Talk
Tool we will be using
A project that visualizes C/C++ to Assembly for you. (use g++ compiler, intel syntax, and no options)
Quite useful for learning this stuff�(also interesting: https://github.com/ynh/cpp-to-assembly)
Memory Allocator
The memory manager on most systems runs as part of the process
Done!
unlinked!
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Allocated chunks
// This moves a chunk from
// the free list, to be used
#define unlink(P, BK, FD) {
FD = P->fd;
BK = P->bk;
FD->bk = BK;
BK->fd = FD;
}
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Free list
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
FD
BK
Memory Allocator
In general requires:
Memory Allocator
Common optimizations:
Doug Lea’s dlmalloc allocator
Basis of Linux mem allocators
Doug Lea’s dlmalloc allocator
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
data | | | | |
| | | | |
Last 4 bytes of user data | ||||
Size of next chunk | 1 |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
| | | | |
| | | | |
Size of this chunk | ||||
Size of next chunk | 0 |
chunk
boundaries
Allocated chunk
Free chunk
Chunk 2
Chunk 1
Doug Lea’s dlmalloc allocator
Each list of free bin has a head:
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
| | | | |
| | | | |
Free chunk
Forward pointer to next chunk | ||||
Back pointer to previous chunk |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
| | | | |
| | | | |
...
How unlinking works
Allocated chunks
// This moves a chunk from
// the free list, to be used
#define unlink(P, BK, FD) {
FD = P->fd;
BK = P->bk;
FD->bk = BK;
BK->fd = FD;
}
//This is from [1]p 184
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Free list
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Say this is P
Allocated chunks
// This moves a chunk from
// the free list, to be used
#define unlink(P, BK, FD) {
FD = P->fd;
BK = P->bk;
FD->bk = BK;
BK->fd = FD;
}
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Free list
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
1)FD = P-> fd;
Allocated chunks
// This moves a chunk from
// the free list, to be used
#define unlink(P, BK, FD) {
FD = P->fd;
BK = P->bk;
FD->bk = BK;
BK->fd = FD;
}
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Free list
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Setting this to FD
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
2)BK = P-> bk;
Allocated chunks
// This moves a chunk from
// the free list, to be used
#define unlink(P, BK, FD) {
FD = P->fd;
BK = P->bk;
FD->bk = BK;
BK->fd = FD;
}
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Free list
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
FD
Setting this to BK
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
3)FD->bk = BK;
Allocated chunks
// This moves a chunk from
// the free list, to be used
#define unlink(P, BK, FD) {
FD = P->fd;
BK = P->bk;
FD->bk = BK;
BK->fd = FD;
}
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Free list
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
FD
BK
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
4)BK->fd = FD;
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Allocated chunks
// This moves a chunk from
// the free list, to be used
#define unlink(P, BK, FD) {
FD = P->fd;
BK = P->bk;
FD->bk = BK;
BK->fd = FD;
}
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Free list
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
FD
BK
Chunk is now Allocated
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Allocated chunks
Things to note:
free() is the
reverse of this
process
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Free list
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
BK
FD
Chunk is now Allocated
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
data | | | | |
data | | | | |
Allocated chunks
free() is the
reverse of this
process
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Free list
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
Size or last 4 bytes of previous | ||||
Size of this chunk | P | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
unused | ||||
Size of this chunk |
BK
FD
Exploring Heap Vulnerabilities
For these examples we’ll use this guy as our friendly guide
How free works (from [1] p186)
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *first, *second, *third;
first = malloc(666);
second = malloc(12);
third = malloc(12);
strcpy(first, argv[1]);
free(first);
free(second);
free(third);
}
Size or last 4 bytes of previous | ||||
Size of this chunk = 672 | P=1 | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=1 | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=1 | |||
data | | | | |
data | | | | |
why 672 not 666?
How free works (from [1] p186)
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *first, *second, *third;
first = malloc(666);
second = malloc(12);
third = malloc(12);
strcpy(first, argv[1]);
free(first);
free(second);
free(third);
}
Size or last 4 bytes of previous | ||||
Size of this chunk = 672 | P=1 | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=1 | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=1 | |||
data | | | | |
data | | | | |
How free works (from [1] p186)
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *first, *second, *third;
first = malloc(666);
second = malloc(12);
third = malloc(12);
strcpy(first, argv[1]);
free(first);
free(second);
free(third);
}
//pseudo code for free()
define free() {
if (next not in use)
consilidate with next;
//(merges with existing chunk on free list)
else
link chunk to free list;
}
Size or last 4 bytes of previous | ||||
Size of this chunk = 672 | P=1 | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=1 | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=1 | |||
data | | | | |
data | | | | |
How free works (from [1] p186)
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *first, *second, *third;
first = malloc(666);
second = malloc(12);
third = malloc(12);
strcpy(first, argv[1]);
free(first);
free(second);
free(third);
}
Checks to see if next chunk is also free
In this case it is in use
So first is just freed up and linked to the free list
The P flag on the next bin (second) is then set to 0
Size or last 4 bytes of previous | ||||
Size of this chunk = 672 | P=1 | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=0 | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=1 | |||
data | | | | |
data | | | | |
How free works (from [1] p186)
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *first, *second, *third;
first = malloc(666);
second = malloc(12);
third = malloc(12);
strcpy(first, argv[1]);
free(first);
free(second);
free(third);
}
Checks to see if next chunk is also free
In this case it is in use
So first is just freed up and linked to the free list
The P flag on the next bin (second) is then set to 0
Size or last 4 bytes of previous | ||||
Size of this chunk = 672 | P=1 | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=0 | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=0 | |||
data | | | | |
data | | | | |
How free works (from [1] p186)
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *first, *second, *third;
first = malloc(666);
second = malloc(12);
third = malloc(12);
strcpy(first, argv[1]);
free(first);
free(second);
free(third);
}
and so on
Note that consolidation may happen, and this is not shown
Size or last 4 bytes of previous | ||||
Size of this chunk = 672 | P=1 | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=0 | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=0 | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk |
How free works (from [1] p186)
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *first, *second, *third;
first = malloc(666);
second = malloc(12);
third = malloc(12);
strcpy(first, argv[1]);
free(first);
free(second);
free(third);
}
What to note:
Size or last 4 bytes of previous | ||||
Size of this chunk = 672 | P=1 | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=0 | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk | ||||
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=0 | |||
Forward pointer to next chunk | ||||
Back pointer to previous chunk |
How free works (from [1] p186)
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *first, *second, *third;
first = malloc(666);
second = malloc(12);
third = malloc(12);
strcpy(first, argv[1]);
free(first);
free(second);
free(third);
}
This metadata
is the target
but we can only hit the
2nd one here
Size or last 4 bytes of previous | ||||
Size of this chunk = 672 | P=1 | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=0 | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=1 | |||
data | | | | |
data | | | | |
Heap Buffer Overflow (from [1] p186)
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *first, *second, *third;
first = malloc(666);
second = malloc(12);
third = malloc(12);
strcpy(first, argv[1]);
free(first);
free(second);
free(third);
}
Size or last 4 bytes of previous | ||||
Size of this chunk = 672 | P=1 | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=1 | |||
data | | | | |
data | | | | |
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=1 | |||
data | | | | |
data | | | | |
Heap Buffer Overflow (from [1] p186)
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *first, *second, *third;
first = malloc(666);
second = malloc(12);
third = malloc(12);
strcpy(first, argv[1]);
free(first);
free(second);
free(third);
}
Size or last 4 bytes of previous | ||||
Size of this chunk = 672 | P=1 | |||
“FREEEEEEEEEEEEDOOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM” | ||||
“FREEEEEEEEEEEEDOOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM”
Heap Buffer Overflow (from [1] p186)
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *first, *second, *third;
first = malloc(666);
second = malloc(12);
third = malloc(12);
strcpy(first, argv[1]);
free(first);
free(second);
free(third);
}
Size or last 4 bytes of previous | ||||
Size of this chunk = 672 | P=1 | |||
“FREEEEEEEEEEEEDOOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM” | ||||
“FREEEEEEEEEEEEDOOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM”
Will cause free(second) �to segfault
Heap Buffer Overflow (from [1] p186)
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *first, *second, *third;
first = malloc(666);
second = malloc(12);
third = malloc(12);
strcpy(first, argv[1]);
free(first);
free(second);
free(third);
}
“FREEEEEEEEEEEEDOOMMMMMMMMMMMMMMMMMMMMMMM…<dummy even integer(to have P=0)><new size (-4)><a fd pointer><a bk pointer>”
will alter the behavior of free()
Size or last 4 bytes of previous | ||||
Size of this chunk = 672 | P=1 | |||
FREEEEEDOOOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM | ||||
dummy size field | P=0 | |||
size of chunk = -4 | P=0 | |||
Malicious fd pointer | ||||
Malicious bk pointer | ||||
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=1 | |||
data | | | | |
data | | | | |
Heap Buffer Overflow (from [1] p186)
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *first, *second, *third;
first = malloc(666);
second = malloc(12);
third = malloc(12);
strcpy(first, argv[1]);
free(first);
free(second);
free(third);
}
Size or last 4 bytes of previous | ||||
Size of this chunk = 672 | P=1 | |||
FREEEEEDOOOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM | ||||
dummy size field | P=0 | |||
size of chunk = -4 | P=0 | |||
Malicious fd pointer | ||||
Malicious bk pointer | ||||
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=1 | |||
data | | | | |
data | | | | |
Size field in second chunk overwritten with a negative number
Heap Buffer Overflow (from [1] p186)
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *first, *second, *third;
first = malloc(666);
second = malloc(12);
third = malloc(12);
strcpy(first, argv[1]);
free(first);
free(second);
free(third);
}
Size or last 4 bytes of previous | ||||
Size of this chunk = 672 | P=1 | |||
FREEEEEDOOOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM | ||||
dummy size field | P=0 | |||
size of chunk = -4 | P=0 | |||
Malicious fd pointer | ||||
Malicious bk pointer | ||||
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=1 | |||
data | | | | |
data | | | | |
Size field in second chunk overwritten with a negative number
Heap Buffer Overflow (from [1] p186)
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *first, *second, *third;
first = malloc(666);
second = malloc(12);
third = malloc(12);
strcpy(first, argv[1]);
free(first);
free(second);
free(third);
}
Size or last 4 bytes of previous | ||||
Size of this chunk = 672 | P=1 | |||
FREEEEEDOOOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM | ||||
dummy size field | P=0 | |||
size of chunk = -4 | P=0 | |||
Malicious fd pointer | ||||
Malicious bk pointer | ||||
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=1 | |||
data | | | | |
data | | | | |
#define unlink(P, BK, FD) {
FD = P->fd;
BK = P->bk;
FD->bk = BK;
BK->fd = FD;
}
need not point to the heap or to the free list!
Heap Buffer Overflow (from [1] p186)
Size or last 4 bytes of previous | ||||
Size of this chunk = 672 | P=1 | |||
FREEEEEDOOOMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM | ||||
dummy size field | P=0 | |||
size of chunk = -4 | P=0 | |||
Malicious fd pointer | ||||
Malicious bk pointer | ||||
Size or last 4 bytes of previous | ||||
Size of this chunk = 16 | P=1 | |||
data | | | | |
data | | | | |
#define unlink(P, BK, FD) {
FD = P->fd;
BK = P->bk;
FD->bk = BK;
BK->fd = FD;
}
The destination of the arbitrary write
The value which to write
When this command runs:
Double free() bug (kinda) does this
Take this guy free() him
free() him again and it produces some messed up zombie state of the former heap
bins...
Double free() Vulnerability
Double free() Vulnerability
Use after free() Vulnerability
Integer Security
Signed vs Unsigned (char == 1 byte)
signed char y;
unsigned char y;
0 0 0 0 0 0 0 1
0 1 1 1 1 1 1 1
1 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1
0 0 1 0 1 0 0 0
1 1 1 1 1 1 1 1
SIGN 64 32 16 8 4 2 1
SIGN 64 32 16 8 4 2 1
SIGN 64 32 16 8 4 2 1
SIGN 64 32 16 8 4 2 1
=1
=127
=-128
=-1
=0
=1
=40
=255
128 64 32 16 8 4 2 1
128 64 32 16 8 4 2 1
128 64 32 16 8 4 2 1
128 64 32 16 8 4 2 1
Truncation Example 1
#include <stdio.h>
void foo()
{
int i = -1;
short x;
x = i;
}
Lets see how this compiles and exactly what happens
Truncation Example 1
x86_64 vs x86_32
Integer Truncation continued
Do not code your applications with the native C/C++ data types that change size on a 64-bit operating system
The 64-bit return value from sizeof in the following statement is truncated to 32-bits when assigned to bufferSize.
int bufferSize = (int) sizeof (something);
The solution is to cast the return value using size_t and assign it to bufferSize declared as size_t as shown below:
size_t bufferSize = (size_t) sizeof (something);
Safe type definitions/functions
Platform Matters (intro)
Platform Matters
The difference among the three 64-bit models (LP64, LLP64, and ILP64) lies in the non-pointer data types.
ILP32 = Microsoft Windows & Most Unix and Unix-like systems @ 32bit
LP64 = Most Unix and Unix-likesystems, e.g. Solaris,Linux, BSD, and OS X;z/OS
LLP64 = Microsoft Windows (x86-64 and IA-64)�ILP64 = HAL Computer Systemsport of Solaris toSPARC64
A side note on Exploit Dev
The size of a struct may change from platform to platform!
struct test {� int i1;� double d;� int i2;� long l;�}
Why does this matter?
Can you find the bug? (60 secs)
__inline__ unsigned long long int rdtsc()�{�#ifdef __x86_64__� unsigned int a, d;� __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d));� return (unsigned long)a | ((unsigned long)d << 32);�#elif defined(__i386__)� unsigned long long int x;� __asm__ volatile ("rdtsc" : "=A" (x));� return x;�#else�#define NO_CYCLE_COUNTER� return 0;�#endif�}
Another Example
The solution is to store pointers as pointer types or the special types defined for this purpose, such as intptr_t and uintptr_t.
Integer “overflow”
image source: wikipedia
Integer “overflow”
Integer “overflow”
image source: wikipedia
Integer “underflow”
Other Integer Nuances
Other Nuances
Integer Promotion / Conversion
Other Integer Nuances
Integer Casting
(unsigned int) -1 becomes UINT_MAX
0 0 0 0 0 0 0 1
0 1 1 1 1 1 1 1
1 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 1
0 0 1 0 1 0 0 0
1 1 1 1 1 1 1 1
SIGN 64 32 16 8 4 2 1
SIGN 64 32 16 8 4 2 1
SIGN 64 32 16 8 4 2 1
SIGN 64 32 16 8 4 2 1
=1
=127
=-128
=-1
=0
=1
=40
=255
128 64 32 16 8 4 2 1
128 64 32 16 8 4 2 1
128 64 32 16 8 4 2 1
128 64 32 16 8 4 2 1
size_t vs ssize_t
size_t = an unsigned int
But stupid things happen:
http://pubs.opengroup.org/onlinepubs/009604499/functions/mbstowcs.html
My buddy Sean @ CMU CERT found this awesome case
Importance of Integer Bugs
Floats
Float variable can be NaN (Not a number)
Float Nuances:
Bug time! (30 Seconds)
// Coefficients USED TO CONVERT FROM RGB TO monochrome.�const uint32 kRedCoefficient = 2125;�const uint32 kGreenCoefficient = 7154;�const uint32 kBlueCoefficient = 0721;�const uint32 kColorCoefficientDenominator = 10000;
This error was found in theChromium project by PVS-Studio C/C++ static code analyzer.
Integer bug resources
Formatted Output Security
Format Strings
Looking at how functions are called
Looking at how functions are called
64 bit
Looking at how functions are called
32 bit (-m32 compiler flag)
Looking at how functions are called
64 bit
Looking at how functions are called
Looking at how functions are called
32 bit
Looking at how functions are called
32 bit
Looking at how functions are called
32 bit
Format Strings
%[flags][width][.precision][{length-modifier}] conversion-specifier
| specifiers | ||||||
length | d i | u o x X | f F e E g G a A | c | s | p | n |
(none) | int | unsigned int | double | int | char* | void* | int* |
hh | signed char | unsigned char | | | | | signed char* |
h | short int | unsigned short int | | | | | short int* |
l | long int | unsigned long int | | wchar_t* | | long int* | |
ll | long long int | unsigned long long int | | | | | long long int* |
j | | | | | |||
z | | | | | |||
t | | | | | |||
L | | | long double | | | | |
Exploiting Format Strings
Back to that example:
gets(buffer); // buffer == “%s%s…”
printf(buffer);
printf(“%s%s%s%s%s%s%s%s%s%s…”);
Exploiting Format Strings
printf(“%08x %08x %08x %08x %08x.…”);
Exploiting Format Strings
printf(“%08x %08x %08x %08x %08x.…”);
Exploiting Format Strings
printf(“%04x.…”);
Exploiting Format Strings
printf(“\xde\xf5\xe5\x04%x%x%x%x%s”);
Exploiting Format Strings
printf(“\xde\xf5\xe5\x04%x%x%x%x%s”);
Exploiting Format Strings
Writing to memory address (from [1] p326)
int i;
printf(“hello%n\n”, (int *)&i);
writes 5 to variable i;
Exploiting Format Strings
Writing to arbitrary memory address
printf(“\xde\xf5\xe5\x04%x%x%x%x%n”);
printf(“\xde\xf5\xe5\x04%x%x%x%150x%n”);
works well for writing small values
Exploiting Format Strings
Writing to arbitrary memory address
printf(“\xde\xf5\xe5\x04%x%x%x%x%n”);
will write the number of characters before the %n printed so far to 04e5f5de.
%[flags][width][.precision][{length-modifier}] conversion-specifier
| specifiers | ||||||
length | d i | u o x X | f F e E g G a A | c | s | p | n |
(none) | int | unsigned int | double | int | char* | void* | int* |
hh | signed char | unsigned char | | | | | signed char* |
h | short int | unsigned short int | | | | | short int* |
l | long int | unsigned long int | | wchar_t* | | long int* | |
ll | long long int | unsigned long long int | | | | | long long int* |
j | | | | | |||
z | | | | | |||
t | | | | | |||
L | | | long double | | | | |
Other modifiers
%[flags][width][.precision][{length-modifier}] conversion-specifier
int i;
printf(“%50u%n”, 1, &i); // i = 50
printf(“%5000u%n”, 1, &i);// i = 5000
Exploiting Format Strings
Combine these techniques to write arbitrary values to arbitrary memory location(s) byte by byte:
We’ll finish
this topic
later in the semester
Memory | 94 | 95 | 96 | 97 | | | |
First write to 0x08409755 | AA | 00 | 00 | 00 | | | |
Second write to 0x08409756 | | BB | 00 | 00 | 00 | | |
Third write to 0x08409757 | | | CC | 00 | 00 | 00 | |
Fourth write to 0x08409758 | | | | DD | 00 | 00 | 00 |
RESULT | AA | BB | CC | DD | | | |
Concurrency & Race Conditions
Concurrency, Parallelism, & Multithreading
Parallelism:
Concurrency:
Multithreading:
3 Properties for Race Conditions [1]
Race Condition explained
concurrency property:
shared object
change state:
Race Condition Bughunting Strategy
Race Condition potential results
Questions?
Reading: 0x280 up to 0x300 (HAOE)
and 0x350 up to 0x400
Great Study Resource: http://q.viva64.com/ �