Upload
others
View
3
Download
0
Embed Size (px)
Citation preview
Format string vulnerabilities
(Take 2)
1Tuesday, September 25, 2012
Goal
• Take control of the program (as usual)
• How?
• Write4 (write 4 bytes to an arbitrary location)
• Inject shellcode (or other exploits) into the process
2Tuesday, September 25, 2012
What should we overwrite?
• Saved instruction pointer (seip)
• Other pointers to code (we’ll come back to this)
3Tuesday, September 25, 2012
The way snprintf() normally works
void foo(int w) {char buf[500];const char *ending = w==1? “”:”s”;snprintf(buf, 500, “Hello %d world%s”,
w, ending);}…foo(5);
w: 5seipsebpbuf____________________ending
5
500
seip
format string
“s”
next arg
4Tuesday, September 25, 2012
The way snprintf() normally works
void foo(int w) {char buf[500];const char *ending = w==1? “”:”s”;snprintf(buf, 500, “Hello %d world%s”,
w, ending);}…foo(5);
w: 5seipsebpbuf________________H___ending
5
500
seip
format string
“s”
next arg
5Tuesday, September 25, 2012
The way snprintf() normally works
void foo(int w) {char buf[500];const char *ending = w==1? “”:”s”;snprintf(buf, 500, “Hello %d world%s”,
w, ending);}…foo(5);
w: 5seipsebpbuf________________He__ending
5
500
seip
format string
“s”
next arg
6Tuesday, September 25, 2012
The way snprintf() normally works
void foo(int w) {char buf[500];const char *ending = w==1? “”:”s”;snprintf(buf, 500, “Hello %d world%s”,
w, ending);}…foo(5);
w: 5seipsebpbuf________________Hel_ending
5
500
seip
format string
“s”
next arg
7Tuesday, September 25, 2012
The way snprintf() normally works
void foo(int w) {char buf[500];const char *ending = w==1? “”:”s”;snprintf(buf, 500, “Hello %d world%s”,
w, ending);}…foo(5);
w: 5seipsebpbuf________________Hellending
5
500
seip
format string
“s”
next arg
8Tuesday, September 25, 2012
The way snprintf() normally works
void foo(int w) {char buf[500];const char *ending = w==1? “”:”s”;snprintf(buf, 500, “Hello %d world%s”,
w, ending);}…foo(5);
w: 5seipsebpbuf____________o___Hellending
5
500
seip
format string
“s”
next arg
9Tuesday, September 25, 2012
The way snprintf() normally works
void foo(int w) {char buf[500];const char *ending = w==1? “”:”s”;snprintf(buf, 500, “Hello %d world%s”,
w, ending);}…foo(5);
w: 5seipsebpbuf____________o␣__Hellending
5
500
seip
format string
“s”
next arg
10Tuesday, September 25, 2012
The way snprintf() normally works
void foo(int w) {char buf[500];const char *ending = w==1? “”:”s”;snprintf(buf, 500, “Hello %d world%s”,
w, ending);}…foo(5);
w: 5seipsebpbuf____________o␣5_Hellending
5
500
seip
format string
“s”next arg
11Tuesday, September 25, 2012
The way snprintf() normally works
void foo(int w) {char buf[500];const char *ending = w==1? “”:”s”;snprintf(buf, 500, “Hello %d world%s”,
w, ending);}…foo(5);
w: 5seipsebpbuf____________o␣5␣Hellending
5
500
seip
format string
“s”next arg
12Tuesday, September 25, 2012
The way snprintf() normally works
void foo(int w) {char buf[500];const char *ending = w==1? “”:”s”;snprintf(buf, 500, “Hello %d world%s”,
w, ending);}…foo(5);
w: 5seipsebpbuf________w___o␣5␣Hellending
5
500
seip
format string
“s”next arg
13Tuesday, September 25, 2012
The way snprintf() normally works
void foo(int w) {char buf[500];const char *ending = w==1? “”:”s”;snprintf(buf, 500, “Hello %d world%s”,
w, ending);}…foo(5);
w: 5seipsebpbuf________wo__o␣5␣Hellending
5
500
seip
format string
“s”next arg
14Tuesday, September 25, 2012
The way snprintf() normally works
void foo(int w) {char buf[500];const char *ending = w==1? “”:”s”;snprintf(buf, 500, “Hello %d world%s”,
w, ending);}…foo(5);
w: 5seipsebpbuf________wor_o␣5␣Hellending
5
500
seip
format string
“s”next arg
15Tuesday, September 25, 2012
The way snprintf() normally works
void foo(int w) {char buf[500];const char *ending = w==1? “”:”s”;snprintf(buf, 500, “Hello %d world%s”,
w, ending);}…foo(5);
w: 5seipsebpbuf________worlo␣5␣Hellending
5
500
seip
format string
“s”next arg
16Tuesday, September 25, 2012
The way snprintf() normally works
void foo(int w) {char buf[500];const char *ending = w==1? “”:”s”;snprintf(buf, 500, “Hello %d world%s”,
w, ending);}…foo(5);
w: 5seipsebpbuf____d___worlo␣5␣Hellending
5
500
seip
format string
“s”next arg
17Tuesday, September 25, 2012
The way snprintf() normally works
void foo(int w) {char buf[500];const char *ending = w==1? “”:”s”;snprintf(buf, 500, “Hello %d world%s”,
w, ending);}…foo(5);
w: 5seipsebpbuf____ds__worlo␣5␣Hellending
5
500
seip
format string
“s”next arg
18Tuesday, September 25, 2012
The way snprintf() normally works
void foo(int w) {char buf[500];const char *ending = w==1? “”:”s”;snprintf(buf, 500, “Hello %d world%s”,
w, ending);}…foo(5);
w: 5seipsebpbuf____ds␀_worlo␣5␣Hellending
5
500
seip
format string
“s”next arg
19Tuesday, September 25, 2012
Now with %nvoid foo(int w) {
char buf[500];int x;snprintf(buf, 500, “Hello %d world%n”,
w, &x);}…foo(5);
w: 5seipsebpbuf____________________x: _
5
500
seip
format stringnext arg
20Tuesday, September 25, 2012
Now with %nvoid foo(int w) {
char buf[500];int x;snprintf(buf, 500, “Hello %d world%n”,
w, &x);}…foo(5);
w: 5seipsebpbuf________
o␣__Hellx: _
5
500
seip
format stringnext arg
21Tuesday, September 25, 2012
Now with %nvoid foo(int w) {
char buf[500];int x;snprintf(buf, 500, “Hello %d world%n”,
w, &x);}…foo(5);
w: 5seipsebpbuf____________o␣5_Hellx: _
5
500
seip
format string
next arg
22Tuesday, September 25, 2012
Now with %nvoid foo(int w) {
char buf[500];int x;snprintf(buf, 500, “Hello %d world%n”,
w, &x);}…foo(5);
w: 5seipsebpbuf____d___worlo␣5␣Hellx: _
5
500
seip
format string
next arg
23Tuesday, September 25, 2012
Now with %nvoid foo(int w) {
char buf[500];int x;snprintf(buf, 500, “Hello %d world%n”,
w, &x);}…foo(5);
w: 5seipsebpbuf____d___worlo␣5␣Hellx: 13
5
500
seip
format string
next arg
24Tuesday, September 25, 2012
Now with %nvoid foo(int w) {
char buf[500];int x;snprintf(buf, 500, “Hello %d world%n”,
w, &x);}…foo(5);
w: 5seipsebpbuf____d␀__worlo␣5␣Hellx: 13
5
500
seip
format string
next arg
25Tuesday, September 25, 2012
Attacker controlled format string
void foo(const char *evil) {char buf[500];snprintf(buf, 500, evil);
}…foo(“ZZZZ%x%x%x%x%x”);
evilseipsebpbuf____________________
garbage: 117garbage: 10
garbage: 1839garbage: 43
evil500
seip
next arg format string
26Tuesday, September 25, 2012
void foo(const char *evil) {char buf[500];snprintf(buf, 500, evil);
}…foo(“ZZZZ%x%x%x%x%x”);
evilseipsebpbuf________________ZZZZ
garbage: 117garbage: 10
garbage: 1839garbage: 43
evil500
seip
next arg format string
Attacker controlled format string
27Tuesday, September 25, 2012
void foo(const char *evil) {char buf[500];snprintf(buf, 500, evil);
}…foo(“ZZZZ%x%x%x%x%x”);
evilseipsebpbuf____________2b__ZZZZ
garbage: 117garbage: 10
garbage: 1839garbage: 43
evil500
seip
next argformat string
Attacker controlled format string
28Tuesday, September 25, 2012
void foo(const char *evil) {char buf[500];snprintf(buf, 500, evil);
}…foo(“ZZZZ%x%x%x%x%x”);
evilseipsebpbuf________f___2b72ZZZZ
garbage: 117garbage: 10
garbage: 1839garbage: 43
evil500
seip
next arg
format string
Attacker controlled format string
29Tuesday, September 25, 2012
void foo(const char *evil) {char buf[500];snprintf(buf, 500, evil);
}…foo(“ZZZZ%x%x%x%x%x”);
evilseipsebpbuf________fa__2b72ZZZZ
garbage: 117garbage: 10
garbage: 1839garbage: 43
evil500
seip
next arg
format string
Attacker controlled format string
30Tuesday, September 25, 2012
void foo(const char *evil) {char buf[500];snprintf(buf, 500, evil);
}…foo(“ZZZZ%x%x%x%x%x”);
evilseipsebpbuf________fa752b72ZZZZ
garbage: 117garbage: 10
garbage: 1839garbage: 43
evil500
seip
next arg
format string
Attacker controlled format string
31Tuesday, September 25, 2012
void foo(const char *evil) {char buf[500];snprintf(buf, 500, evil);
}…foo(“ZZZZ%x%x%x%x%x”);
evilseipsebpbuf5a5a5a5afa752b72ZZZZ
garbage: 117garbage: 10
garbage: 1839garbage: 43
evil500
seip
next arg
format string
Attacker controlled format string
32Tuesday, September 25, 2012
void foo(const char *evil) {char buf[500];snprintf(buf, 500, evil);
}…foo(“ZZZZ%x%x%x%x%x”);
evilseipsebpbuf5a5a5a5afa752b72ZZZZ
garbage: 117garbage: 10
garbage: 1839garbage: 43
evil500
seip
next arg
format string
‘Z’ = 0x5a
Attacker controlled format string
32Tuesday, September 25, 2012
void foo(const char *evil) {char buf[500];snprintf(buf, 500, evil);
}…foo(“ZZZZ%x%x%x%x%x”);
evilseipsebp
␀___5a5a5a5afa752b72ZZZZ
garbage: 117garbage: 10
garbage: 1839garbage: 43
evil500
seip
next arg
format string
Attacker controlled format string
33Tuesday, September 25, 2012
Overwriting seipvoid foo(const char *evil) {
char buf[500];snprintf(buf, 500, evil);
}…foo(“\xe6\xff\xff\xbf%x%x%x%x%n”);
evilseipsebpbuf____________________
garbage: 117garbage: 10
garbage: 1839garbage: 43
evil500
seip
next arg format string
0xbfffffe6
34Tuesday, September 25, 2012
Overwriting seipvoid foo(const char *evil) {
char buf[500];snprintf(buf, 500, evil);
}…foo(“\xe6\xff\xff\xbf%x%x%x%x%n”);
evilseipsebpbuf____________________
garbage: 117garbage: 10
garbage: 1839garbage: 43
evil500
seip
next arg format string
0xbfffffe6
34Tuesday, September 25, 2012
Overwriting seipvoid foo(const char *evil) {
char buf[500];snprintf(buf, 500, evil);
}…foo(“\xe6\xff\xff\xbf%x%x%x%x%n”);
evilseipsebpbuf________fa752b72____
garbage: 117garbage: 10
garbage: 1839garbage: 43
evil500
seip
next arg
format string
0xbfffffe6
35Tuesday, September 25, 2012
Overwriting seipvoid foo(const char *evil) {
char buf[500];snprintf(buf, 500, evil);
}…foo(“\xe6\xff\xff\xbf%x%x%x%x%n”);
evil12
sebpbuf________fa752b72____
garbage: 117garbage: 10
garbage: 1839garbage: 43
evil500
seip
next arg
format string
0xbfffffe6
36Tuesday, September 25, 2012
Overwriting seipvoid foo(const char *evil) {
char buf[500];snprintf(buf, 500, evil);
}…foo(“\xe6\xff\xff\xbf%x%x%x%x%n”);
evil12
sebpbuf____␀___fa752b72____
garbage: 117garbage: 10
garbage: 1839garbage: 43
evil500
seip
next arg
format string
0xbfffffe6
37Tuesday, September 25, 2012
Picking the bytes to write
• Use %⟨len⟩x to control the length of the output
• Use %hhn to write just the least-significant byte of the length
38Tuesday, September 25, 2012
Almost putting it all together
evil = “⟨address⟩ZZZZ” “⟨address+1⟩ZZZZ” “⟨address+2⟩ZZZZ” “⟨address+3⟩” “%8x%8x…%8x” “%⟨len⟩x%hhn” “%⟨len⟩x%hhn” “%⟨len⟩x%hhn” “%⟨len⟩x%hhn”;
39Tuesday, September 25, 2012
Misaligned buf
• If buf is not 4-byte aligned, prepend 1, 2, or 3 characters to evil
40Tuesday, September 25, 2012
Advantages of format string exploits
• No need to smash the stack (targeted write)
• Avoids defenses such as stack canaries!
• Stack canary is a random word pushed onto the stack that is checked before the function returns
41Tuesday, September 25, 2012
Stack CanariesExample from Target 6:_Z16print_sub_stringRK18SubStringReference:
pushl!! %ebpmovl! ! %esp, %ebppushl!! %ebxsubl! ! $68, %espmovl! ! 8(%ebp), %ebx! // str movl! ! %gs:20, %eax!! // canary!movl! ! %eax, -12(%ebp)!// on stackxorl! ! %eax, %eax…movl! ! -12(%ebp), %eax!// load canaryxorl! ! %gs:20, %eax!! // compare themje!! ! .L13call! ! __stack_chk_fail
.L13:addl! ! $68, %esppopl! ! %ebxpopl! ! %ebpret
strseipsebpsebx____canary
buf
42Tuesday, September 25, 2012
Disadvantages of format string exploits
• Easy to catch so rarer:$ gcc -Wformat=2 f.cf.c: In function ‘main’:f.c:5: warning: format not a string literal and no format arguments
• Tricky to exploit compared to buffer overflows
43Tuesday, September 25, 2012
What else can we overwrite?
• Function pointers
• C++ vtables
• Global offset table (GOT)
44Tuesday, September 25, 2012
Function pointers#include <stdlib.h>#include <stdio.h>
int compare(const void *a, const void *b) { const int *x = a; const int *y = b; return *x - *y;}
int main() { int i; int arr[6] = {2, 1, 5, 13, 8, 4}; qsort(arr, 6, 4, compare); for (i = 0; i < 6; ++i) printf("%d ", arr[i]); putchar('\n'); return 0;}
main:pushl! %ebpmovl! %esp, %ebp…leal! 24(%esp), %esi // arr…movl! $compare, 12(%esp)movl! $4, 8(%esp)movl! $6, 4(%esp)movl! %esi, (%esp)call! qsort
qsort:…call *0x14(%ebp)…
45Tuesday, September 25, 2012
C++ Virtual function tables (vtable)
struct Foo { Foo() { } virtual ~Foo() { } virtual void fun1() { } virtual void fun2() { }};
void bar(Foo &f) { f.fun1(); f.fun2();}
int main() { Foo f; foo(f);}
_Z3barR3Foo: // bar(Foo&)pushl! %ebpmovl! %esp, %ebppushl! %ebxsubl! $20, %espmovl! 8(%ebp), %ebx! // ebx <- arg1movl! (%ebx), %eax!! // eax <- vtablemovl! %ebx, (%esp)!! // (esp) <- thiscall! *8(%eax)!! ! // call virtual functionmovl! (%ebx), %eax!! // eax <- vtablemovl! %ebx, (%esp)!! // (esp) <- thiscall! *12(%eax)! ! // call virtual functionaddl! $20, %esppopl! %ebxpopl! %ebpret
46Tuesday, September 25, 2012
vtable for Foo// Real code_ZN3FooC1Ev:
pushl! %ebpmovl! %esp, %ebpmovl! 8(%ebp), %eaxmovl! $_ZTV3Foo+8, (%eax)popl! %ebpret
_ZTV3Foo:.long! 0.long! _ZTI3Foo.long! _ZN3FooD1Ev.long! _ZN3FooD0Ev.long! _ZN3Foo4fun1Ev.long! _ZN3Foo4fun2Ev
// DemangledFoo::Foo():
pushl! %ebpmovl! %esp, %ebpmovl! 8(%ebp), %eaxmovl! vtable for Foo+8, (%eax)popl! %ebpret
vtable for Foo:.long! 0.long! typeinfo for Foo.long! Foo::~Foo().long! Foo::~Foo().long! Foo::fun1().long! Foo::fun2()
address of vtable+8 stored in first word
of object
47Tuesday, September 25, 2012
Global Offset Table (GOT)
• Contains pointers to code and data in shared libraries
• Library functions aren’t called directly; stub in the Procedure Linkage Table (PLT) called
• E.g., call exit -> call exit@plt
• exit@plt looks up the address of exit in the GOT and jumps to it (not the whole story)
• Overwrite function pointer in GOT48Tuesday, September 25, 2012