Обновить
70
Антон Кортунов@ToSHiC

Программист

25
Подписчики
Отправить сообщение
Ага, сделать какой нибудь специальный флажок на mmap, который будет выделенные страницы помечать таким образом — и в userspace приложениях можно будет выделять «защищённую» память для критически важны данных.
В ядре linux есть специальная функция, которая правильно размечает страницы: elixir.free-electrons.com/linux/v4.6/source/arch/x86/mm/pageattr.c#L1517
Перечитал ещё раз, согласен — выглядит как решение. Интересно, можно ли использовать для защиты от spectre.
Обратите внимание, что в кэш подтягивается строка из userspace_array. А будут ли те данные, которые располагаются по адресу из kernel_space_ptr, в кэше или нет — без разницы.
Судя по git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5aa90a84589282b87666f92b6c3c917c8080a9bf, за счёт аккуратной работы с PCID всё не на столько плохо, как могло бы быть. CR3 переключают дважды, если pti включен.
Смотря что именно вы имеете в виду под уязвимостью. FLUSH+RELOAD — способ передачи данных через наличие/отсутствие закешированных линий был опубликован как минимум в 2014 году на USENIX, возможно и раньше где-то про него говорили. Автор тут про TLB распинается, но суть примерно та же. Свежее в Meltdown-Spectre — это эксплуатация спекулятивного исполнения кода и отравление предсказателя переходов.
Это локальная атака.
Скорость чтения памяти ядра в Meltdown, по заявлению авторов — порядка 500КБ/с.
Можно только читать.
Всё это есть в оригинальной статье :)
Скажите, а это вы — автор статьи www.securitylab.ru/analytics/490642.php? Если да, то ответьте, пожалуйста, на вопросы:

Если из юзерспейса нельзя сбросить TLB, то как гуглерам удалось прочитать полный дамп памяти? На каждый байт создавать массив на 2 мегабайта? Ну пусть даже побитовое чтение, всё равно 8 килобайт на каждый бит — это дофига.

И если дело именно в TLB, то почему работают примеры реализации meltdown, которые используют clflush и переиспользуют один и тот же массив, выделенный единожды?
github.com/gkaindl/meltdown-poc/blob/master/meltdown.c
github.com/paboldin/meltdown-exploit/blob/master/meltdown.c
На сколько я помню, надо в винде делать undock, тогда кнопка разблокируется.
Ответил чуть ниже.
Цитаты из оригинальной статьи:
Flush+Reload attacks work on
a single cache line granularity. These attacks exploit the
shared, inclusive last-level cache. An attacker frequently
flushes a targeted memory location using the clflush
instruction. By measuring the time it takes to reload the
data, the attacker determines whether data was loaded
into the cache by another process in the meantime.

As already discussed, we utilize cache attacks that allow
to build fast and low-noise covert channel using the
CPU’s cache. Thus, the transient instruction sequence
has to encode the secret into the microarchitectural cache
state, similarly to the toy example in Section 3.
We allocate a probe array in memory and ensure that
no part of this array is cached. To transmit the secret, the
transient instruction sequence contains an indirect memory
access to an address which is calculated based on the
secret (inaccessible) value. In line 5 of Listing 2 the secret
value from step 1 is multiplied by the page size, i.e.,
4 KB. The multiplication of the secret ensures that accesses
to the array have a large spatial distance to each
other. This prevents the hardware prefetcher from loading
adjacent memory locations into the cache as well.
Here, we read a single byte at once, hence our probe array
is 256×4096 bytes, assuming 4 KB pages.

