a single gadget weird machine Framing Signals a return to portable - - PowerPoint PPT Presentation

a single gadget weird machine
SMART_READER_LITE
LIVE PREVIEW

a single gadget weird machine Framing Signals a return to portable - - PowerPoint PPT Presentation

a single gadget weird machine Framing Signals a return to portable shellcode Erik Bosman and Herbert Bos stack buffer overflow stack return addr buffer sp 1 stack buffer overflow stack return addr buffer sp 1 stack buffer overflow


slide-1
SLIDE 1

a single gadget weird machine

Framing Signals a return to portable shellcode

Erik Bosman and Herbert Bos

slide-2
SLIDE 2

stack buffer overflow stack sp

return addr buffer

1

slide-3
SLIDE 3

stack buffer overflow stack sp

return addr buffer

1

slide-4
SLIDE 4

stack buffer overflow stack sp

return addr buffer

1

slide-5
SLIDE 5

stack buffer overflow stack sp

return addr buffer

1

slide-6
SLIDE 6

stack buffer overflow stack sp

return addr buffer

1

slide-7
SLIDE 7

stack buffer overflow stack sp

return addr buffer

1

slide-8
SLIDE 8

stack sp

return addr buffer

2

slide-9
SLIDE 9

stack sp

return addr buffer

code

2

slide-10
SLIDE 10

return oriented programming / ret2libc

stack

return addr buffer

code

2

slide-11
SLIDE 11

return addr buffer

code

return addr return addr

2

slide-12
SLIDE 12

Return Oriented Programming

  • dependent on available gadgets
  • chains may differ greatly between

different binaries

  • non-trivial to program
  • ASLR makes it harder to guess gadgets

without an info-leak

3

slide-13
SLIDE 13

Sigreturn Oriented Programming

  • minimal number of gadgets
  • constructing shellcode by

chaining system calls

  • easy to change functionality of

shellcode

  • gadgets are always present

4

slide-14
SLIDE 14

unix signals stack sp

5

slide-15
SLIDE 15

unix signals stack sp

5

slide-16
SLIDE 16

unix signals stack sp

ucontext

5

slide-17
SLIDE 17

unix signals stack sp

ucontext siginfo

5

slide-18
SLIDE 18

unix signals stack sp

ucontext sigreturn siginfo

5

slide-19
SLIDE 19

unix signals stack sp

ucontext sigreturn siginfo good: kernel agnostic about signal handlers

5

slide-20
SLIDE 20

unix signals stack sp

