1 of 134

A New Method to Bypass 64-bit Linux ASLR

Dr. Hector Marco-Gisber

Dr. Ismael Ripoll-Ripo

Presenter:

Location:

Date:

@aesophor

SQLab, NCTU

Sep. 24, 2020

return-to-csu

2 of 134

Outline

---

0x10 Introduction

0x20 The Attached Code

0x30 Bypassing ASLR on x86_64

0x31 Scenarios where Ret2csu is Useful

0x32 Exploiting a Forking Server� 0x33 pwnable.tw De-ASLR (500 pts)

2

3 of 134

0x10

Introduction

3

4 of 134

Before we jump straight into it ...

4

ASLR

5 of 134

Let’s review the virtual memory layout on 64-bit Linux

5

6 of 134

0x11 Virtual Address Space (x86_64)

---

6

Virtual Address Space

Userspace

Kernel Space

  • In the following context, I’ll abbreviate the term� virtual address space as va_space .

unused

0

264

7 of 134

0x11 Virtual Address Space (x86_64)

---

7

Virtual Address Space

Userspace

Kernel Space

unused

  • In the following context, I’ll abbreviate the term “virtual address space” as “va_space”.

  • The total va_space is divided into

two parts.

0

264

8 of 134

0x11 Virtual Address Space (x86_64)

---

8

Virtual Address Space

unused

Kernel Space

Userspace

  • In the following context, I’ll abbreviate the term “virtual address space” as “va_space”.

  • The total va_space is divided into

two parts.

  • The contents of userspace memory

may change depending on which process is currently running, whereas the contents of the

kernel space will always be the same.

0

264

9 of 134

0x11 Virtual Address Space (x86_64)

---

9

Virtual Address Space

0xffffffffffffffff

0x0000000000000000

0x00007fffffffffff

0xffff800000000000

Userspace

128 TiB

unused

128 TiB

Kernel Space

10 of 134

0x12 Process Memory Layout (x86_64)

---

10

Virtual Address Space

Userspace

Kernel Space

unused

stack

shared

libraries

User Process’s va_space

0xffffffffffffffff

0x0000000000000000

0x00007fffffffffff

0xffff800000000000

0x0000000000000000

0x0000000000400000

0x00007ffffffde000

0x00007ffffffff000

.text

.rodata

.data

heap

.bss

11 of 134

0x12 Process Memory Layout (x86_64)

---

11

  • If libc is loaded at a fixed (known) address, and the libc version installed on the target machine is known, then an attacker

can exploit a buffer overflow vuln and call any function in libc e.g., system@libc(“/bin/sh”); .

stack

User Process’s va_space

.text

.rodata

.data

heap

.bss

shared

libraries

12 of 134

0x12 Process Memory Layout (x86_64)

---

12

  • If libc is loaded at a fixed (known) address, and the libc version installed on the target machine is known, then an attacker

can exploit a buffer overflow vuln and call any function in libc e.g., system@libc(“/bin/sh”); .

stack

shared

libraries

User Process’s va_space

.text

.rodata

.data

heap

.bss

system_address = libc_base_address + offset

libc.so.6

system@libc

13 of 134

0x12 Process Memory Layout (x86_64)

---

13

  • If libc is loaded at a fixed (known) address, and the libc version installed on the target machine is known, then an attacker

can exploit a buffer overflow vuln and call any function in libc e.g., system@libc(“/bin/sh”); .

  • The addresses of attacker-injected data can be easily deduced
  • shellcode
  • ROP chains
  • b‘/bin/sh\x00’
  • ...

stack

shared

libraries

User Process’s va_space

.text

.rodata

.data

heap

.bss

14 of 134

0x12 Process Memory Layout (x86_64)

---

14

  • If libc is loaded at a fixed (known) address, and the libc version installed on the target machine is known, then an attacker

can exploit a buffer overflow vuln and call any function in libc e.g., system@libc(“/bin/sh”); .

  • The addresses of attacker-injected data can be easily deduced
  • shellcode
  • ROP chains
  • b‘/bin/sh\x00’

  • This is where ASLR comes in

stack

shared

libraries

User Process’s va_space

.text

.rodata

.data

heap

.bss

15 of 134

0x13 ASLR (Address Space Layout Randomization)

---

15

  • Places shared libraries, the stack (and optionally the heap) at random locations, making existing vulns more difficult to exploit

stack

shared

libraries

User Process’s va_space

.text

.rodata

.data

heap

.bss

16 of 134

0x13 ASLR (Address Space Layout Randomization)

---

16

stack

shared

libraries

User Process’s va_space

