Lecture 4: Buffer Overflow Defenses

Ensuring Memory Safety



Optional: Reasoning About Memory Safety

One way to ensure memory safety is to carefully reason about memory accesses in your code, by defining pre-conditions and post-conditions for every function you write and using invariants to prove that these conditions are satisfied. Although it is a good skill to have, this process is rarely used in practice, so it is out of scope for the summer.

If you are interested, the Spring 2020 video is linked below. It will not be tested on any exams or projects.



(End of optional content. Everything below this is in scope.)

Use a Memory-Safe Language


True or false: Using a memory-safe language prevents 100% of buffer overflow attacks.



Defending Non-Memory-Safe Code



Stack Canaries


An attacker runs a vulnerable program with some input that leaks the stack canary, and writes down the value of the leaked stack canary. Then the attacker runs the program again, using the leaked canary value to overwrite the stack canary. True or false: this strategy defeats the stack canary defense.



Guessing the Canary

How many bits of entropy would a canary on a 64-bit system have if one of the bytes is always 0x00?



Non-Executable Pages


True or false: An attacker cannot do anything malicious if non-executable pages are enabled. (Hint: Think about the analogy from the last lecture.)



Return Into libc



Return-Oriented Programming


Why does enabling non-executable pages not prevent return-oriented programming?



Address Space Layout Randomization (ASLR)


True or false: If ASLR is enabled but DEP is not enabled, an attacker can write malicious code to the stack and overwrite the return address to execute that code.



Defenses in Practice



Fuzz Testing



Memory Safety Conclusion


After finishing this lecture, you should have everything you need to start Project 1. Debugging this project can be time-consuming, so please start early!