An approach for load-time hacking using LD_PRELOAD is presented.
We discuss a simple, yet intriguing, strategy for overcoming the limitations discussed in Part 1 (i.e., the first publication given in the reference) of reverse engineering and exploitation using LD_PRELOAD, a dynamic linking technique. In particular, we relax the need for exit(1) in the main function. The essence of the technique is that both the stack pointer (esp) and the base frame pointer (ebp) are carefully adjusted when the wrapper to the library function is called. The proposed solution allows us to safely return to libc after dynamically modifying the control flow in the wrapper to (library) functions.
2. 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;
}
2
What if you do not know the passwd?
Reference: Reverse Engineering with LD_PRELOAD by Itzik Kotler
5. /*
* 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 zeron");
exit(1);
}
5
Can we avoid “Sorry” and print the “On a long…”?
[~]$ ./cerberus
On a long enough timeline, the survival rate
for everyone drops to zero
7. 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!
7
8. /* 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;
}
8
• Why add 7 to eax?
• 0x804840a – 0x8048403
• Why add 28 to esp?
• Answered in next slides
10. 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
10
11. 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
[~]$
11
Why the program is not terminating?
12. Ret2libc
EBP (libc)
17
13
Ptr to printf
input str
ret address
after printf
EBP
ESP
Ret2libc
EBP (libc)
17
13
Ptr to printf
input str
ret address
after printf
EBP
ESP
EBP (main)96
Ret2libc
EBP (libc)
17
13
Ptr to printf
input str
ret address
after printf
EBP
ESP
EBP (main)96
(a). Just before the 2nd printf.
(b). In the wrapper puts. (c). After pointers rewinding.
12
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 printf
input str
ret address
after printf
EBP of puts
(wrapper) 76
ESP
EBP
Ret2libc
EBP (libc)
17
13
Ptr to printf
input str
ret address
after printf
EBP of puts
(wrapper) 76
ESP
EBP
EIP
(d). Inside the real puts. (e). After returning from real puts.
13
76 76
100
96
92
88
84
80
100
96
92
88
84
*80
14. Control comes back to main and will try to
run return 1:
mov %ebp, %esp
pop %ebp
Pop %eip (or ret)
14
15. Ret2libc
EBP (libc)
17
13
Ptr to printf
input str
ret address
after printf
EBP of puts
(wrapper) 76
ESP
EBP
15
76
100
96
92
88
76
80
mov %ebp, %esp
84
16. Ret2libc
EBP (libc)
17
13
Ptr to printf
input str
ret address
after printf
EBP of puts
(wrapper) 76EBP
ESP
16
76
100
96
92
88
80
pop %ebp
84
17. Ret2libc
EBP (libc)
17
13
Ptr to printf
input str
ret address
after printf
EBP of puts
(wrapper) 76EBP
EIP
17
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. 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!
18
21. Ret2libc
EBP (libc)
17
13
Ptr to printf
input str
ret address
after printf
EBP
ESP
EBP (main)
Ret2libc
EBP (libc)
17
13
Ptr to printf
input str
ret address
after printf
EBP, ESP EBP (main)
(a). In the wrapper puts. (b). After ESP rewinding.
Ret2libc
EBP (libc)
17
13
Ptr to printf
input str
ret address
after printfESP
EBP (main)
(c). After pop EBP.
EBP
21
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 printf
input str
ret address
after printf
EBP of main
(96)
ESP
EBP
Ret2libc
EBP (libc)
17
13
Ptr to printf
input str
ret address
after printf
EBP of main
ESP
EBP
EIP
(d). Inside the real puts. (e). After returning from real puts.
22
76
100
96
92
88
84
80
52
76
76
100
96
92
88
84
*80
52
23. 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
23
24. 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
24