Load-time Hacking using LD_PRELOAD

24
Load-time Hacking using LD_PRELOAD Dharmalingam Ganesan ([email protected]) Itzik Kotler ([email protected]) 1

description

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.

Transcript of Load-time Hacking using LD_PRELOAD

Page 2: Load-time Hacking using LD_PRELOAD

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

Page 3: Load-time Hacking using LD_PRELOAD

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;

}

Page 4: Load-time Hacking using LD_PRELOAD

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!”

Page 5: Load-time Hacking using LD_PRELOAD

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

Page 6: Load-time Hacking using LD_PRELOAD

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

Page 7: Load-time Hacking using LD_PRELOAD

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!

Page 8: Load-time Hacking using LD_PRELOAD

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

Page 9: Load-time Hacking using LD_PRELOAD

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

Page 10: Load-time Hacking using LD_PRELOAD

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

Page 11: Load-time Hacking using LD_PRELOAD

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?

Page 12: Load-time Hacking using LD_PRELOAD

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

Page 13: Load-time Hacking using LD_PRELOAD

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

Page 14: Load-time Hacking using LD_PRELOAD

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)

Page 15: Load-time Hacking using LD_PRELOAD

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

Page 16: Load-time Hacking using LD_PRELOAD

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

Page 17: Load-time Hacking using LD_PRELOAD

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

Page 18: Load-time Hacking using LD_PRELOAD

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!

Page 19: Load-time Hacking using LD_PRELOAD

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" );

Page 20: Load-time Hacking using LD_PRELOAD

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

Page 21: Load-time Hacking using LD_PRELOAD

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

Page 22: Load-time Hacking using LD_PRELOAD

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

Page 23: Load-time Hacking using LD_PRELOAD

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

Page 24: Load-time Hacking using LD_PRELOAD

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