Format String Protection David Brumley Sam Wu June 12 th, 2002.

22
Format String Protection David Brumley Sam Wu June 12 th , 2002
  • date post

    21-Dec-2015
  • Category

    Documents

  • view

    216
  • download

    0

Transcript of Format String Protection David Brumley Sam Wu June 12 th, 2002.

Format String Protection

David BrumleySam Wu

June 12th, 2002

Format String Attack Basis Similar to buffer overflow attacks, it

relies on altering flow of control to attacking code

Unlike buffer overflow attacks, it takes advantage of C variable argument macros e.g. printf(“%d %n”, a, b) vs

printf(“%d %n”, a)

Expected Contributions of our proposal

A Metric: Previous work has been ad-hoc, and does not generalize

well. General Variatic Solutions:

We propose a generalized solution to dealing with variatic arguments that rely upon ideas first investigated by StackGuard.

Application Specific Protection: We detail statically inserting dynamic checks into format

string specifiers. Thus, coders who don’t want to pay the price of full pointer safety can pay a smaller price for a different safety guarantee.

Actual arguments

Saved IP for main

Saved FP for main

a

Foo’s frame:

Actual arguments

Custom IP (“/bin/sh”)

Saved FP for main

a

Metric: Different Levels of Attacks Level 1: Common

misuse of printf like functions to rewrite return address or frame pointer

e.g. a simplified versionfoo() {

int a;printf(“%d %d %n”);

}main() {

foo();}

-Also possible: double return, multiple returns attack

Guarding Level 1 Attacks Statically:

Type inferencing: Tainted/untainted

analysis Dynamically:

StackGuard: protect the saved

IP and FP from being overwritten by using a random canary

However…….

a

Actual arguments

Saved IP

Canary

Saved FP

with StackGuard…

“Hello World”

Actual arguments

Saved IP

Saved FP

(buf2[5])

“%d %n”

Actual arguments

Saved IP

Saved FP

“abcde”

“%d %n”

Actual arguments

Crafted IP

Saved FP

“abcde”

Level 2 Attacks Also called a “hybrid

attack” Using buffer overflow to

taint the format string specifier

e.g.int main(int argc, char argv[])

{char buf1[] = “Hello World”;char buf2[5];strcpy(buf2, argv[1]);printf(buf1);

}Tainted/untainted analysis says fine

How StackGuard fails… Consider just a

simple format string: printf(“%d %d

%d %n”, a, b, c);

Canary

Actual arguments

Saved IP

Saved FP

int

In theory, you can have any number of % directivesthat eventually would lead you to write anabitrary location on the stack frame…

Canary

Actual arguments

Crafted IP

Saved FP

int

Guarding Level 2 Attacks LibSafe:

Intercepts most library calls and makes sure all buffer writes are within frame bounds

e.g. strcpy, strcat, getwd, gets, fscanf, scanf, realpath, sprintf…

LibVerify: Wraps all functions such

that the integrity of the return address is checked upon function entry and exit

argc, argv[]

Saved IP

Saved FP

Locals for Main

Actual Arguments

IP for Main

FP for Main

Locals for Foo

main:

foo:LibSafe:Writable area

Locals for Main

Actual Arguments

LibVerify:Check integrity

IP for Main

FP for Main

Still yet….

p

argv[]

Saved IP

Saved FP

(buf[30])

p’

argv[]

Saved IP

Saved FP

(buf[30])

Level 3 Attacks An attacker can

construct an arbitrary pointer during run-time

e.g.int func(char argv[]) {

char *p;char buf[30];p = buf;printf(arg[1]);strncpy(p, arg[2], 29);

}

-can set p’ points to the global offset table (GOT) and change printf() to system(), so executing printf(“/bin/sh”) becomes system(“/bin/sh”)

-demonstrates the ability to alter program control flow to an arbitrary execution point

Guarding Level 3 Attacks

However, in the context of dynamic format string, which allows overwriting of (almost) any arbitrary memory location, point-to analysis cannot conclude anything, since any pointer could be rewritten to point to any other data in memory

Bottom line: Dynamic checking is necessary for ensuring

security integrity

Requires full inter-procedural point-to analysis

A Possible Exploit (where LibSafe failed…)