.text

.rodata

.data

heap

.bss

  • Places shared libraries, the stack and (optionally the heap) at random locations, making existing vulns more difficult to exploit
  • On Linux, there are 3 levels of ASLR to choose from
    • level 0 - no randomization
    • level 1 - randomize shared libraries, stack, mmap() .
    • level 2 - randomize shared libraries, stack, mmap(), brk() , heap .

malloc(x)

mmap() .

brk() .

X > 128 KB

x <= 128 KB

17 of 134

0x13 ASLR (Address Space Layout Randomization)

---

17

stack

shared

libraries

User Process’s va_space

.text

.rodata

.data

heap

.bss

  • Places shared libraries, the stack and (optionally the heap) at random locations, making existing vulns more difficult to exploit
  • On Linux, there are 3 levels of ASLR to choose from
    • level 0 - no randomization
    • level 1 - randomize shared libraries, stack, mmap() .
    • level 2 - randomize shared libraries, stack, mmap(), brk() , heap .

malloc(x)

mmap() .

brk() .

X > 128 KB

x <= 128 KB

A gap will be inserted here

18 of 134

0x13 ASLR (Address Space Layout Randomization)

---

18

  • Places shared libraries, the stack and (optionally the heap) at random locations, making existing vulns more difficult to exploit
  • On Linux, there are 3 levels of ASLR to choose from
    • level 0 - no randomization
    • level 1 - randomize shared libraries, stack, mmap() .
    • level 2 - randomize shared libraries, stack, mmap(), brk() , heap .
  • Can be configured anytime via procfs

stack

shared

libraries

User Process’s va_space

.text

.rodata

.data

heap

.bss

[root]# echo 1 > /proc/sys/kernel/randomize_va_space�[root]# cat /proc/sys/kernel/randomize_va_space

1

19 of 134

0x13 ASLR (Address Space Layout Randomization)

---

19

  • ASLR alone is not enough, since there are still some techniques�to bypass it
    • return-to-plt (PLT is within .text)
    • GOT hijacking (GOT is within .data or .rodata)
    • stack pivoting (e.g., pivot to .bss which is not randomized)

stack

shared

libraries

User Process’s va_space

.text

.rodata

.data

heap

.bss

20 of 134

0x13 ASLR (Address Space Layout Randomization)

---

20

  • ASLR alone is not enough, since there are still some techniques�to bypass it
    • return-to-plt (PLT is within .text)
    • GOT hijacking (GOT is within .data or .rodata)
    • stack pivoting (e.g., pivot to .bss which is not randomized)

  • It would be even better if these sections are also randomized
    • .text
    • .rodata
    • .data
    • .bss
    • heap

stack

shared

libraries

User Process’s va_space

.text

.rodata

.data

heap

.bss

21 of 134

0x14 PIE (Position-Independent Executable)

---

21

  • Requires ASLR >= 1
  • Places an ELF at a random location
  • Randomizes the locations of .text, .data, .bss and heap .
  • Randomizing these sections requires technical assistance�from compilers, so we can ask the compiler to compile�an ELF as a “PIE”.

stack

shared

libraries

User Process’s va_space

.text

.rodata

.data

heap

.bss

user$ gcc -o out src.c -fPIE

22 of 134

0x15 Some Other Defensive Security Mechanisms

---

  • Security Properties of Executables

22

Short Name

Full Name

Desc

RELRO

RELocation Read-Only

Partial - GOT writable

Full - GOT read-only

STACK CANARY

Stack Canary

NX

No-eXecute

Are the stack pages executable?

PIE

Position-Independent Executable

If ASLR >= 1, then ELF will be loaded at a random address.

23 of 134

0x16 Linux x86_64 Calling Convention

---

23

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

24 of 134

0x16 Linux x86_64 Calling Convention

---

24

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

rdi

rsi

rdx

rcx

r8

r9

a

b

c

d

e

f

The first 6 arguments

are passed by registers

25 of 134

0x16 Linux x86_64 Calling Convention

---

25

...

The stack of a user process

low

high

h

g

rdi

rsi

rdx

rcx

r8

r9

a

b

c

d

e

f

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

26 of 134

0x16 Linux x86_64 Calling Convention

---

26

...

The stack of a user process

low

high

h

g

rdi

rsi

rdx

rcx

r8

r9

a

b

c

d

e

f

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

27 of 134

0x16 Linux x86_64 Calling Convention

---

27

...

The stack of a user process

low

high

h

g

return addr

saved rbp

rdi

rsi

rdx

rcx

r8

r9

a

b

c

d

e