ucontext sigreturn siginfo bad: kernel agnostic about signal handlers (we can fake 'em)

5

slide-21
SLIDE 21

two gadgets

  • call to sigreturn
  • syscall & return

6

slide-22
SLIDE 22

forged signal frame sigreturn

7

slide-23
SLIDE 23

forged signal frame sigreturn program counter

7

slide-24
SLIDE 24

forged signal frame sigreturn program counter stack pointer

7

slide-25
SLIDE 25

sigreturn program counter stack pointer RAX RDX R10 R8 R9 RDI RSI ... ...

7

slide-26
SLIDE 26

sigreturn program counter stack pointer syscall number arg3 arg4 arg5 arg6 arg1 arg2 ... ...

7

slide-27
SLIDE 27

sigreturn syscall & return stack pointer syscall number arg3 arg4 arg5 arg6 arg1 arg2 ... ...

7

slide-28
SLIDE 28

sigreturn syscall & return next sigframe syscall number arg3 arg4 arg5 arg6 arg1 arg2 ... ...

7

slide-29
SLIDE 29

syscall(...) next

8

slide-30
SLIDE 30

socket() bind() listen() accept() execve() 9

slide-31
SLIDE 31

clone(...) parent

10

slide-32
SLIDE 32

clone(...) parent child

10

slide-33
SLIDE 33

clone() (wait)

11

slide-34
SLIDE 34

usage scenarios

  • stealthy backdoor
  • code signing circumvention
  • generic shellcode for exploitation

12

slide-35
SLIDE 35

usage scenarios

  • code signing circumvention
  • generic shellcode for exploitation

stealthy backdoor

12

slide-36
SLIDE 36

stealthy backdoor

basic idea:

  • use the inotify API to wait for a file

to be read

  • when this file is read: open a listen

socket to spawn a shell

  • terminate the listening socket quickly

if nobody connects

13

slide-37
SLIDE 37

close() inotify_init() inotify_add_watch() read() clone()

backdoor

14

slide-38
SLIDE 38

close() alarm() close() close() socket() setsockopt() bind() listen() accept() dup2() alarm() execve() inotify_init() inotify_add_watch() read() clone()

backdoor

14

slide-39
SLIDE 39

usage scenarios

  • stealthy backdoor
  • generic shellcode for exploitation

code signing circumvention

15

slide-40
SLIDE 40

code signing circumvention

  • serialize system calls over a socket
  • write into our own signal frames

useful to bypass code-signing restrictions

16

slide-41
SLIDE 41

read() read() read() read()

system call proxy

17

slide-42
SLIDE 42

read() read() read() read() ???() syscall nr arg1 arg2 ...

system call proxy

18

slide-43
SLIDE 43

and... It's turing complete

19

slide-44
SLIDE 44

usage scenarios

generic shellcode for exploitation

  • stealthy backdoor
  • code signing circumvention
  • 20
slide-45
SLIDE 45

SROP exploit on x86-64

we have:

  • a stack buffer overflow
  • not a single gadget from the binary

assumption:

  • we can guess/leak the location
  • f a writable address (any address!)
  • we have some control over RAX

(function's return value)

21

slide-46
SLIDE 46

two gadgets

  • call to sigreturn
  • syscall & return

22

slide-47
SLIDE 47

two gadgets

  • call to sigreturn: RAX = 15 + syscall
  • syscall & return

22

slide-48
SLIDE 48
  • ne gadget
  • RAX = 15
  • syscall & return

22

slide-49
SLIDE 49

[vsyscall]

23

slide-50
SLIDE 50

[vsyscall]

ffffffffff600000 48 c7 c0 60 00 00 00 0f 05 c3 cc cc cc cc cc cc gettimeofday() fffffffffff60010 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc * ffffffffff600400 48 c7 c0 c9 00 00 00 0f 05 c3 cc cc cc cc cc cc time() ffffffffff600410 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc * ffffffffff600800 48 c7 c0 35 01 00 00 0f 05 c3 cc cc cc cc cc cc getcpu() ffffffffff600810 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc * ffffffffff601000

24

slide-51
SLIDE 51

[vsyscall]

ffffffffff600000 48 c7 c0 60 00 00 00 0f 05 c3 cc cc cc cc cc cc gettimeofday() fffffffffff60010 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc * ffffffffff600400 48 c7 c0 c9 00 00 00 0f 05 c3 cc cc cc cc cc cc time() ffffffffff600410 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc * ffffffffff600800 48 c7 c0 35 01 00 00 0f 05 c3 cc cc cc cc cc cc getcpu() ffffffffff600810 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc * ffffffffff601000

24

slide-52
SLIDE 52

[vsyscall]

ffffffffff600000 48 c7 c0 60 00 00 00 0f 05 c3 cc cc cc cc cc cc gettimeofday() fffffffffff60010 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc * ffffffffff600400 48 c7 c0 c9 00 00 00 0f 05 c3 cc cc cc cc cc cc time() ffffffffff600410 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc * ffffffffff600800 48 c7 c0 35 01 00 00 0f 05 c3 cc cc cc cc cc cc getcpu() ffffffffff600810 cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc * ffffffffff601000

0f05 syscall c3 return

24

slide-53
SLIDE 53

syscall(arg1, arg2, arg3, ...) = result

24

slide-54
SLIDE 54

execve("/bin/sh", ["/bin/sh", "-c", "...", NULL], NULL)

24

slide-55
SLIDE 55

execve("/bin/sh", ["/bin/sh", "-c", "...", NULL], NULL)

24

slide-56
SLIDE 56

syscall(arg1, arg2, arg3, ...) = result

24

slide-57
SLIDE 57

read(fd, addr, ...) = result

24

slide-58
SLIDE 58

read(fd, stack_addr, ...) = result

24

slide-59
SLIDE 59

read(fd, stack_addr, 306) = 306

24

slide-60
SLIDE 60

read(fd, stack_addr, 306) = 306 RAX == 306 == __NR_syncfs

24

slide-61
SLIDE 61

read(fd, stack_addr, 306) = 306 RAX == 306 == __NR_syncfs top of stack points to syscall & return

24

slide-62
SLIDE 62

read(fd, stack_addr, 306) = 306 syncfs(fd) = ... RAX == 306 == __NR_syncfs top of stack points to syscall & return

24

slide-63
SLIDE 63

read(fd, stack_addr, 306) = 306 syncfs(fd) = 0 RAX == 306 == __NR_syncfs top of stack points to syscall & return

24

slide-64
SLIDE 64

read(fd, stack_addr, 306) = 306 syncfs(fd) = 0 RAX == 306 == __NR_syncfs top of stack points to syscall & return RAX == 0 == __NR_read top of stack points to syscall & return

24

slide-65
SLIDE 65

read(fd, stack_addr, 306) = 306 syncfs(fd) = 0 RAX == 306 == __NR_syncfs top of stack points to syscall & return RAX == 0 == __NR_read top of stack points to syscall & return read(fd, stack_addr, 306) = ...

24

slide-66
SLIDE 66

read(fd, stack_addr, 306) = 306 syncfs(fd) = 0 RAX == 306 == __NR_syncfs top of stack points to syscall & return RAX == 0 == __NR_read top of stack points to syscall & return read(fd, stack_addr, 306) = 15

24

slide-67
SLIDE 67

read(fd, stack_addr, 306) = 306 syncfs(fd) = 0 RAX == 306 == __NR_syncfs top of stack points to syscall & return RAX == 0 == __NR_read top of stack points to syscall & return read(fd, stack_addr, 306) = 15 RAX == 15 == __NR_rt_sigreturn top of stack points to syscall & return

24

slide-68
SLIDE 68

read(fd, stack_addr, 306) = 306 syncfs(fd) = 0 RAX == 306 == __NR_syncfs top of stack points to syscall & return RAX == 0 == __NR_read top of stack points to syscall & return read(fd, stack_addr, 306) = 15 RAX == 15 == __NR_rt_sigreturn top of stack points to syscall & return mprotect(stack_addr, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC)

24

slide-69
SLIDE 69

read(fd, stack_addr, 306) = 306 syncfs(fd) = 0 RAX == 306 == __NR_syncfs top of stack points to syscall & return RAX == 0 == __NR_read top of stack points to syscall & return read(fd, stack_addr, 306) = 15 RAX == 15 == __NR_rt_sigreturn top of stack points to syscall & return mprotect(stack_addr, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC) top of stack points to our code

24

slide-70
SLIDE 70

CVE-2012-5976 (asterisk)

25

slide-71
SLIDE 71

On some systems SROP gadgets are randomised, on others, they are not

Operating system Gadget Memory map Linux i386 sigreturn [vdso] Linux < 3.11 ARM sigreturn [vectors] Linux < 3.3 x86-64 syscall & return [vsyscall] Linux ≥ 3.3 x86-64 syscall & return Libc Linux x86-64 sigreturn Libc FreeBSD 9.2 x86-64 sigreturn 0x7ffffffff000 Mac OSX x86-64 sigreturn Libc iOS ARM sigreturn Libsystem iOS ARM syscall & return Libsystem 0xffff0000 0xffffffffff600000

26

slide-72
SLIDE 72

On some systems SROP gadgets are randomised, on others, they are not

Operating system Gadget Memory map Linux i386 sigreturn [vdso] Linux < 3.11 ARM sigreturn [vectors] Linux < 3.3 x86-64 syscall & return [vsyscall] Linux ≥ 3.3 x86-64 syscall & return Libc Linux x86-64 sigreturn Libc FreeBSD 9.2 x86-64 sigreturn 0x7ffffffff000 Mac OSX x86-64 sigreturn Libc iOS ARM sigreturn Libsystem iOS ARM syscall & return Libsystem 0xffff0000 0xffffffffff600000

non-ASLR :-( android

26

slide-73
SLIDE 73

questions?

27

slide-74
SLIDE 74

mitigation:

It may be useful to disable vsyscall vsyscall=emulate (default from Linux 3.3 onward)

  • r

vsyscall=none

slide-75
SLIDE 75

mitigation:

  • Signal frame canaries
slide-76
SLIDE 76

stack canary stack sp

return addr buffer

slide-77
SLIDE 77

stack canary stack sp

return addr buffer

slide-78
SLIDE 78

sigreturn program counter stack pointer RAX RDX R10 R8 R9 RDI RSI ... ...

slide-79
SLIDE 79

sigreturn program counter stack pointer RAX RDX R10 R8 R9 RDI RSI ... ...

slide-80
SLIDE 80

mitigation:

  • Signal frame canaries
slide-81
SLIDE 81

mitigation:

  • Signal frame canaries
  • Counting signals in progress
slide-82
SLIDE 82

CVE-2012-5976 (asterisk)

stack sp stack sp

slide-83
SLIDE 83

CVE-2012-5976 (asterisk)

stack sp stack sp alloca

slide-84
SLIDE 84

CVE-2012-5976 (asterisk)

stack sp stack sp alloca

slide-85
SLIDE 85

dispatch jmp dispatch load CODE jump cond jump P = P + c *P = *P + c *P=getchar() putchar(*P) store exit

slide-86
SLIDE 86

code = open("/proc/self/mem",O_RDWR); p = open("/proc/self/mem",O_RDWR); a = open("/proc/self/mem",O_RDWR);

slide-87
SLIDE 87

code = open("/proc/self/mem",O_RDWR); p = open("/proc/self/mem",O_RDWR); a = open("/proc/self/mem",O_RDWR); instruction dispatch: read(code, &ucontext.sp, sizeof(long));

slide-88
SLIDE 88

code = open("/proc/self/mem",O_RDWR); p = open("/proc/self/mem",O_RDWR); a = open("/proc/self/mem",O_RDWR); instruction dispatch: read(code, &ucontext.sp, sizeof(long)); pointer ops: p++ -> lseek(p, 1, SEEK_CUR);

slide-89
SLIDE 89

code = open("/proc/self/mem",O_RDWR); p = open("/proc/self/mem",O_RDWR); a = open("/proc/self/mem",O_RDWR); instruction dispatch: read(code, &ucontext.sp, sizeof(long)); pointer ops: p++ -> lseek(p, 1, SEEK_CUR); addition: lseek(a, &identity_table_x2, SEEK_SET); lseek(a, val1, SEEK_SET); lseek(a, val2, SEEK_SET); read(a, dest, 1);