Lab 5 - Memory Security
Task: Wild Pointer Arithmetic Info Leak
Navigate to chapters/data/memory-security/drills/tasks/pointer-arithmetic-leak
and run make skels
to generate the support/
folder.
Then navigate to support/src
.
Navigate to the chapters/data/memory-security/drills/tasks/pointer-arithmetic-leak/support/
directory.
Open and analyze the buff_leak.c
file.
The pointer p
points to the stack.
However, we can modify any variable that is declared in the program through p
.
All we need to know is the offset of the other memory locations that we wish to access.
Run the program and try to input the correct offsets to modify variables from different regions of our program. Once a correct offset is given as input, the program will output a validation message.
Note that adding or subtracting user-provided values to pointers enables an attacker to observe a program's entire memory! If you're having difficulties solving this exercise, go through this reading material.
Task: ASLR
Navigate to chapters/data/memory-security/drills/tasks/aslr
and run make skels
to generate the support/
folder.
Then navigate to support/src
.
Use the Makefile.aslr
file to compile the chapters/data/memory-security/drills/tasks/aslr/support/aslr.c
file:
student@os:~/.../drills/tasks/aslr/support/$ make -f Makefile.aslr
By default, ASLR and PIC are enabled. Observe the results. Next, we disable ASLR:
student@os:~/.../drills/tasks/aslr/support/$ echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
Even though the code is compiled with PIC, both library and user functions have the same address between runs. Re-enable ASLR:
student@os:~/.../drills/tasks/aslr/support/$ echo 2 | sudo tee /proc/sys/kernel/randomize_va_space
Disable PIC by uncommenting the -fno-PIC
and LDFLAGS
lines.
We observe that for randomization to work, we need to instruct the OS to randomize the program sections and the compiler to generate code that is position independent.
If you're having difficulties solving this exercise, go through this reading material.
Checker
To run the checker, go into the tests
directory located in src
, then type make check
.
A successful output of the checker should look like this :
student@os:~/.../drills/tasks/aslr/support/src/tests make check
test_aslr ........................ passed ... 100
========================================================================
Total: 100/100
Task: Stack Protector
Navigate to chapters/data/memory-security/drills/tasks/stack-protector
and run make skels
to generate the support/
folder.
Then navigate to support/src
.
Comment the -fno-stack-protector
switch from the chapters/data/memory-security/drills/tasks/stack-protector/support/Makefile
, recompile and run the bo_practice_write
executable.
Examine the binary with objdump
and identify the instructions that set and test the canary.
Observe what happens when a buffer overflow occurs.
If you're having difficulties solving this exercise, go through this reading material.
Checker
To run the checker, go into the tests
directory located in src
, then type make check
.
A successful output of the checker should look like this :
student@os:~/.../drills/tasks/aslr/support/src/tests make check
test_stackprotector ........................ passed ... 100
========================================================================
Total: 100/100
Task: Bypassing the Stack Protector
Navigate to chapters/data/memory-security/drills/tasks/bypassing-stack-protector
and run make skels
to generate the support/
folder.
Then navigate to support/src
.
Inspect the chapters/data/memory-security/drills/tasks/bypassing-stack-protector/support/stack_protector.c
source file.
Compile the program and examine the object code.
Try to identify the canary value.
Using the addr
variable, write 2 instructions: one that indexes addr
to overwrite the canary with the correct value and one that indexes addr
to overwrite the return address with the address of function pawned()
.
In case of a successful exploit a video will be offered as reward.
If you're having difficulties solving this exercise, go through this reading material.
Checker
To run the checker, go into the tests
directory located in src
, then type make check
.
A successful output of the checker should look like this :
student@os:~/.../drills/tasks/aslr/support/src/tests make check
test_bypassing-stackprotector ........................ passed ... 100
========================================================================
Total: 100/100
Task: Shellcode Executor
Navigate to chapters/data/memory-security/drills/tasks/exec-shellcode
and run make skels
to generate the support/
folder.
Then navigate to support/src
.
Navigate to the chapters/data/memory-security/drills/tasks/exec-shellcode/support/
directory.
Your goal is to update the src/exec-shellcode.s
source code file to be able to read and execute shellcodes from a given binary files.
The program thus acts as a shellcode tester.
A shellcode is a small program that is commonly used in memory-related security exploits as a form of arbitrary code execution. It's a binary string consisting of instructions / code to be directly interpreted by the CPU during the execution of the targeted vulnerable program.
Shellcodes end up in an exit()
system call to ensure a graceful exit of the program after running the shellcode.
Use mmap()
to reserve a virtual page.
Use anonymous mapping (i.e. the MAP_ANONYMOUS
) flag.
Use the proper permissions required to enable the shellcode to be read from the file into memory and then executed.
To test the implementation, enter the tests/
directory and run:
make check
As an extra item, add a shellcode for the brk()
system call in the tests/brk.asm
file.
It should be a simple shellcode that calls brk(NULL)
, i.e. with the purpose of getting the current program break.
In case of a correct solution, you will get an output such as:
./run_all_tests.sh
test_helloworld ........................ passed ... 25
test_getpid ........................ passed ... 25
test_openfile ........................ passed ... 25
test_brk ........................ passed ... 25
Total: 100/100
If you're having difficulties solving this exercise, go through this reading material.
Memory Security
Memory security is one of the most important aspects in today's computer systems. Its main objectives are to avoid the development of vulnerable code and prevent attackers from exploiting existing memory vulnerabilities. Memory protection techniques are implemented at all levels of memory abstraction: hardware, operating system and programming language. In addition, third-party software tools may be used to statically and dynamically examine a program to identify vulnerabilities when working with memory.
In this section, we will focus on the main memory vulnerabilities and how to exploit them. We will use the C programming language for presentation purposes, however, these examples may be reproduced in any language that implements arrays as pointers without bounds and allows the liberal use of pointers.
Wild Pointer Arithmetic Info Leak
In C, once a pointer has been initialized with a valid address it can potentially access any location of the process address space. This is problematic for situations where the binary contains sensitive information. Take for example a server that authenticates users: if the password is stored in some buffer, then if a pointer is wrongfully used, it can end up leaking the password.
ASLR
Address Space Layout Randomization (ASLR) is a protection technique employed by operating systems to make it harder for attackers to identify code locations to jump to. Essentially, every program section (including shared library code) is mapped at a random virtual address every time the program is run. That way, it is harder for the attacker to exploit a buffer overflow: the address of a potential code target is different with each run.
To enable randomization, there are 2 steps that need to be taken:
Enable ASLR on the operating system. This enables the operating system to map library code at different virtual addresses in a program.
Compile our code as Position Independent Code (PIC). This instructs the compiler to generate code such that it does not use absolute addresses when calling functions or accessing variables. As a consequence, the code may be loaded at any given address in memory.
On most Linux systems, ASLR is enabled by default. Modern compilers generate position independent code by default.
Stack Protector
As observed in the previous quiz, ASLR prevents an attacker (most of the times) from discovering a reliable address where to jump to, however, it does not prevent the occurrence of a buffer overflow.
An effective strategy to protect against buffer overflow is represented by the stack protector (or stack canary).
Between the end of the buffer and the return address (below the saved base pointer rbp
), a random value is placed on the stack.
Before the function returns, the value is checked: if the initial value was modified, then an exception is issued and the program is aborted.
Stack canaries are enabled by default, however, for learning purposes we have disabled it by passing -fno-stack-protector
to the compiler.
Bypassing the Stack Protector
The stack protector is generally placed immediately after the old rbp
.
With this information we can craft various exploits to bypass it:
Leak the canary and do not modify it. Reading the canary, as long as we do not modify it is not going to cause any disturbances. If we have the canary, we can just include it in our payload and happily overwrite the return address.
If we have access to a pointer that we can modify appropriately, we can just jump over the canary and directly modify the return address.
In case we have multiple buffers defined in the same stack frame, we can simply overwrite data in a buffer that is placed above the buffer we are exploiting without the stack protector intervention.
Guide: Buffer Overflow Info Leak
Since arrays decay to pointers in C, the same effect may be obtained by using them.
Navigate to the chapters/data/memory-security/guides/buffer-overflow-leak/support/
directory.
Open and analyze the array_leak.c
file.
Compile and run the program. Can you extract any information from the output? Can you identify the return address of the main function?
Note: You can use objdump -d -M intel array_leak
to check the stack layout.
Note: Depending on the environment the layout may differ.
Next, open and analyze the string_leak.c
file.
Compile and run the program.
To better understand the output, use xxd
to interpret the output as hexadecimal bytes:
student@os:~/.../guides/buffer-overflow-leak/support/$ ./string_leak | xxd
Note: 73
and 6f
are the ascii values of s
and o
Practice
In file string_leak.c
replace the usage of memcpy
with strcpy
.
Do not modify anything else (including the size of the buffer).
As the name suggests, strcpy()
is specialized for string copies, therefore we don need to specify how much we want to copy.
What is the result?
Is the result correct?
Explain the result.
Guide: Buffer Overflow Overwrite
Up until this point we have seen how we can exploit a buffer to leak information. However, this vulnerability may be used most of the time to overwrite data.
Take for example the code in chapters/data/memory-security/guides/buffer-overflow-overwrite/support/bo_write.c
.
Compile and run the code.
What happens?
Why?
Practice
Open the guides/buffer-overflow-overwrite/support/bo_write_practice.c
file.
Analyze the code, then compile it and run it.
Try to find an input that alters the control flow of the program so that "Comm-link online" is printed. You are not allowed to modify the source file.
Try to find an input that alters the control flow of the program so that "Channel open." is printed. You are not allowed to modify the source file.
Note: Addresses are 8 bytes long on 64 bit machines.
- Can you think of a different input that results in printing "Comm-link online"?