f

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

28 of 134

0x16 Linux x86_64 Calling Convention

---

28

...

The stack of a user process

xx

low

high

h

g

return addr

saved rbp

rdi

rsi

rdx

rcx

r8

r9

a

b

c

d

e

f

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

29 of 134

0x16 Linux x86_64 Calling Convention

---

29

...

The stack of a user process

xx

yy

low

high

h

g

return addr

saved rbp

rdi

rsi

rdx

rcx

r8

r9

a

b

c

d

e

f

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

30 of 134

0x16 Linux x86_64 Calling Convention

---

30

...

The stack of a user process

zz

xx

yy

low

high

h

g

return addr

saved rbp

rdi

rsi

rdx

rcx

r8

r9

a

b

c

d

e

f

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

31 of 134

0x16 Linux x86_64 Calling Convention

---

31

...

The stack of a user process

zz

xx

yy

low

high

h

g

return addr

saved rbp

rdi

rsi

rdx

rcx

r8

r9

a

b

c

d

e

f

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

The return value will be

stored in rax

32 of 134

0x16 Linux x86_64 Calling Convention

---

32

...

The stack of a user process

zz

xx

yy

low

high

h

g

return addr

saved rbp

rdi

rsi

rdx

rcx

r8

r9

a

b

c

d

e

f

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

About to return!

Release local variables

33 of 134

0x16 Linux x86_64 Calling Convention

---

33

...

The stack of a user process

low

high

h

g

return addr

saved rbp

rdi

rsi

rdx

rcx

r8

r9

a

b

c

d

e

f

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

About to return!

Release local variables

34 of 134

0x16 Linux x86_64 Calling Convention

---

34

...

The stack of a user process

low

high

h

g

return addr

saved rbp

rdi

rsi

rdx

rcx

r8

r9

a

b

c

d

e

f

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

Pop the saved rbp value to restore rbp

35 of 134

0x16 Linux x86_64 Calling Convention

---

35

...

The stack of a user process

low

high

h

g

return addr

rdi

rsi

rdx

rcx

r8

r9

a

b

c

d

e

f

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

Pop the saved rbp value to restore rbp

36 of 134

0x16 Linux x86_64 Calling Convention

---

36

...

The stack of a user process

low

high

h

g

return addr

rdi

rsi

rdx

rcx

r8

r9

a

b

c

d

e

f

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

Finally, we will return from this function.

The return value is

already in rax.

37 of 134

0x16 Linux x86_64 Calling Convention

---

37

...

The stack of a user process

low

high

h

g

return addr

rdi

rsi

rdx

rcx

r8

r9

a

b

c

d

e

f

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

ret == pop rip

38 of 134

0x16 Linux x86_64 Calling Convention

---

38

...

The stack of a user process

low

high

h

g

rdi

rsi

rdx

rcx

r8

r9

a

b

c

d

e

f

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

ret == pop rip

39 of 134

0x16 Linux x86_64 Calling Convention

---

39

...

The stack of a user process

low

high

h

g

rdi

rsi

rdx

rcx

r8

r9

a

b

c

d

e

f

  • Consider the following code snippet

long myfunc(long a, long b, long c, long d,

long e, long f, long g, long h)

{

long xx = 0;

long yy = 0;

long zz = 0;

return zz + 20;

}

It is the caller who pushed

g, h before calling myfunc()

so it is up to the caller to

pop them off the stack

40 of 134

0x17 Overwriting the Return Address

---

40

The stack of a user process

buf

low

high

return addr

saved rbp

  • Suppose we have this function

void pwnme(void)

{

char buf[8];

gets(buf);

}

...

41 of 134

0x17 Overwriting the Return Address

---

41

The stack of a user process

buf

low

high

return addr

saved rbp

  • Suppose we have this function

void pwnme(void)

{

char buf[8];

gets(buf);

}

  • We can input the following bytes to the program

user$ python2 -c "print 8*'A' + 8*'B' + addr"

...

42 of 134

0x17 Overwriting the Return Address

---

42

The stack of a user process

buf

low

high

return addr

saved rbp

  • Suppose we have this function

void pwnme(void)

{

char buf[8];

gets(buf);

}

  • We can input the following bytes to the program

user$ python2 -c "print 8*'A' + 8*'B' + addr"

...

43 of 134

0x17 Overwriting the Return Address

---

43

...

The stack of a user process

AAAAAAAA

low

high

addr

BBBBBBBB

  • Suppose we have this function

void pwnme(void)

{

char buf[8];

gets(buf);

}

  • We can input the following bytes to the program

