Skip to content

Homework

Do not submit private keys, passwords, tokens, or secrets in your documentation!

The final assignment at the end of this course is:

Analyze and harden the vulnerabilities of a computing system that you want to secure

Keep this in mind during classes. Class 1 - Cryptography

Choose one or more of the assignments below. For each one, do the practical work and write a short explanation of how it works. The advanced questions are optional deeper dives for those who want to.

Create a public/private key pair.

What is the public key?
What is the private key?
What can this key pair be used for?
Why must the private key be protected?

Install an SSH public key on a server and use it to log in.

How does SSH key authentication work?
What is stored on the server?
What stays on your own computer?
Why does the server not need your private key?

Encrypt and decrypt a file.

Did you use symmetric or public-key encryption?
Where did the key come from?
Who can decrypt the file?
What happens if the key or password is lost?

Encrypt a disk, USB stick, directory, or filesystem.

What exactly is encrypted?
When is the data decrypted?
What does this protect against?
What does it not protect against?

Create a file, compute its hash, change the file, and compute the hash again.

What changed?
Why are hashes useful?
Why does a hash not prove who created the file?
How could hashes be used to detect tampering?

Sign a file or message and verify the signature.

What does the signature prove?
What does it not prove?
Why is the message usually hashed before signing?
What happens if the signed file is changed?

Send a digitally signed email.

How can the recipient verify the signature?
What key or certificate do they need?
Is the email encrypted, signed, or both?
What is the difference between signing and encrypting?

Install a certificate on a web server, or inspect the certificate of an HTTPS website.

Why does the server send a certificate instead of just a public key?
What role does the Certificate Authority play?
What attack does this help prevent?

Show or describe how to encrypt a long message for someone using their public key.

Why is the whole message usually not encrypted directly with the public key?
What is a symmetric key?
How is the symmetric key protected?
Why is this called hybrid encryption?

Optional Advanced questions

Research how RSA creates its private key.

Suppose Alice chooses two primes p1, and p2, and her modulus is then n=p1*p2. She also chooses a public exponent e, so her public key is (e,n). How does she compute the private exponent d?
What is the role of (p1 - 1)(p2 - 1)?
Why is it hard for someone that sees the public key (e,n) to compute the private key (d,n)? Why can Alice do this?

Suppose Alice uses (e, p) as a public key, where p is prime.

How would she compute the inverse exponent d?
Why would Alice’s public key (e,p) be insecure if her modulus p were a prime?

Research why faster factoring would break RSA.

What RSA publishes publicly.
Why factoring n reveals the private key.
Why a large quantum computer would be a problem for RSA.
Why this does not break all cryptography in the same way.

Research why the industry is moving away from RSA and other older public-key systems.

What problem RSA depends on being hard.
Why quantum computers threaten RSA.
What “post-quantum” or “quantum-safe” means.
Name one post-quantum algorithm and what it is used for.

Class 2 - Internet security

For each of the following systems, research and document 3 parts what threat does it address? what’s the trust model? what’s the residual risk?

PGP (end to end email security)
SSH (secure remote access)
VPN / IPsec (network-layer tunneling)
MACsec (link-layer encryption)
DNS Security (DNSSEC & DNS spoofing)
IDS / IPS (detection vs. prevention)

Complete these exercises:

TLS Handshake Trace

Trace a real TLS handshake and an encrypted data segment. Identify how the session is established — cipher suite, certificate, key exchange — and confirm what an observer can and cannot see.

Port Scan & Firewall

Your VM will be subjected to a port scan. Determine what it reveals about your exposed services, then configure the firewall to reduce what's reachable from outside.

Class 3 - Software security / memory safety

Install QEMU (already installed on the virtual machines we provide).
    If you run your own setup, install from qemu.org
    If you need help installing or running QEMU, Google searching helps a lot
Download the Protostar exploit virtual machine from exploit.education/protostar
From the desktop terminal, run the virtual machine with QEMU; typically:
    qemu-system-x86_64 -boot d -cdrom ~/Downloads/exploit-exercises-protostar-2.iso -m 512 (linux/mac)
    qemu.exe ~/Downloads/exploit-exercises-protostar-2.iso (windows)
Login to the virtual machine; username and password are on the protostar page
Navigate to the right directory (instructions on the Protostar page)

Run ls; do you see the exercise levels? If not you made a mistake in steps [3-5]

Perform this exercise:
    https://exploit.education/protostar/stack-zero/
    Try until you get this message: “you have changed the 'modified' variable”
    Explain what happened.
    How is it that providing an input in the line that reads “gets(buffer)” can change the value of the variable “modified”?
    What does “Segmentation fault” mean?