void foo(int c, char *s, …) {int i;char *j;va_list va;

va_start(va, s);j = va_arg(va, char*);printf(“j: %#x @ %#x\n”, *j, j);printf(“abcd: %d%n\n”, 2, j);va_end(va);

}

int main(int argc, char **argv) {int x=0;printf(“before x: %#x @ %#x\n”, x, &x);foo(4, argv[1]);printf(“after x: %#x @ %#x\n”, x, &x);return 0;

}Running yields:[root]./a.outbefore x: 0 @ 0xbffffa04j: 0 @ 0xbffffa04abcd: 2after x: 0x7 @ 0xbffffa04

A Possible Exploit (cont’d)void foo(int c, char *s, …) {

int i;char *j;va_list va;va_start(va, s);j = va_arg(va, char*);printf(“j: %#x @ %#x\n”, *j, j);printf(“abcd: %d%n\n”, 2, j);va_end(va);

}

int main(int argc, char **argv) {int x=0;printf(“before x: %#x @ %#x\n”, x, &x);foo(4, argv[1]);printf(“after x: %#x @ %#x\n”, x, &x);return 0;

}

argc, **argv

Saved IP

Saved FP

x = 0

main:

s = &(argv[1])

c = 4

IP for Main

FP for Main

foo:

Locals for Foo

argc, **argv

Saved IP

Saved FP

x = 7

main:

va

Locals for Foo

j

Recall: the va_list mechanism Recall (as defined in stdarg.h):

void va_start(va_list ap, arg) Sets up ap to point to first variatic argument (aka.

anonymous arguments) placed upon the stack type va_arg(va_list ap, type)

Returns current values pointed to by ap, then advanc ap by sizeof(type)

void va_end(va_list ap) Cleans up ap

This mechanism allows one function body to work with multiple arguments without code duplication.

Our Proposal

Our motivations and goals: To fix variatic functions such that the program is able

to determine at run time where the end of the variatic arguments actually are.

Finer grained than libsafe because it protects against local variables that may be involved in a level 3 attack (recall previous exploit)

Provide a richer functionality than FormatGuard by eliminating attacks in functions that take va_list

Previous work: Has not solved the generic variatic argument and

va_list problem; they instead focused on eliminating format string exploits found in practice

Three potential solutions…

Canary Protection Canary protection:

Push onto the stack a canary value before pushing the function actuals onto the stack. Thus function arguments are guarded up the stack by the canary, and down the stack by the start of the anonymous arguments (i.e. what returned by va_start)

Since both values are known at compile time, the va_arg macro can be modified to insert a dynamic check to make sure all accesses are within these bounds

Canary Protection (cont’d) Example pseudo code:

#define va_arg(x, type) tmp = real_va_arg(x, type)

if (tmp == canary) {set perm errorreturn error

}else

return tmp

Locals

Canary

Actual arguments

Saved FP

Saved IP

Cons :the false canary problem:two pointers check per va_arg

Pros :access via format string (va_list ultimately) is guarded to only the actuals

Add total size of variatic args Redefine variatic arguments to include a total

size of arguments passed to the function as the first paramter

For va_start, this means also returns the total number of bytes in the va_arg

Add total size of variatic args (cont’d)

Pseudo code:#define va_start(x, y) x = va_list(y); va_arg(y);

$non_anonymous_args

3

2

$total_size

1

Pros :allows precise bounding of the anonymous argument total size :no false canary problem

Cons :recompilation of source, as stack offset is now changed

Reorder the stack Change function invocation to push the

functions actuals onto the stack after the saved IP and FP

Pros :one check per va_arg access

Cons :does not protect local variables from misuse

Plans of Actions Require

a protected libc implementation a compiler that emits the proper code

for the bounds check; which we believe to be a medium-hard fix to gcc

a set of tests indicating the performance difference between the three approaches

Related Work Type Qualifiers (e.g. tainted/untainted static analysis)

Detecting possible flow of tainted buffer to supposedly untainted buffer

Require manual annotations and access to source code FormatGuard

Statically insert code to check dynamically number of % directives with the number of actual arguments to printf functions

Does not support vprintf like functions (or functions that use va_list), does not handle pointer to printf functions, does not check type of % directives

Metal Static analysis to find bugs in program Unsound and incomplete: finding bugs is important to security

applications, but is orthogonal to insuring security safety

Related Work (cont’d) StackGuard

Ensure return address integrity by using a random canary Run time overhead and require access to source code, and

possibly recompilation LibSafe

Intercepting and redirecting common string functions to safe versions, thus enforcing buffer write within stack frame bound

Does not prevent the exploit previously presented Fails to protect programs compiled with -fomit-frame-pointer or -

static, functions not defined in libsafe, kernel code, programs that do not have return address followed by frame pointer

LibVerify Wrap all functions and compare return address on function entry

and exit to ensure integrity Require duplication for each function defined (modified function

stored on heap)