user$ python2 -c "print 8*'A' + 8*'B' + addr"

44 of 134

0x18 ROP (Return Oriented Programming) in x86_64

---

44

The stack of a user process

return addr

low

high

  • Consider the following scenario
    • the string “/bin/sh\x00” is at addr1 .
    • and we have the following “gadgets”

addr2 : pop rdi ; ret

addr3 : call system@plt

45 of 134

0x18 ROP (Return Oriented Programming) in x86_64

---

45

The stack of a user process

return addr

low

high

  • Consider the following scenario
    • the string “/bin/sh\x00” is at addr1 .
    • and we have the following “gadgets”

addr2 : pop rdi ; ret

addr3 : call system@plt

    • Can we spawn a shell with the existing resources?

46 of 134

0x18 ROP (Return Oriented Programming) in x86_64

---

46

The stack of a user process

addr2 .

low

high

  • Consider the following scenario
    • the string “/bin/sh\x00” is at addr1 .
    • and we have the following “gadgets”

addr2 : pop rdi ; ret

addr3 : call system@plt

    • Can we spawn a shell with the existing resources?

47 of 134

0x18 ROP (Return Oriented Programming) in x86_64

---

47

The stack of a user process

addr2 .

low

high

addr1 .

  • Consider the following scenario
    • the string “/bin/sh\x00” is at addr1 .
    • and we have the following “gadgets”

addr2 : pop rdi ; ret

addr3 : call system@plt

    • Can we spawn a shell with the existing resources?

48 of 134

0x18 ROP (Return Oriented Programming) in x86_64

---

48

The stack of a user process

low

high

addr2 .

addr1 .

addr3 .

  • Consider the following scenario
    • the string “/bin/sh\x00” is at addr1 .
    • and we have the following “gadgets”

addr2 : pop rdi ; ret

addr3 : call system@plt

    • Can we spawn a shell with the existing resources?

49 of 134

0x18 ROP (Return Oriented Programming) in x86_64

---

49

  • Consider the following scenario
    • the string “/bin/sh\x00” is at addr1 .
    • and we have the following “gadgets”

addr2 : pop rdi ; ret

addr3 : call system@plt

The stack of a user process

low

high

addr2 .

addr1 .

addr3 .

    • Can we spawn a shell with the existing resources?
    • system(“/bin/sh”);

50 of 134

0x20

The Attached Code

50

51 of 134

0x21 The Attached Code

---

  • A minimal do-nothing C program

51

int main() { return 0; }

pwndbg> info func

0x0000000000001000 _init

0x0000000000001020 _start

0x0000000000001050 deregister_tm_clones

0x0000000000001080 register_tm_clones

0x00000000000010c0 __do_global_dtors_aux

0x0000000000001110 frame_dummy

0x0000000000001119 main

0x0000000000001130 __libc_csu_init

0x00000000000011a0 __libc_csu_fini

0x00000000000011a8 _fini

“The Attached Code”

52 of 134

0x21 The Attached Code

---

  • What are they used for ?
    • Program-level initializer

52

53 of 134

0x21 The Attached Code

---

  • What are they used for ?
    • Program-level initializer
    • Program-level finalizer

53

54 of 134

0x21 The Attached Code

---

  • What are they used for ?
    • Program-level initializer
    • Program-level finalizer

54

55 of 134

0x22 __libc_csu_init()

---

  • Contains two useful gadgets
    • __libc_csu_init + 90

55

1

56 of 134

0x22 __libc_csu_init()

---

  • Contains two useful gadgets
    • __libc_csu_init + 90
    • __libc_csu_init + 64

56

2

1

57 of 134

0x23 ret2csu

---

  • We can control the first 3 arguments

57

1

2

First, return to

this gadget

58 of 134

0x23 ret2csu

---

  • We can control the first 3 arguments

58

1

2

Return here

from gadget 1

59 of 134

0x23 ret2csu

---

  • We can control the first 3 arguments
    • r12d -> edi
    • r13 -> rsi
    • rdx -> rdx

59

1

2

Return here

from gadget 1

60 of 134

0x23 ret2csu

---

  • We can control the first 3 arguments
    • r12d -> edi
    • r13 -> rsi
    • rdx -> rdx

60

1

2

call [ r15 + rbx * 8 ]

61 of 134

0x23 ret2csu

---

  • We can control the first 3 arguments
    • r12d -> edi
    • r13 -> rsi
    • rdx -> rdx

61

1

2

call [ r15 + rbx * 8 ]

The Content at address r15 + rbx*8

must be the address of the function we want to call

62 of 134

0x23 ret2csu

