Post on 29-Jun-2015
description
transcript
1
Load-time Hacking using LD_PRELOAD
Dharmalingam Ganesan(dganesan@fc-md.umd.edu)
Itzik Kotler(xorninja@gmail.com)
2
Sample C program
int main(int argc, char **argv) { char passwd[] = "foobar";
if (argc < 2) {printf("usage: %s <given-password>\n", argv[0]);return 0;
}
if (!strcmp(passwd, argv[1])) {printf("Green light!\n");return 1;
}
printf("Red light!\n"); return 0;}
What if you do not know the passwd?
Reference: Reverse Engineering with LD_PRELOAD by Itzik Kotler
3
Just hijack the strcmp
/* * strcmp, Fixed strcmp function -- Always equal! */ int strcmp(const char *s1, const char *s2) {
printf("S1 eq %s\n", s1);printf("S2 eq %s\n", s2);
// ALWAYS RETURN EQUAL STRINGS!return 0;
}
4
LD_PRELOAD
gcc -fPIC -c strcmp-hijack.c -o strcmp-hijack.o
gcc -shared -o strcmp-hijack.so strcmp-hijack.o
./strcmp-target redbull Output: “Red light!”
Attack using LD_PRELOAD
LD_PRELOAD="./strcmp-hijack.so" ./strcmp-target redbull
Output: “Green light!”
5
Another Sample – Cool Hacks!/* * cerberus.c, Impossible statement */ #include <stdio.h>
int main(int argc, char **argv) {int a = 13, b = 17;
if (a != b) {printf("Sorry!\n");return 0;
}printf("On a long enough timeline," " the survival rate for everyone drops to zero\n");exit(1);
}
Can we avoid “Sorry” and print the “On a long…”?
[~]$ ./cerberus On a long enough timeline, the survival rate for everyone drops to zero
6
Disassembly of main
080483d4 <main>: 80483d4: 55 push %ebp 80483d5: 89 e5 mov %esp,%ebp 80483d7: 83 e4 f0 and $0xfffffff0,%esp 80483da: 83 ec 20 sub $0x20,%esp 80483dd: c7 44 24 18 0d 00 00 movl $0xd,0x18(%esp) 80483e4: 00 80483e5: c7 44 24 1c 11 00 00 movl $0x11,0x1c(%esp) 80483ec: 00 80483ed: 8b 44 24 18 mov 0x18(%esp),%eax 80483f1: 3b 44 24 1c cmp 0x1c(%esp),%eax 80483f5: 74 13 je 804840a <main+0x36> 80483f7: c7 04 24 f0 84 04 08 movl $0x80484f0,(%esp) 80483fe: e8 ed fe ff ff call 80482f0 <puts@plt> 8048403: b8 00 00 00 00 mov $0x0,%eax 8048408: eb 11 jmp 804841b <main+0x47> 804840a: c7 04 24 f8 84 04 08 movl $0x80484f8,(%esp) 8048411: e8 da fe ff ff call 80482f0 <puts@plt> 8048416: b8 01 00 00 00 mov $0x1,%eax 804841b: c9 leave 804841c: c3 ret
Note: puts is used for printf
7
Strategy for Hacking
Create our own “puts” wrapper
Update the return address after the first puts
Transfer control to the second puts
Embed assembly code and adjust the ESP!
8
Wrapper of puts (megatron.c)/* Pointer to the original puts call */static int (*_puts)(const char *str) = NULL;
int puts(const char *str){ if (_puts == NULL) { _puts = (int (*)(const char *str)) dlsym(RTLD_NEXT, "puts");
// Hijack the RET address and modify it to <main+xx>
__asm__ __volatile__ ( "movl 0x4(%ebp), %eax \n" "addl $7, %eax \n" "movl %eax, 0x4(%ebp)" );
return 1; } __asm__ __volatile__ ( "addl $28, %%esp \n“ “ jmp *%0 \n" : : "g" (_puts) : "%esp" ); return -1;}
• Why add 7 to eax?• 0x804840a – 0x8048403
• Why add 28 to esp?• Answered in next slides
9
Disassembly of wrapper puts00000000 <printf>: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 18 sub $0x18,%esp 6: a1 00 00 00 00 mov 0x0,%eax b: 85 c0 test %eax,%eax d: 75 2a jne 39 <printf+0x39> f: b8 00 00 00 00 mov $0x0,%eax 14: 89 44 24 04 mov %eax,0x4(%esp) 18: c7 04 24 ff ff ff ff movl $0xffffffff,(%esp) 1f: e8 fc ff ff ff call 20 <printf+0x20> 24: a3 00 00 00 00 mov %eax,0x0 29: 8b 45 04 mov 0x4(%ebp),%eax 2c: 83 c0 0f add $0xf,%eax 2f: 89 45 04 mov %eax,0x4(%ebp) 32: b8 01 00 00 00 mov $0x1,%eax 37: eb 00 jmp 39 <printf+0x39> 39: c9 leave 3a: c3 ret
Esp got adjusted:4 bytes (push %ebp)0x18 bytes (sub $0x18, %esp)
Total: 0x18 + 4 = 24 + 4 = 28
10
Output
Create a shared lib of the wrapper: gcc -c -m32 megatron.c -o megatron.o –ldl gcc -shared -o megatron.so megatron.o -
m32 –ldl
export LD_PRELOAD=./megatron.so [~]$ ./cerberus On a long enough timeline, the survival rate for everyone drops to zero
11
Challenge: Replace exit(1) by return(1)
The main function uses exit(1) If we replace it by return(1) and run:
[~]$ gcc -o cerberus cerberus.c -m32[~]$ [~]$ export LD_PRELOAD=./megatron.so[~]$ [~]$ ./cerberus On a long enough timeline, the survival rate for everyone drops to zero
^C[~]$
Why the program is not terminating?
12
Ret2libc
EBP (libc)
17
13
Ptr to printfinput str
ret addressafter printf
EBP
ESP
Ret2libc
EBP (libc)
17
13
Ptr to printfinput str
ret addressafter printf
EBP
ESP
EBP (main)96
Ret2libc
EBP (libc)
17
13
Ptr to printfinput str
ret addressafter printf
EBP
ESP
EBP (main)96(a). Just before the 2nd printf.
(b). In the wrapper puts. (c). After pointers rewinding.
Stack Layouts – Megatron
100
96
92
88
84
80
76
52
80
76
100
96
92
88
84
80
100
96
92
88
84
13
Ret2libc
EBP (libc)
17
13
Ptr to printfinput str
ret addressafter printf
EBP of puts(wrapper) 76
ESP
EBP
Ret2libc
EBP (libc)
17
13
Ptr to printfinput str
ret addressafter printf
EBP of puts(wrapper) 76
ESP
EBP
EIP
(d). Inside the real puts. (e). After returning from real puts.
Stack Layouts – Entry and Exit of Puts
76 76
100
96
92
88
84
80
100
96
92
88
84
*80
14
On ret of real puts
Control comes back to main and will try to run return 1: mov %ebp, %esp pop %ebp Pop %eip (or ret)
15
Ret2libc
EBP (libc)
17
13
Ptr to printfinput str
ret addressafter printf
EBP of puts(wrapper) 76
ESPEBP
Stack Layouts – After Exit of Puts
76
100
96
92
88
76
80
mov %ebp, %esp
84
16
Ret2libc
EBP (libc)
17
13
Ptr to printfinput str
ret addressafter printf
EBP of puts(wrapper) 76EBP
ESP
Stack Layouts – After Exit of Puts
76
100
96
92
88
80
pop %ebp
84
17
Ret2libc
EBP (libc)
17
13
Ptr to printfinput str
ret addressafter printf
EBP of puts(wrapper) 76EBP
EIP
Stack Layouts – After Exit of Puts
76
100
96
92
88
84
ret: pop %eip
ESP
• Now EIP points to leave-ret sequence!
• Never ending because EBP of mains is lost
*80
18
Reason for non-termination
We lost main’s EBP along the way There is an infinite loop when the
control comes to main mov %ebp, %esp pop %ebp Ret (or pop %eip)
Program is not able to return to libc Fix: Why not restore the EBP!
19
New wrapper of puts
OLD
__asm__ __volatile__ ( "addl $28, %%esp \n“ "jmp *%0 \n" : : "g" (_puts) : "%esp" );
NEW
__asm__ __volatile__ ( "addl $24, %%esp \n" "popl %%ebp \n" "jmp *%0 \n" : : "g" (_puts) : "%esp" );
20
Output with the new wrapper
[~]$ export LD_PRELOAD=./megatron.so[~]$ [~]$ ./cerberus On a long enough timeline, the survival rate for everyone drops to zero[~]$ [~]$ echo $?1
21
Ret2libc
EBP (libc)
17
13
Ptr to printfinput str
ret addressafter printf
EBP
ESP
EBP (main)
Ret2libc
EBP (libc)
17
13
Ptr to printfinput str
ret addressafter printf
EBP, ESP EBP (main)
(a). In the wrapper puts. (b). After ESP rewinding.
Ret2libc
EBP (libc)
17
13
Ptr to printfinput str
ret addressafter printfESP
EBP (main)
(c). After pop EBP.
EBP
Stack Layouts – New Megatron
76
100
96
92
88
84
80
52
76
100
96
92
88
84
80
52
76
100
96
92
88
84
80
52
22
Ret2libc
EBP (libc)
17
13
Ptr to printfinput str
ret addressafter printf
EBP of main(96)
ESP
EBP
Ret2libc
EBP (libc)
17
13
Ptr to printfinput str
ret addressafter printf
EBP of main
ESP
EBP
EIP
(d). Inside the real puts. (e). After returning from real puts.
Stack Layouts – Entry and Exit of Puts
76
100
96
92
88
84
80
5276
76
10096
92
88
84
*80
52
23
Summary
LD_PRELOAD is a powerful way to hack Key idea: Wrapper to library functions
Collect data such as input arguments! Modify control flow dynamically
ESP and EBP rewinding is the core concept Try it out yourself Things to keep in mind:
Number of byte adjustments in your wrapper
24
References
Itzik Kotler Reverse Engineering with LD_PRELOAD http://securityvulns.com/articles/reveng/
Dharma Ganesan and Itzik Kotler Reverse Engineering with LD_PRELOAD
(Part 11) Article to be published