Perform this exercise:
    https://exploit.education/protostar/stack-one/
    Try until you get this message: “you have correctly got the variable to the right value”
    Explain what happened.
    What does “little endian” mean?
    Why didn’t you get “Segmentation fault”?

Perform this exercise:
    https://exploit.education/protostar/stack-three/
    How can you input hex values that don’t correspond to keyboard letters?
    Try until you get this message “code flow successfully changed”
    Explain what happened.

Congratulations! You have successfully hijacked control of a virtual machine! Class 4 - Microarchitecture security Cache Side Channel Attacks Lab

Download the files for Part 1
Download the files for Part 2

Introduction

In this lab, you will complete the following tasks:

Measure how long it takes the processor to read data from different levels of the memory hierarchy (the cache vs. main memory / DRAM).
Solve a CTF (capture-the-flag) puzzle using a cache-based side channel attack called Flush+Reload.

The goal of this lab is to give you hands-on experience interacting with the cache on a real machine. In class we talked about the cache hierarchy: the processor keeps recently used data close by in small, fast caches (L1, L2, L3), and falls back to the much slower main memory (DRAM) when the data is not cached. That speed difference is normally invisible to a program — but if we measure carefully, we can see it. And once we can see whether a piece of data was in the cache or not, we can use that as a side channel to learn secrets.

By the end of this lab you will have measured the cache-vs-memory timing difference yourself, and then used it to steal a secret from another program. Setting Up Your Environment

To compile and run the code in this lab, you need make and gcc installed. On ayour VM, you can install them with:

sudo apt install make gcc

On other systems, use your package manager’s equivalent. If gcc –version and make –version both print something, you are ready to go. Part 1: Timing the Cache vs. Main Memory The Idea

When the processor needs a piece of data, it first checks the cache. If the data is there (a cache hit), the read is fast. If it is not (a cache miss), the processor has to go all the way out to main memory (DRAM), which takes much longer.

In this part, you will measure both cases directly:

Cache (L1) latency: read an address so it lands in the cache, then immediately read it again and time how long that takes.
Memory (DRAM) latency: push the data out of the cache, then read it and time how long that takes.

We will not worry about telling L2 and L3 apart — we only care about the big, clearly visible difference between “in the cache” and “all the way out in DRAM.” The Tools We Give You

We provide helper functions in Part1-Timing/utility.h. Read through them and make sure you understand what each one does.

measure_one_block_access_time(addr): reads the data at addr and returns how many cycles the read took. This is your "stopwatch."
clflush(addr): flushes the given address out of the cache, all the way back to DRAM. After calling this, the next read of addr will be a cache miss (slow), because the processor has to fetch it from main memory again. This is the key instruction that lets us force a cache miss on demand.

You do not need to understand the assembly inside these helpers to do the lab. Just understand what they do at a high level: one times a memory read, the other evicts data from the cache. What to Measure

Open Part1-Timing/main.c. Your task is to fill in two timing loops:

Cache (L1) latency. For each sample:
Read the target address once, so the data is now in the cache.
Call measure_one_block_access_time on it and store the result.

Because the data is already cached, this should be fast.

DRAM latency. For each sample:
Call clflush on the target address to evict it from the cache.
Then call measure_one_block_access_time on it and store the result.

Because we just flushed it, the read has to go to main memory, so this should be slow.

That’s it — no eviction buffers, no cache-set math. The whole trick is: access it to make it fast, flush it to make it slow. A Warning: Measure First, Print Later

When you measure timing, it is tempting to write code like this:

for (int i = 0; i < SAMPLES; i++) { long t = measure_one_block_access_time(addr); printf(“Access took %ld cycles\n”, t); // <– DON’T do this inside the loop }

Do not print inside your measurement loop. A single printf runs an enormous number of instructions behind the scenes (string formatting, a system call into the kernel, etc.), and all of that touches the cache. Printing in the middle of measuring destroys exactly the cache state you are trying to observe.

Instead, measure into an array first, then print afterwards:

long measurements[SAMPLES]; for (int i = 0; i < SAMPLES; i++) { measurements[i] = measure_one_block_access_time(addr); // just measure } // … after the loop is done … print_results(measurements); // now print

Building and Running

From the Part1-Timing directory:

make # compile make run # run your program

Expected Outcome

We will not check for exact cycle numbers — they vary from machine to machine and run to run. What you should see is a clear gap: the DRAM (flushed) reads should be noticeably slower than the cache reads. A typical machine might show something like a handful of cycles for a cache hit and well over a hundred cycles for a DRAM access, but your exact numbers will differ.

Measurements are noisy, so look at the typical (median) values rather than any single reading.

Discussion Question 1-1

Run your program and report the typical cache (L1) latency and the typical DRAM latency that you measured. How big is the difference?

Discussion Question 1-2

Based on your numbers, pick a single threshold value (in cycles) that you could use to decide, given one timing measurement, whether the data was in the cache or in DRAM. You will use this threshold in Part 2.

Part 2: Capture the Flag with Flush+Reload

Now we turn that timing difference into an attack. In this part you will extract a secret value from a victim program without the victim ever telling you what it is — just by watching which memory it touches. Get to Know the Victim

We provide a victim program in Part2-FlushReload/victim. In pseudocode it does this:

// Allocate a large memory buffer char *buf = get_buffer();

// Pick a secret flag: a random integer in the range [0, SEC_RANGE) int flag = random();

// Main loop: keep reading one specific spot in the buffer, // where the spot depends on the secret flag while (true) { value = load(buf + flag * STRIDE); }

The victim keeps reading from one location in buf, and which location it reads depends on the secret flag. Your job is to figure out the flag by watching which part of buf gets pulled into the cache. Why This Works

The trick is that the victim and your attacker program share the same buffer in physical memory. (We set this up for you using a memory-mapped file: a virtual address in the victim and a virtual address in your attacker both point to the same physical memory.) Because the memory is shared, the cache state is shared too. When the victim reads buf + flag * STRIDE, that location gets loaded into the cache — and you can detect that. Your Plan: Flush + Reload

Your attacker repeats three steps in a loop:

Flush. Use clflush to evict every candidate location of buf from the cache. Now nothing is cached.
Wait. Pause briefly to give the victim a chance to do its flag-dependent read. (Do not use sleep() — that makes a system call and trashes the cache. Just spin in a small empty loop.)
Reload. Read each candidate location and time it with measure_one_block_access_time. The one location the victim touched will be fast (it's back in the cache); all the others will be slow (still in DRAM). Use the threshold you found in Part 1 to tell them apart.

Whichever candidate is consistently the fast one reveals the secret flag.

Exercise 2-1

Complete the code in Part2-FlushReload/attacker.c so that it reliably extracts the secret flag from the victim.

To test it: 1. Compile with make. 2. Generate the shared buffer file with python3 gen_file.py. 3. Open two terminals (use two SSH sessions, or a tool like tmux or screen). In one, run make run_victim. In the other, run make run_attacker. 4. The victim prints the true flag, so you can check whether your attacker recovered it correctly.

If the victim binary won't run, you may need to make it executable first: chmod +x victim.

A Note on Prefetching

Modern processors try to be clever: if they notice you reading memory in a simple, predictable pattern (e.g. address 0, then 1, then 2, …), they will prefetch the next addresses into the cache before you ask for them. This can fool your attack — locations look “fast” not because the victim touched them, but because the processor guessed you’d want them.

To avoid this, don’t reload your candidate locations in a simple linear order. Visit them in a randomized order so the prefetcher can’t predict your pattern.

Discussion Question 2-2

Your attack works because the victim's memory read location depends directly on the secret. How could you rewrite the victim so it still gets its work done, but without leaking the secret through which cache line it accesses?

Class 5 - OS and Application security chroot Demo Objective

Let’s say that you’re going to hand a terminal shell to a friend. They need root privileges to run their program, but you still don’t trust them to not go snooping around, and want to make sure that they can only get access to a very limited part of the OS.

What would you do?

In this lab, you will explore the historical foundation of modern containerization (like Docker) by manually building a minimalist file system sandbox using the Linux chroot (change root) utility.

You will observe how the operating system forces a process to accept a subfolder as its entire universe, and learn why this historic containment mechanism fails to meet certain security goals. Step 1: Create the Host Secret Flag & Set Up a Shell

To simulate confidential data (e.g., password databases, environmental variables) that an attacker wants to steal, we’re going to create a “sensitive” file on your actual host operating system!

Open your terminal and run:

sudo echo “FLAG{CHROOT_IS_NOT_A_SECURE_SANDBOX_1206}” > /host_secret.txt

Modern programs rely on dozens of shared libraries (.so files). If you attempt to isolate a standard utility like Bash inside an empty directory, it will crash immediately due to missing dependencies.

To avoid “dependency hell” and keep the focus on security concepts, you will sandbox BusyBox Static. BusyBox combines tiny versions of many common UNIX utilities into a single, statically compiled binary that does not require external library paths.

Install BusyBox and set up your sandbox environment:

Update repositories and install the static version of BusyBox

sudo apt update && sudo apt install -y busybox-static

Create a clean directory structure for your jail

mkdir -p ~/chroot_jail/bin

Copy the shell we’re going to run into our “jail”

cp /bin/busybox-static ~/chroot_jail/bin/sh

Step 2: Activate the Jail!

Use the chroot command to execute your new shell. This command instructs the Linux kernel: “Launch /bin/sh, but trick it into thinking ~/chroot_jail is the absolute root (/) of the entire machine.”

sudo chroot ~/chroot_jail /bin/sh

Note: Your command prompt will change significantly (often down to a simple # or empty prompt). You are now inside the jail. Step 3: Test the Containment

Now execute the following commands inside your jailed environment to test if you can break past the sandbox walls and find the host file created in Step 1.

List the Root Directory:

   ls -la /

Observation: You should only see the bin directory. To this shell, the parent directories do not exist.

Attempt Directory Traversal:

    cd ../../..
    ls -la

Observation: No matter how many times you step "up" via .., you remain locked inside your bin folder tree. The kernel rewrites any traversal requests beyond the designated root back to the jail's root.

Can you grab the file?:

cat /host_secret.txt

Observation: The system outputs cat: can’t open ‘/host_secret.txt’: No such file or directory. The host file is safe from discovery.

Exit the Jail: When finished exploring, close the sandbox session to return safely to your host terminal:

exit

Part 2: Deliverables & Submission Requirements

Submit a brief lab report with answers to the following questions:

When you ran cd ../../.. inside the jail and checked your location, why couldn't you exit? How does the kernel manipulate path resolution for a chroot-ed process?

What happens if you try to run commands like nano, python3, or grep inside your jail? Explain why they fail, even if they are installed on your host OS.

Part 3: Deep-Dive Discussion — The Privilege Escape Paradox

New questions! 1. The user I mentioned isn’t happy with the limitations discussed in Part 2 above. Fix them! Add all of /bin and /lib, your user should be able to access a compiler and other tooling.

Now various other commands are available to your sandboxed user, consider the below issue. You find out that your user was able to gain access to your secret file! How? Demonstrate at least one method of escaping the sandbox on your student VM, and demonstrate in your writeup that this works.

Hint: consider mount.

While chroot effectively shifts the file path layout for standard operations, it was never designed to be a true security sandbox.

chroot modifies path resolution but does not isolate processes, namespaces, or administrative permissions.

Why would this be a problem? The Double-Chroot Escape Vulnerability

If an untrusted process inside a chroot sandbox manages to maintain or upgrade its privileges to root (UID 0), it can easily bypass the filesystem barrier using a well-known exploit technique colloquially called a Double-Chroot Escape. How the Escape Works

The attacker s running inside the jail, but retains full system admin capability (root).

The attacker calls open(".", ...) to grab a direct hardware handle (file descriptor) pointing to the jail's current root directory.

The attacker creates a temporary subfolder inside the jail (e.g., /breakout) and issues a new chroot command targeting that deeper folder.

The kernel updates the process's internal root pointer to point to /breakout. Crucially, the old root folder handle from step 2 is now technically outside the process's new active root boundary.

Because the attacker has a raw file descriptor to a directory outside its current root, they can call fchdir() onto that handle, and execute cd .. repeatedly. The kernel can no longer stop the traversal because the boundary calculation breaks down when navigating upwards from an external file handle.

The attacker arrives at the true host root, issues one final chroot("."), and breaks completely into the host filesystem with full administrative permissions intact.

Class 6 - Human security

What, in your own words, is ‘cyber resilience’ and how is it different than ‘protection’ or ’security’?
    a. What is one thing you can do to build cyber resilience?
    b. What might get in the way of building cyber resilience?
    c. Why is cyber resilience an effective response to human-centric vulnerabilities?

A cybersecurity culture is defined as the “values, attitudes and beliefs that drive cyber-resilient behaviors”. The culture is created by managerial mechanisms like governance mechanisms, reward systems, consequences, leadership, priorities, performance evaluation, and more.
    a. What are 2-3 behaviors you would like to see the people do to help the organization be cyber secure?
    b. What are the values, attitudes and beliefs that would drive these behaviors?
    c. What are the mechanisms you can employ to create the values, attitudes and beliefs?

What do you consider the top 2 human-centric security concerns or vulnerabilities from the use of GenAI tools (ChatGPT, Claude, etc)?
    a. For each concern, explain why this is a concern? Provide a short example to illustrate the risk you see. Be as specific as possible.
    b. What behavior(s) would minimize the risk of a negative impact for this concern?
    c. What is a mechanism you can use to drive the values, attitudes, and beliefs that would encourage the behavior(s) you seek?