---

  • We can control the first 3 arguments
    • r12d -> edi
    • r13 -> rsi
    • rdx -> rdx

62

1

2

call [ r15 + rbx * 8 ]

The Content at address r15 + rbx*8

must be the address of the function we want to call

At this point, we have two choices :

  1. Call a function (e.g., _fini()) that doesn’t touch rbx, rbp, r12, r13, r14, r15
  2. Call a function@libc

63 of 134

0x23 ret2csu

---

  • How can we call _fini() ???

63

1

2

64 of 134

0x23 ret2csu

---

  • How can we call _fini() ???
    • the .dynamic section contains�the address of _fini()

64

1

2

65 of 134

0x23 ret2csu

---

  • How can we call _fini() ???
    • the .dynamic section contains�the address of _fini()

65

1

2

66 of 134

0x23 ret2csu

---

  • How can we call _fini() ???
    • the .dynamic section contains�the address of _fini()

66

1

2

r15 = 0x3e20

rbx = 0

call [ r15 + rbx * 8 ] == call 0x1254

67 of 134

0x23 ret2csu

---

  • How can we call _fini() ???
    • the .dynamic section contains�the address of _fini()

67

1

2

68 of 134

0x23 ret2csu

---

  • How can we call _fini() ???
    • the .dynamic section contains�the address of _fini()

  • Make sure rbx + 1 == rbp .
    • so that it can execute all the way down and ret

68

69 of 134

0x23 ret2csu

---

  • How can we call _fini() ???
    • the .dynamic section contains�the address of _fini()

  • Make sure rbx + 1 == rbp .
    • so that it can execute all the way�down and ret

  • Can’t we just call the function we want via call [ r15 + rbx * 8] ???
    • Sure!
    • But it can be difficult

69

70 of 134

0x30

Bypassing ASLR

on x86_64

70

71 of 134

0x31 Scenarios where Ret2csu is Useful

---

71

72 of 134

0x31 Scenarios where Ret2csu is Useful

---

72

73 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Forking Server
    • Widely used for handling multiple concurrent clients

73

74 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Forking Server
    • Widely used for handling multiple concurrent clients
    • Each client request is served by a dedicated child process

74

75 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Forking Server
    • Widely used for handling multiple concurrent clients
    • Each client request is served by a dedicated child process

75

Server

Client 1

Client 2

Client n

req

.

.

.

76 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Forking Server
    • Widely used for handling multiple concurrent clients
    • Each client request is served by a dedicated child process

76

Server

Client 1

Client 2

Client n

req

.

.

.

accept()

77 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Forking Server
    • Widely used for handling multiple concurrent clients
    • Each client request is served by a dedicated child process

77

Child 1

fork()

Server

Client 1

Client 2

Client n

req

.

.

.

accept()

78 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Forking Server
    • Widely used for handling multiple concurrent clients
    • Each client request is served by a dedicated child process

78

Child 1

fork()

Server

Client 1

Client 2

Client n

req

.

.

.

accept()

handle_req()

79 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Forking Server
    • Widely used for handling multiple concurrent clients
    • Each client request is served by a dedicated child process

79

Child 1

fork()

Server

Client 1

Client 2

Client n

req

.

.

.

accept()

resp

handle_req()

80 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Forking Server
    • Widely used for handling multiple concurrent clients
    • Each client request is served by a dedicated child process
    • If there’s a bof in handle_req() , then we can exploit this remote service

80

Child 1

fork()

Server

Client 1

Client 2

Client n

req

.

.

.

accept()

resp

handle_req()

81 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Analyzing the Target Service
    • A forking server
    • running on x86_64 Linux
    • ASLR level 2
    • checksec

81

82 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Analyzing the Target Service
    • A forking server
    • running on x86_64 Linux
    • ASLR level 2
    • checksec

82

GOT read-only

83 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Analyzing the Target Service
    • A forking server
    • running on x86_64 Linux
    • ASLR level 2
    • checksec

83

GOT read-only

Canary is present

84 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Analyzing the Target Service
    • A forking server
    • running on x86_64 Linux
    • ASLR level 2
    • checksec

84

GOT read-only

Canary is present

Stack is not executable

85 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Analyzing the Target Service
    • A forking server
    • running on x86_64 Linux
    • ASLR level 2
    • checksec

85

GOT read-only

Canary is present

Stack is not executable

.data ~ .bss

is at a fixed location

86 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Analyzing the Target Service
    • A forking server
    • running on x86_64 Linux
    • ASLR level 2
    • checksec

  • How to exploit this ?

86

GOT read-only

