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
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
0x10
Introduction
3
Before we jump straight into it ...
4
ASLR
Let’s review the virtual memory layout on 64-bit Linux
5
0x11 Virtual Address Space (x86_64)
---
6
Virtual Address Space
Userspace
Kernel Space
unused
0
264
0x11 Virtual Address Space (x86_64)
---
7
Virtual Address Space
Userspace
Kernel Space
unused
two parts.
0
264
0x11 Virtual Address Space (x86_64)
---
8
Virtual Address Space
unused
Kernel Space
Userspace
two parts.
may change depending on which process is currently running, whereas the contents of the
kernel space will always be the same.
0
264
0x11 Virtual Address Space (x86_64)
---
9
Virtual Address Space
0xffffffffffffffff
0x0000000000000000
0x00007fffffffffff
0xffff800000000000
Userspace
128 TiB
unused
128 TiB
Kernel Space
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
0x12 Process Memory Layout (x86_64)
---
11
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
0x12 Process Memory Layout (x86_64)
---
12
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
0x12 Process Memory Layout (x86_64)
---
13
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
0x12 Process Memory Layout (x86_64)
---
14
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
0x13 ASLR (Address Space Layout Randomization)
---
15
stack
shared
libraries
User Process’s va_space
.text
.rodata
.data
heap
.bss
0x13 ASLR (Address Space Layout Randomization)
---
16
stack
shared
libraries
User Process’s va_space
.text
.rodata
.data
heap
.bss
malloc(x)
mmap() .
brk() .
X > 128 KB
x <= 128 KB
0x13 ASLR (Address Space Layout Randomization)
---
17
stack
shared
libraries
User Process’s va_space
.text
.rodata
.data
heap
.bss
malloc(x)
mmap() .
brk() .
X > 128 KB
x <= 128 KB
A gap will be inserted here
0x13 ASLR (Address Space Layout Randomization)
---
18
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
0x13 ASLR (Address Space Layout Randomization)
---
19
stack
shared
libraries
User Process’s va_space
.text
.rodata
.data
heap
.bss
0x13 ASLR (Address Space Layout Randomization)
---
20
stack
shared
libraries
User Process’s va_space
.text
.rodata
.data
heap
.bss
0x14 PIE (Position-Independent Executable)
---
21
stack
shared
libraries
User Process’s va_space
.text
.rodata
.data
heap
.bss
user$ gcc -o out src.c -fPIE
0x15 Some Other Defensive Security Mechanisms
---
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. |
0x16 Linux x86_64 Calling Convention
---
23
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;
}
0x16 Linux x86_64 Calling Convention
---
24
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
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 |
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;
}
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 |
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;
}
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 |
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;
}
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 |
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;
}
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 |
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;
}
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 |
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;
}
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 |
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
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 |
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
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 |
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
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 |
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
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 |
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
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 |
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.
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 |
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
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 |
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
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 |
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
0x17 Overwriting the Return Address
---
40
The stack of a user process
buf
low
high
return addr
saved rbp
void pwnme(void)
{
char buf[8];
gets(buf);
}
...
0x17 Overwriting the Return Address
---
41
The stack of a user process
buf
low
high
return addr
saved rbp
void pwnme(void)
{
char buf[8];
gets(buf);
}
user$ python2 -c "print 8*'A' + 8*'B' + addr"
...
0x17 Overwriting the Return Address
---
42
The stack of a user process
buf
low
high
return addr
saved rbp
void pwnme(void)
{
char buf[8];
gets(buf);
}
user$ python2 -c "print 8*'A' + 8*'B' + addr"
...
0x17 Overwriting the Return Address
---
43
...
The stack of a user process
AAAAAAAA
low
high
addr
BBBBBBBB
void pwnme(void)
{
char buf[8];
gets(buf);
}
user$ python2 -c "print 8*'A' + 8*'B' + addr"
0x18 ROP (Return Oriented Programming) in x86_64
---
44
The stack of a user process
return addr
low
high
addr2 : pop rdi ; ret
addr3 : call system@plt
0x18 ROP (Return Oriented Programming) in x86_64
---
45
The stack of a user process
return addr
low
high
addr2 : pop rdi ; ret
addr3 : call system@plt
0x18 ROP (Return Oriented Programming) in x86_64
---
46
The stack of a user process
addr2 .
low
high
addr2 : pop rdi ; ret
addr3 : call system@plt
0x18 ROP (Return Oriented Programming) in x86_64
---
47
The stack of a user process
addr2 .
low
high
addr1 .
addr2 : pop rdi ; ret
addr3 : call system@plt
0x18 ROP (Return Oriented Programming) in x86_64
---
48
The stack of a user process
low
high
addr2 .
addr1 .
addr3 .
addr2 : pop rdi ; ret
addr3 : call system@plt
0x18 ROP (Return Oriented Programming) in x86_64
---
49
addr2 : pop rdi ; ret
addr3 : call system@plt
The stack of a user process
low
high
addr2 .
addr1 .
addr3 .
0x20
The Attached Code
50
0x21 The Attached Code
---
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”
0x21 The Attached Code
---
52
0x21 The Attached Code
---
53
0x21 The Attached Code
---
54
0x22 __libc_csu_init()
---
55
1
0x22 __libc_csu_init()
---
56
2
1
0x23 ret2csu
---
57
1
2
First, return to
this gadget
0x23 ret2csu
---
58
1
2
Return here
from gadget 1
0x23 ret2csu
---
59
1
2
Return here
from gadget 1
0x23 ret2csu
---
60
1
2
call [ r15 + rbx * 8 ]
0x23 ret2csu
---
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
0x23 ret2csu
---
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 :
0x23 ret2csu
---
63
1
2
0x23 ret2csu
---
64
1
2
0x23 ret2csu
---
65
1
2
0x23 ret2csu
---
66
1
2
r15 = 0x3e20
rbx = 0
call [ r15 + rbx * 8 ] == call 0x1254
0x23 ret2csu
---
67
1
2
0x23 ret2csu
---
68
0x23 ret2csu
---
69
0x30
Bypassing ASLR
on x86_64
70
0x31 Scenarios where Ret2csu is Useful
---
71
0x31 Scenarios where Ret2csu is Useful
---
72
0x31 Scenarios where Ret2csu is Useful
---
73
0x31 Scenarios where Ret2csu is Useful
---
74
0x31 Scenarios where Ret2csu is Useful
---
75
Server
Client 1
Client 2
Client n
req
.
.
.
0x31 Scenarios where Ret2csu is Useful
---
76
Server
Client 1
Client 2
Client n
req
.
.
.
accept()
0x31 Scenarios where Ret2csu is Useful
---
77
Child 1
fork()
Server
Client 1
Client 2
Client n
req
.
.
.
accept()
0x31 Scenarios where Ret2csu is Useful
---
78
Child 1
fork()
Server
Client 1
Client 2
Client n
req
.
.
.
accept()
handle_req()
0x31 Scenarios where Ret2csu is Useful
---
79
Child 1
fork()
Server
Client 1
Client 2
Client n
req
.
.
.
accept()
resp
handle_req()
0x31 Scenarios where Ret2csu is Useful
---
80
Child 1
fork()
Server
Client 1
Client 2
Client n
req
.
.
.
accept()
resp
handle_req()
0x31 Scenarios where Ret2csu is Useful
---
81
0x31 Scenarios where Ret2csu is Useful
---
82
GOT read-only
0x31 Scenarios where Ret2csu is Useful
---
83
GOT read-only
Canary is present
0x31 Scenarios where Ret2csu is Useful
---
84
GOT read-only
Canary is present
Stack is not executable
0x31 Scenarios where Ret2csu is Useful
---
85
GOT read-only
Canary is present
Stack is not executable
.data ~ .bss
is at a fixed location
0x31 Scenarios where Ret2csu is Useful
---
86
GOT read-only
Canary is present
Stack is not executable
.data ~ .bss
is at a fixed location
0x31 Scenarios where Ret2csu is Useful
---
87
GOT read-only
Canary is present
Stack is not executable
.data ~ .bss
is at a fixed location
Bruteforce THE stack canary
1
0x31 Scenarios where Ret2csu is Useful
---
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
0x31 Scenarios where Ret2csu is Useful
---
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
0x31 Scenarios where Ret2csu is Useful
---
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
0x32 Exploiting a Forking Server - Stage 1
---
91
0x32 Exploiting a Forking Server - Stage 1
---
92
0x32 Exploiting a Forking Server - Stage 1
---
93
0x32 Exploiting a Forking Server - Stage 1
---
94
We can bruteforce the canary byte-by-byte,
and then we will be able to overwrite return addresses
0x32 Exploiting a Forking Server - Stage 2
---
95
0x32 Exploiting a Forking Server - Stage 2
---
96
0x32 Exploiting a Forking Server - Stage 2
---
97
0x32 Exploiting a Forking Server - Stage 3
---
98
0x32 Exploiting a Forking Server - Stage 3
---
99
0x32 Exploiting a Forking Server - Stage 3
---
100
0x32 Exploiting a Forking Server - Stage 3
---
101
0x32 Exploiting a Forking Server - Stage 4
---
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
0x32 Exploiting a Forking Server - Stage 4
---
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
0x33 De-ASLR (500 pts)
---
104
int main()
{
char buf[8];
gets(buf);
return 0;
}
0x33 De-ASLR (500 pts)
---
105
int main()
{
char buf[8];
gets(buf);
return 0;
}
0x33 De-ASLR (500 pts)
---
106
0x33 De-ASLR (500 pts)
---
107
0x33 De-ASLR (500 pts)
---
108
0x33 De-ASLR (500 pts)
---
109
0x33 De-ASLR (500 pts)
---
110
long _IO_file_write(FILE *fp, void *buf, size_t size)
{
......
}
typedef struct _IO_FILE FILE;
0x33 De-ASLR (500 pts)
---
111
0x33 De-ASLR (500 pts)
---
112
In previous example, we called _fini() here
0x33 De-ASLR (500 pts)
---
113
0x33 De-ASLR (500 pts)
---
114
0x33 De-ASLR (500 pts)
---
115
0x33 De-ASLR (500 pts)
---
116
Okay, so somewhere in libc.so
also contains &_IO_file_write
r12
target
0x33 De-ASLR (500 pts)
---
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
0x33 De-ASLR (500 pts)
---
118
r12
target
Validation
r12 + 0x79 * 8 == target
rbx
call [target] == call IO_file_write
0x33 De-ASLR (500 pts)
---
119
0x6010a0
1
2
0x33 De-ASLR (500 pts)
---
120
r12
0x6010a0
1
2
0x33 De-ASLR (500 pts)
---
121
r12
rbp
rbx
r13
r14
r15
ret
0x6010a0
1
2
0x33 De-ASLR (500 pts)
---
122
r12
rbp
rbx
r13
r14
r15
ret
0x6010a0
pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
Stack pivot gadget
1
2
0x33 De-ASLR (500 pts)
---
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
0x33 De-ASLR (500 pts)
---
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
0x33 De-ASLR (500 pts)
---
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
0x33 De-ASLR (500 pts)
---
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
0x33 De-ASLR (500 pts)
---
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
0x33 De-ASLR (500 pts)
---
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
0x33 De-ASLR (500 pts)
---
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
rbp
rbx
0x33 De-ASLR (500 pts)
---
130
0x33 De-ASLR (500 pts)
---
131
?????
0x33 De-ASLR (500 pts)
---
132
?????
0x33 De-ASLR (500 pts)
---
133
?????
Program received signal SIGSEGV, Segmentation fault.
Thank you !