Как атака Spectre считывает кэш, который он обманул для загрузки ЦП?
Я понимаю ту часть статьи, где они обманывают процессор, чтобы спекулятивно загрузить часть памяти жертвы в кэш процессора. Часть я не понимаю, как они получают его из кэша.
3 ответа
Они не извлекают его напрямую (за пределами считанных байтов процессор не "удаляется" и не может быть виден атакующему при атаке).
Вектор атаки состоит в том, чтобы делать "поиск" поочередно. После того, как кэш ЦП был подготовлен (очищает кэш там, где он должен быть) и "научен", что ветвь if проходит, пока условие полагается на не кэшированные данные, ЦП умозрительно выполняет пару строк из если область действия, включая доступ за пределы допустимого диапазона (предоставление байта B), а затем немедленно получить доступ к некоторому авторизованному некэшируемому массиву по индексу, который зависит от одного бита секретного B (B никогда не будет непосредственно виден атакующему)). Наконец, злоумышленник извлекает тот же самый массив разрешенных данных, скажем, из индекса, вычисленного с помощью бита B, скажем, ноля: если извлечение этого байта ok выполняется быстро, данные все еще находились в кэше, то есть бит B равен нулю. Если поиск (относительно) медленный, ЦП должен был загрузить в свой кеш данные, которые подтвердили данные, что означало, что раньше это не происходило, то есть бит B был равен единице.
Например, Cond
, все ValidArray
не кэшируется, LargeEnough
достаточно большой, чтобы процессор не загружал оба ValidArray[ valid-index + 0 ]
а также ValidArray[ valid-index + LargeEnough ]
в своем тайнике за один выстрел
if ( Cond ) {
// the next 2 lines are only speculatively executed
V = SomeArray[ out-of-bounds-attacked-index ]
Dummy = ValidArray [ valid-index + ( V & bit ) * LargeEnough ]
}
// the next code is always retired (executed, not only speculatively)
t1 = get_cpu_precise_time()
Dummy2 = ValidArray [ valid-index ]
diff = get_cpu_precise_time() - t1
if (diff > SOME_CALCULATED_VALUE) {
// bit was its value (1, or 2, or 4, or ... 128)
}
else {
// bit was 0
}
где bit
пробуют последовательно быть первым 0x01
, затем 0x02
... чтобы 0x80
, Измеряя "время" (количество циклов ЦП), который "следующий" код берет для каждого бита, выявляется значение V:
- если
ValidArray[ valid-index + 0 ]
находится в кеше,V & bit
является0
- иначе
V & bit
являетсяbit
Это занимает время, каждый бит требует подготовки кэш-памяти L1 ЦП, пытается несколько раз один и тот же бит, чтобы минимизировать ошибки синхронизации и т. Д.
Тогда правильная атака "смещение" должна быть определена, чтобы прочитать интересную область.
Умная атака, но не так просто реализовать.
как они извлекают его из кеша
По сути, секрет, полученный спекулятивно, сразу же используется как индекс для чтения из другого массива, называемого side_effects
, Все, что нам нужно, это "прикоснуться" к указателю в side_effects
массив, поэтому соответствующий элемент попадает из памяти в кеш процессора:
secret = base_array[huge_index_to_a_secret];
tmp = side_effects[secret * PAGE_SIZE];
Тогда задержка для доступа к каждому элементу в side_effects
Массив измеряется и сравнивается со временем доступа к памяти:
for (i = 0; i < 256; i++) {
start = time();
tmp = side_effects[i * PAGE_SIZE];
latency = time() - start;
if (latency < MIN_MEMORY_ACCESS_TIME)
return i; // so, thas was the secret!
}
Если время ожидания меньше минимального времени доступа к памяти, элемент находится в кеше, поэтому секрет был текущим индексом. Если задержка высока, элемент не находится в кеше, поэтому мы продолжаем наши измерения.
Таким образом, в основном мы не получаем какую-либо информацию напрямую, скорее, мы касаемся некоторой памяти во время умозрительного выполнения и затем наблюдаем побочные эффекты.
Вот основанное на Призраке доказательство концепции Meltdown в 99 строках кода, которое вам может быть легче понять, чем другие PoC: https://github.com/berestovskyy/spectre-meltdown
В общем, эта техника называется атакой по боковому каналу, и дополнительную информацию можно найти в Википедии: https://en.wikipedia.org/wiki/Side-channel_attack
Я хотел бы добавить одну часть информации к уже существующим ответам, а именно, как злоумышленник может фактически исследовать массив из процесса-жертвы на этапе зондирования. Это проблема, потому что Spectre (в отличие от Meltdown) запускается в процессе жертвы и даже через кеш злоумышленник не может просто запросить массивы из других процессов.
Вкратце: при использовании Spectre для атаки FLUSH+RELOAD требуется KSM или другой метод для разделяемой памяти. Таким образом, злоумышленник (насколько я понимаю) может реплицировать соответствующие части памяти жертвы в своем собственном адресном пространстве и, таким образом, сможет запрашивать в кеше время доступа к массиву зондов.
Длинное объяснение:
Одно большое различие между Meltdown и Spectre заключается в том, что в Meltdown вся атака выполняется в адресном пространстве злоумышленника. Таким образом, совершенно ясно, как злоумышленник может одновременно вносить изменения в кеш и читать кеш. Однако со Spectre сама атака проходит в процессе жертвы. Используя так называемые гаджеты , жертва будет выполнять код, который записывает секретные данные в индекс массива зондов, например, с помощью
a = array2[array1[x] * 4096]
.
Доказательства концепции, которые были связаны в других ответах, реализуют базовую концепцию ветвления/спекуляций Spectre, но весь код, похоже, выполняется в одном и том же процессе. Таким образом, конечно, нет никакой проблемы в том, чтобы код гаджета записывался, а затем считывался для проверки. Однако в реальном сценарии процесс-жертва будет писать в адрес, который также находится в процессе-жертве.
Теперь проблема, которую, на мой взгляд, в статье недостаточно хорошо объясняют, заключается в том, что злоумышленник должен иметь возможность исследовать кэш в поисках массива адресного пространства жертвы (). Теоретически это можно было сделать либо снова из-под жертвы, либо из адресного пространства злоумышленников.
В исходной статье это описывается лишь расплывчато, вероятно, потому, что авторам было ясно:
На заключительном этапе восстанавливаются конфиденциальные данные. Для атак Spectre с использованием Flush+Reload или Evict+Reload процесс восстановления состоит из синхронизации доступа к адресам памяти в отслеживаемых строках кэша.
Чтобы завершить атаку, злоумышленник измеряет, какое место в массиве2 было помещено в кеш, например, с помощью Flush+Reload или Prime+Probe.
Доступ к кешу из адресного пространства жертвы был бы возможен, но для этого потребовался бы другой гаджет, и злоумышленник должен был бы иметь возможность инициировать выполнение этого гаджета. Мне это казалось совершенно нереальным, особенно в Spectre-PHT.
В статье « Обнаружение атак Spectre путем выявления атак по побочным каналам кэша с использованием машинного обучения» я нашел отсутствующее объяснение:
Чтобы атака FLUSH+RELOAD сработала в этом случае, должны быть выполнены три предварительных условия. [...] Но самое главное, ЦП должен иметь такой механизм, как слияние одной и той же страницы ядра (KSM) [4] или прозрачное совместное использование страниц (TPS) [54] [10].
KSM позволяет процессам совместно использовать страницы, объединяя разные виртуальные адреса в одну и ту же страницу, если они ссылаются на один и тот же физический адрес. Таким образом, увеличивается плотность памяти, что позволяет более эффективно использовать память. KSM впервые был реализован в Linux2.6.32 и включен по умолчанию [33].
KSM объясняет, как злоумышленник может получить доступ
array2
это обычно было бы доступно только в рамках процесса жертвы.