Canary is present

Stack is not executable

.data ~ .bss

is at a fixed location

87 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Analyzing the Target Service
    • A forking server
    • running on x86_64 Linux
    • ASLR level 2
    • checksec

  • How to exploit this ?

87

GOT read-only

Canary is present

Stack is not executable

.data ~ .bss

is at a fixed location

Bruteforce THE stack canary

1

88 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Analyzing the Target Service
    • A forking server
    • running on x86_64 Linux
    • ASLR level 2
    • checksec

  • How to exploit this ?

88

GOT read-only

Canary is present

Stack is not executable

.data ~ .bss

is at a fixed location

Bruteforce THE stack canary

1

(Optional) Pivot the stack to .bss

2

89 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Analyzing the Target Service
    • A forking server
    • running on x86_64 Linux
    • ASLR level 2
    • checksec

  • How to exploit this ?

89

GOT read-only

Canary is present

Stack is not executable

.data ~ .bss

is at a fixed location

Bruteforce THE stack canary

1

Leak libc address to bypass ASLR

3

(Optional) Pivot the stack to .bss

2

90 of 134

0x31 Scenarios where Ret2csu is Useful

---

  • Analyzing the Target Service
    • A forking server
    • running on x86_64 Linux
    • ASLR level 2
    • checksec

  • How to exploit this ?

90

GOT read-only

Canary is present

Stack is not executable

.data ~ .bss

is at a fixed location

Bruteforce THE stack canary

1

Leak libc address to bypass ASLR

3

(Optional) Pivot the stack to .bss

2

Reverse shell / arbitrary exec

4

91 of 134

0x32 Exploiting a Forking Server - Stage 1

---

  • Bruteforce the 8-byte Stack Canary (x86_64)
    • On x86_64, it is a randomly generated 8-byte value at ( $rbp - 8 )�Whose purpose is to prevent the return address from being overwritten

91

92 of 134

0x32 Exploiting a Forking Server - Stage 1

---

  • Bruteforce the 8-byte Stack Canary (x86_64)
    • On x86_64, it is a randomly generated 8-byte value at ( $rbp - 8 )�Whose purpose is to prevent the return address from being overwritten
    • Before returning from a function, the program will call __stack_chk_fail(),�and if the canary value has been modified (via bof) then the program dies

92

93 of 134

0x32 Exploiting a Forking Server - Stage 1

---

  • Bruteforce the 8-byte Stack Canary (x86_64)
    • On x86_64, it is a randomly generated 8-byte value at ( $rbp - 8 )�Whose purpose is to prevent the return address from being overwritten
    • Before returning from a function, the program will call __stack_chk_fail(),�and if the canary value has been modified (via bof) then the program dies
    • The parent’s canary is generated only once (when the parent starts),�so even if the child dies, when a new req is sent, the new child will still�have the same canary

93

94 of 134

0x32 Exploiting a Forking Server - Stage 1

---

  • Bruteforce the 8-byte Stack Canary (x86_64)
    • On x86_64, it is a randomly generated 8-byte value at ( $rbp - 8 )�Whose purpose is to prevent the return address from being overwritten
    • Before returning from a function, the program will call __stack_chk_fail(),�and if the canary value has been modified (via bof) then the program dies
    • The parent’s canary is generated only once (when the parent starts),�so even if the child dies, when a new req is sent, the new child will still�have the same canary

94

We can bruteforce the canary byte-by-byte,

and then we will be able to overwrite return addresses

95 of 134

0x32 Exploiting a Forking Server - Stage 2

---

  • (Optional) Stack Pivoting
    • Since a forking server would typically interact with its clients,�usually write@plt will exist

95

96 of 134

0x32 Exploiting a Forking Server - Stage 2

---

  • (Optional) Stack Pivoting
    • Since a forking server would typically interact with its clients,�usually write@plt will exist
    • In the case of forking server, we don’t need stack pivoting

96

97 of 134

0x32 Exploiting a Forking Server - Stage 2

---

  • (Optional) Stack Pivoting
    • Since a forking server would typically interact with its clients,�usually write@plt will exist
    • In the case of forking server, we don’t need stack pivoting
    • In the second example, we’ll cover the use of stack pivoting�in bypassing ASLR

97

98 of 134

0x32 Exploiting a Forking Server - Stage 3

---

  • Leaking libc address
    • Since we are connected to the server, the server will have�a fd (an integer) connected to us

98

99 of 134

0x32 Exploiting a Forking Server - Stage 3

---

  • Leaking libc address
    • Since we are connected to the server, the server will have�a fd (an integer) connected to us
    • If the server writes into that fd, we’ll receive something