Цитата из FLUSH+RELOAD: a High Resolution, Low Noise,
L3 Cache Side-Channel Attack
:
We observe that the clflush instruction evicts the
memory line from all the cache levels, including from
the shared Last-Level-Cache (LLC). Based on this observation
we design the FLUSH+RELOAD attack—an extension
of the Gullasch et al. attack. Unlike the original
attack, FLUSH+RELOAD is a cross-core attack, allowing
the spy and the victim to execute in parallel on different
execution cores. FLUSH+RELOAD further extends
the Gullasch et al. attack by adapting it to a virtualised
environment, allowing cross-VM attacks.
Two properties of the FLUSH+RELOAD attack make
it more powerful, and hence more dangerous, than prior
micro-architectural side-channel attacks. The first is that
the attack identifies access to specific memory lines,
whereas most prior attacks identify access to larger
classes of locations, such as specific cache sets. Consequently,
FLUSH+RELOAD has a high fidelity, does not
suffer from false positives and does not require additional
processing for detecting access. While the Gullasch et al.
attack also identifies access to specific memory lines, the
attack frequently interrupts the victim process and as a
result also suffers from false positives.
The second advantage of the FLUSH+RELOAD attack
is that it focuses on the LLC, which is the cache level
furthest from the processors cores (i.e., L2 in processors
with two cache levels and L3 in processors with
three). The LLC is shared by multiple cores on the
same processor die. While some prior attacks do use the
LLC [47, 60], all of these attacks have a very low resolution
and cannot, therefore, attain the fine granularity
required, for example, for cryptanalysis.
В памяти ядра есть вся замапленная физическая память, и все нужные структуры, чтобы прочитать в итоге память любого процесса.
обратите внимание, что i умножается на 4096, как раз для того, чтобы исключить этот эффект, и ещё несколько.
1. Нет, в Meltdown идёт чтение страниц памяти ядра, атакуемой программы вообще нету.
2. Да, но это можно оптимизировать. Засечь можно разве что слишком большое количество исключений, но это не является гарантией того, что программа пытается читать память ядра.
3. Совершенно непонятно, что вы имели в виду. Почитайте, как устроен ассоциативный кэш, например вот тут: habrahabr.ru/post/93263

В оригинальной статье авторы пишут, что смогли читать память ядра со скоростью около 500КБ/с, что, конечно, не быстро, но уж точно доказывает, что атака работает.
olartamonov на гигтаймс разместил статью про spectre, вот ключевая часть второго типа атаки, наиболее интересная:

То есть, если мы знаем, что нужная нам инструкция в атакуемой программе лежит по адресу 123456, а также в этой программе есть регулярно исполняемый косвенный переход. В атакующей программе мы пишем конструкцию, максимально похожую на переход в атакуемой, но при этом всегда выполняющую переход по адресу 123456. В нашем адресном пространстве, конечно, абсолютно валидный и легальный переход. Что именно у нас лежит по адресу 123456, никакого значения не имеет.

Через некоторое время блок предсказания переходов абсолютно уверен, что все переходы такого вида ведут на адрес 123456, поэтому, когда атакуемая программа — с нашей подачи или по своей инициативе — доходит до аналогичного перехода, процессор радостно начинает спекулятивное исполнение инструкций с адреса 123456. Уже в адресном пространстве атакуемой программы.

Через некоторое время настоящий адрес перехода будет вычислен, процессор осознает ошибку и отбросит результаты спекулятивного выполнения, однако, как и во всех прочих случаях применения Meltdown и Spectre, от него останутся следы в кэше.


Под «нужной инструкцией» по адресу 123456 имеется в виду тот самый «гаджет» (кусок кода атакуемой программы, или из какой либо библиотеки, которую она использует), который оставит такие следы в кэше, которые легко отследить из атакующего процесса.
Так это уже есть — собственно, именно так защищаются страницы нулевого кольца от третьего. Или вы предлагаете явно ставить барьер при обращении к ядерным страницам, который будет запрещать реордеринг?
Meltdown — это чтение защищённых страниц памяти, гонка между отработкой исключения и чтением памяти.
Spectre — отравление или обман предсказателя переходов. Чтение происходит из своего адресного пространства.

Side channel при этом одинаковый — кеш данных.

Из кеша ничего не читается, но можно сделать полный дамп памяти ядра. А потом применить volatility.

Вот такой кусок кода даст примерно такой же ассемблерный код:


struct foo {
    char flags;
    char padding[4095];
};

struct foo array[256];
unsugned char* index_ptr;

do {
    char flags = array[*index_ptr];
} while (flags == 0);

И этот код вполне себе обычный.

Информация

В рейтинге
4 322-й
Откуда
Россия
Зарегистрирован
Активность