99

100 of 134

0x32 Exploiting a Forking Server - Stage 3

---

  • Leaking libc address
    • Since we are connected to the server, the server will have�a fd (an integer) connected to us
    • If the server writes into that fd, we’ll receive something
    • The address of write@libc is stored in the GOT

100

101 of 134

0x32 Exploiting a Forking Server - Stage 3

---

  • Leaking libc address
    • Since we are connected to the server, the server will have�a fd (an integer) connected to us
    • If the server writes into that fd, we’ll receive something
    • The address of write@libc is stored in the GOT
    • Write the address of write@libc to that fd,�and we’ll receive the address of write@libc

101

102 of 134

0x32 Exploiting a Forking Server - Stage 4

---

  • Spawning a Reverse Shell
    • Using the below equation, we can calculate the�base address of libc at runtime�

102

stack

shared

libraries

User Process’s va_space

.text

.rodata

.data

heap

.bss

system_address = libc_base_address + offset

libc.so.6

system@libc

103 of 134

0x32 Exploiting a Forking Server - Stage 4

---

  • Spawning a Reverse Shell
    • Using the below equation, we can calculate the�base address of libc at runtime
    • The final step is to return to system@libc�and spawn a reverse shell�

103

stack

shared

libraries

User Process’s va_space

.text

.rodata

.data

heap

.bss

system_address = libc_base_address + offset

libc.so.6

system@libc

104 of 134

0x33 De-ASLR (500 pts)

---

  • Pwn this ( ASLR level 2 )

104

int main()

{

char buf[8];

gets(buf);

return 0;

}

105 of 134

0x33 De-ASLR (500 pts)

---

  • Pwn this ( ASLR level 2 )

105

int main()

{

char buf[8];

gets(buf);

return 0;

}

106 of 134

0x33 De-ASLR (500 pts)

---

  • This time
    • No write@plt (or any similar function)

106

107 of 134

0x33 De-ASLR (500 pts)

---

  • This time
    • No write@plt (or any similar function)
    • We have gets@libc in the GOT, but�we can’t even write to stdout�in the first place

107

108 of 134

0x33 De-ASLR (500 pts)

---

  • Let’s look at the stack
    • After calling gets(), some addresses�of libc symbols are leaked

108

109 of 134

0x33 De-ASLR (500 pts)

---

  • After calling gets(), there will be addresses of some libc symbols on the stack
  • If we can print out one of these addresses, then libc’s base address is leaked
  • So how do we write this to stdout ?

109

110 of 134

0x33 De-ASLR (500 pts)

---

  • _IO_file_write()

  • The first argument is a struct FILE * which is actually a struct _IO_FILE * .
    • Defined in /usr/include/bits/types/struct_FILE.h
    • In /usr/include/bits/types/FILE.h, we’ll see ...

110

long _IO_file_write(FILE *fp, void *buf, size_t size)

{

......

}

typedef struct _IO_FILE FILE;

111 of 134

0x33 De-ASLR (500 pts)

---

  • We can create a fake struct _IO_FILE within .bss, and use its address as�_IO_file_write()’s first arg.
    • _fileno = 1 (i.e. stdout)
    • _flags2 = 2
    • All other members of the struct = 0

111

112 of 134

0x33 De-ASLR (500 pts)

---

  • We can create a fake struct _IO_FILE within .bss, and use its address as�_IO_file_write()’s first arg.
    • _fileno = 1 (i.e. stdout)
    • _flags2 = 2
    • All other members of the struct = 0

  • How do we call _IO_file_write()
    • Address unknown (∵ASLR)

112

In previous example, we called _fini() here

113 of 134

0x33 De-ASLR (500 pts)

---

  • We can create a fake struct _IO_FILE within .bss, and use its address as�_IO_file_write()’s first arg.
    • _fileno = 1 (i.e. stdout)
    • _flags2 = 2
    • All other members of the struct = 0

  • How do we call _IO_file_write()
    • Address unknown (∵ASLR)
    • call [r12 + rbx * 8]
    • R12 = ???
    • Rbx = ???

113

114 of 134

0x33 De-ASLR (500 pts)

---

  • Previously, we’ve found the .dynamic section contains an address of _fini()

114

115 of 134

0x33 De-ASLR (500 pts)

---

  • Previously, we’ve found the .dynamic section contains an address of _fini()
  • If we want to call _IO_file_write(), we also need to do the same thing

115

116 of 134

0x33 De-ASLR (500 pts)

---

  • Previously, we’ve found the .dynamic section contains an address of _fini()
  • If we want to call _IO_file_write(), we also need to do the same thing

116

Okay, so somewhere in libc.so

also contains &_IO_file_write

r12

target

117 of 134

0x33 De-ASLR (500 pts)

---

  • Previously, we’ve found the .dynamic section contains an address of _fini()
  • If we want to call _IO_file_write(), we also need to do the same thing

117

Okay, so somewhere in libc.so

also contains &_IO_file_write

The offset of the two addresses is 0x3c8

r12

target

r12 + rbx * 8 = target

rbx * 8 = target - r12 = 0x3c8

∴ rbx = 0x3c8 / 8 = 0x79

118 of 134

0x33 De-ASLR (500 pts)

---

  • Previously, we’ve found the .dynamic section contains an address of _fini()
  • If we want to call _IO_file_write(), we also need to do the same thing

118

r12

target

Validation

r12 + 0x79 * 8 == target

rbx

call [target] == call IO_file_write

119 of 134

0x33 De-ASLR (500 pts)

---

  • ret2csu

119

0x6010a0

1

2

120 of 134

0x33 De-ASLR (500 pts)

---

  • ret2csu

120

r12

0x6010a0

1

2

121 of 134

0x33 De-ASLR (500 pts)

---

  • ret2csu

121

r12

rbp

rbx

r13

r14

r15

ret

0x6010a0

1

2

122 of 134

0x33 De-ASLR (500 pts)

---

  • ret2csu

122

r12

rbp

rbx

r13

r14

r15

ret

0x6010a0

pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret

Stack pivot gadget

1

2

123 of 134

0x33 De-ASLR (500 pts)

---

  • ret2csu

123

r12

rbp

rbx

r13

r14

r15

ret

0x6010a0

ret

r15

r14

pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret

Stack pivot gadget

r13

1

2

Stack pivot here

124 of 134

0x33 De-ASLR (500 pts)

---

  • ret2csu

124

r12

rbp

rbx

r13

r14

r15

1

ret

0x6010a0

ret

r15

r14

pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret

Stack pivot gadget

r13

2

Stack pivot here

125 of 134

0x33 De-ASLR (500 pts)

---

  • ret2csu

125

r12

rbp

rbx

r13

r14

r15

ret

0x6010a0

ret

r15

r14

pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret

Stack pivot gadget

r13

1

2

Stack pivot here

126 of 134

0x33 De-ASLR (500 pts)

---

  • ret2csu

126

r12

rbp

rbx

r13

r14

r15

ret

0x6010a0

ret

r15

r14

pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret

Stack pivot gadget

r13

1

2

Stack pivot here

Libc leaked

127 of 134

0x33 De-ASLR (500 pts)

---

  • ret2csu

127

r12

rbp

rbx

r13

r14

r15

ret

0x6010a0

ret

r15

r14

pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret

Stack pivot gadget

r13

1

2

Stack pivot here

Libc leaked

128 of 134

0x33 De-ASLR (500 pts)

---

  • ret2csu

128

r12

rbp

rbx

r13

r14

r15

ret

0x6010a0

ret

r15

r14

pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret

Stack pivot gadget

r13

1

2

Stack pivot here

Libc leaked

rbp

rbx

129 of 134

0x33 De-ASLR (500 pts)

---

  • ret2csu

129

r12

rbp

rbx

r13

r14

r15

ret

0x6010a0

ret

r15

r14

pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret

Stack pivot gadget

r13

1

2

Stack pivot here

Libc leaked

  1. gets() -> write system@libc
  2. rdi -> “/bin/sh”
  3. Return to system@libc

rbp

rbx

130 of 134

0x33 De-ASLR (500 pts)

---

  • _IO_file_write() prints the address of stdin,�which in turn gives us the address of system()

130

131 of 134

0x33 De-ASLR (500 pts)

---

  • But my exploit crashes the program at the last step

131

?????

132 of 134

0x33 De-ASLR (500 pts)

---

  • But my exploit crashes the program at the last step

  • I was trying to write the address of system() into�.bss via get()

132

?????

133 of 134

0x33 De-ASLR (500 pts)

---

  • But my exploit crashes the program at the last step

  • I was trying to write the address of system() into�.bss via get()

  • I speculate that my fake struct _IO_FILE was partially overwritten�due to stack growth, so after calling _IO_file_write() everything went wrong.�IA64 Stack Alignment issue:�$rsp must end in 0 “before” calling a function, and must end in 8 after calling.

133

?????

134 of 134

Program received signal SIGSEGV, Segmentation fault.

Thank you !