- Взлом и защита шифрования дисков LUKS
- Суть проблемы
- Практическая демонстрация
- Меры защиты
- Шифрование загрузочного раздела
- Использование TPM для хранения ключа шифрования и валидации безопасной среды загрузки
- Использование UEFI Secure Boot для полного покрытия загрузочной цепи электронной подписью
- Доступное решение
- Создание зашифрованного LUKS-раздела на Linux
- Предварительная подготовка
- Генерация ключа
- Шифрование раздела
- Монтирование тома
Взлом и защита шифрования дисков LUKS
Шифрование дисков предназначено для защиты данных в компьютере от несанкционированного физического доступа. Бытует распространённое заблуждение, что дисковое шифрование с этой задачей действительно справляется, а сценарии, в которых это не так, представляются уж слишком экзотическими и нереалистичными. В этой статье показано, что извлечение мастер-ключа шифрованного тома LUKS легко осуществимо на практике, и предложен (давно не новый) метод защиты.
Суть проблемы
Отдельно стоит остановиться на предназначении дискового шифрования. Действительно, когда физический доступ невозможен и данными владеет запущенная система, проблем никаких нет. Могут быть проблемы с безопасностью самой системы, но тут шифрование дисков никак не поможет. Дисковое шифрование должно оберегать данные, когда у любопытствующей стороны есть возможность получить доступ к дискам минуя систему, например физически подключив диски к своей системе или загрузив свою ОС на инспектируемом компьютере. Сценарий физического доступа — единственный сценарий, при котором дисковое шифрование имеет какой-то смысл.
Проблема состоит в том, что атакующий может незаметно вмешаться в цепь загрузки ОС и вынудить систему выдать ключи шифрования, как только она их получит при очередном запуске.
Такая атака требует лишь одного акта доступа к компьютеру: данные с диска можно скопировать совместно с подменой цепи загрузки, а потом расшифровать их, дождавшись появления ключа. В сравнении с незашифрованными дисками неудобство состоит только в том, что нужно озаботиться тем, как ключ будет передан, и дождаться запуска.
Далее перейдём к демонстрации такой техники на практике. Может оказаться так, что для её реализации атакующему потребуется меньше усилий, чем владелец системы затратил на настройку какого-то своего экзотического метода разблокировки дисков (например, удалённо).
Практическая демонстрация
Демо я проведу на примере виртуальной машины с Debian 9, на которой шифрование дисков было включено при установке системы.
Установка Debian 9 с шифрованием создаёт загрузочный раздел и раздел с шифрованным LVM. Снимок экрана установленной системы с запросом пароля расшифровки для наглядности:
Всё готово, можно приступать. Выключаем машину, копируем диск. В моем случае это выглядит так:
Монтируем диск машины, извлекаем инитрамдрайв:
Готово, можно редактировать инитрамдрайв. Зная, что машина имеет постоянное сетевое подключение, я хочу организовать зашифрованную отправку мастер-ключа после открытия дисков. Для этого мне потребуется:
- Утилита для шифрованной отправки по сети. Добавляю её в /sbin
- Шелл-скрипт для извлечения ключа и отправки. Отправляется в /scripts/local-top и добавляется в список /scripts/local-top/ORDER после cryptoroot .
- Недостающий родной скрипт обработки событий udhcpc, чтобы запустить автонастройку сети прямо в рамдрайве, пользуясь встроенными средствами. Его законное место в /etc/udhcpc/default.script
Исполняемый файл secsend собран статически, чтобы устранить зависимости от каких-либо библиотек. При обычных условиях сборка даёт на выходе файл размером 2,7 МБ, что довольно ощутимо по сравнению с размером рамдрайва — 62 мегабайта в распакованном виде и 20 в сжатом. Однако, при сборке всех библиотек и исполняемого файла с минималистичной musl libc размер выходного файла получается
250 КБ и 120 КБ после сжатия UPX. Сам secsend просто читает стандартный вход, шифрует его cryptobox-ом из libsodium с использованием заданного публичного ключа Curve25519 и отправляет данные на заданный адрес по TCP. Его использование непринципиально для основной цели демонстрации, он скорее показывает что атакующий по сути ничем не ограничен: можно запускать код, который делает что хочет атакующий и как он этого хочет.
После добавления этих трёх файлов и редактирования ещё одного можно запаковывать всё обратно и возвращать изменённый файл на место:
Потребуется некоторый сервер для приёма зашифрованного мастер-ключа, например такой (Python 3.5.3+). Запустив его с указанием секретной части ключевой пары, дожидаемся, пока условная жертва включит свой компьютер:
При включении виртуальной машины с зашифрованным диском всё внешне выглядит как обычно, ничего не изменилось:
А вот на стороне слушателя подключений появился секретный мастер-ключ:
С этого момента сама виртуальная машина с данными и её пользователь со знанием пароля шифрования уже не представляют интереса для злоумышленника. Особо отмечу, что смена парольной фразы не меняет мастер-ключ, которым зашифрован весь том. Даже если между снятием копии и отправкой ключа как-то затесалась смена парольной фразы — это не помеха. Воспользуемся мастер-ключом для открытия тома. Для этого преобразуем его 16ричную запись в логе в бинарный файл:
Монтируем диски со снятой копии:
Меры защиты
Как можно заключить — корень проблемы в запуске недоверенного кода. Вот небольшой обзор методик, которые стоит рассмотреть в контексте этого вопроса.
Шифрование загрузочного раздела
Некоторые дистрибутивы предлагают и такую возможность при установке (например OpenSuSE). В таком случае загрузочный раздел расшифровывается загрузчиком, а затем с него загружаются ядро и инитрамдрайв. Такой подход не имеет особого смысла по следующим причинам:
- Самый главный вопрос с подменой кода всё равно остаётся открытым. Только теперь подменять нужно будет загрузчик.
- Для загрузочного раздела важнее не конфиденциальность данных, а целостность данных. Обычное шифрование LUKS не предоставляет такой гарантии. Некоторая выгода здесь заключается только в том, что на таком зашифрованном разделе трудно сформировать осмысленную подмену.
- И шифрование LUKS2 с проверкой целостности (dm-integrity) тоже не защищает от вмешательств, потому что оно не даёт гарантий против атак, связанных с повторным воспроизведением секторов. Например, имея дамп такого раздела и конфиг загрузчика на нём, всё равно можно взять и откатить ядро на состояние, скопированное ранее. Это не даёт преимуществ конкретно в вопросе извлечения ключа (разве что если старое ядро было уязвимо и это можно каким-то образом использовать), это скорее довод в пользу бесполезности шифрования загрузочного раздела.
Использование TPM для хранения ключа шифрования и валидации безопасной среды загрузки
Однако в линуксе поддержка TPM пока находится в зачаточном состоянии. Загрузчик TrustedGRUB2 (приспособленный для работы с TPM загрузчик) не поддерживает UEFI и от этого пропадает весь смысл затеи. Кроме того наличие рабочего TPM 2.0 только сейчас начинает появляться в железе, зачастую вместе с обновлениями BIOS. Большинство материнских плат не имеют дискретного TPM-модуля, вместо этого TPM программно реализован внутри Intel ME . По всем этим причинам я пока не рассматриваю такую конфигурацию как рабочую и пригодную для широкого использования.
Использование UEFI Secure Boot для полного покрытия загрузочной цепи электронной подписью
Существуют дистрибутивы (Fedora, OpenSuSE) и одиночные решения, которые позволяют использовать Secure Boot в Linux. Однако, коробочные решения зачастую не обеспечивают целостность кода в цепи загрузки. Они предназначены преимущественно для того, чтобы Linux просто запускался при включенном Secure Boot. Обычно просто используется EFI shim, подписанный сертификатом Microsoft, который дальше запускает всё что угодно. Соответственно, при использовании внешнего сертифицирования покрыть подписью инитрамдрайв, который генерируется прямо в установленной системе, просто невозможно.
- Укрощаем UEFI SecureBoot — первая статья на хабре на эту тему, очень подробная.
- Используем Secure Boot в Linux на всю катушку — здесь особенно хорошо написано, почему Secure Boot с установленными сертификатами Microsoft эквивалентен его отсутствию.
Требуемый результат получается во второй статье. Подпись инитрамдрайва достигается слиянием рамдрайва и ядра в одно EFI-приложение, без использования загрузчика, и UEFI напрямую проверяет подпись сразу оптом. Оба руководства требуют массу ручной работы на каждой защищаемой системе.
Доступное решение
Мне встретился подход к полноценному внедрению Secure Boot, совместимый с общепринятой схемой загрузки и не требующий серьёзного вмешательства в систему: отдельный загрузчик, отдельный рамдрайв, отдельное ядро. UEFI проверяет подпись только загрузчика GRUB2, загрузчик имеет вшитый конфиг с ключом для проверки подписи и паролем администратора, и дальше проверяет ядро и рамдрайв. Подписанный загрузчик устанавливается параллельно со старым и при необходимости сохраняется возможность запуститься обычным образом, выключив Secure Boot. Разумеется, эта возможность должна быть закрыта паролем администратора в меню настроек UEFI.
Я решил автоматизировать процесс внедрения Secure Boot с собственным PKI и сделать его простым и независимым от дистрибутива насколько возможно. В результате получился вот такой набор из рецепта Makefile и утилит: https://github.com/Snawoot/linux-secureboot-kit. Для debian, ubuntu, fedora и centos весь процесс требует всего несколько команд.
Конкретно на примере Debian 9 установка выглядит примерно следующим образом (предполагая, что UEFI уже в Setup Mode):
Здесь все команды введены от имени суперпользователя. В итоге остаётся только убедиться, что Secure Boot включён в меню BIOS и защитить настройки BIOS паролем администратора.
А вот как выглядит попытка подмены рамдрайва на такой инсталляции:
Подмена загрузчика (внешний вид зависит от платформы):
Одного лишь дискового шифрования недостаточно для обеспечения конфиденциальности данных. Подпись всей цепи загрузки с использованием UEFI Secure Boot и GPG позволяет достичь хорошего уровня защиты от подмены исполняемого кода при условии, что эксплуатант компьютера способен распознать сброс или подмену системной платы, или даже всего компьютера. В противном случае крайне трудно предложить адекватные способы защиты, если пользователь готов ввести пароль/передать ключ в любую машину, которая случайно оказалась на столе или в серверной.
ОБНОВЛЕНИЕ (2020-09-24 20:24:24+00:00): NSA опубликовало технический отчёт со схожими рекомендациями по усилению безопасности загрузочной цепи: ссылка, зеркало.
Источник
Создание зашифрованного LUKS-раздела на Linux
Операционные системы семейства Linux предоставляют функцию шифрования жестких дисков через специальные утилиты. Рассмотрим одну из них под названием LUKS. В качестве примера используется Ubuntu Server 18.04.
Предварительная подготовка
Утилиту LUKS используют в качестве стандарта для защиты дисков в Linux-системах. Ее преимущества:
- Она бесплатна.
- Совместима с 99% операционных систем Linux/Unix.
- Позволяет сбросить пароль или кодовую фразу в случае утери либо компрометации.
Прежде чем шифровать диск, необходимо определиться с выбором. Запустим в терминале следующую утилиту:
Она отобразит информацию о свободных томах, а также о разделах, которые доступны для защиты:
Скриншот №1. Перечень свободных логических дисков.
Выберем один из списка и перейдем к редактированию. В качестве примера будем использовать раздел /dev/sdb.
Запустим программу parted, указав выбранный том:
sudo parted /dev/sdb
Администратору будет доступен функционал утилиты через терминал.
Важно! Чтобы получить подробную информацию об утилите parted, введите ключ help.
Получив доступ к parted, разметим выбранный раздел соответствующим образом. Отредактируем под GPT:
Программа отобразит предупреждение о том, что вся информация будет удалена во время выполнения операции. Вводим «Yes»:
Скриншот №2. Удаление данных с тома.
После окончания операции создадим новый том как основной:
mkpart primary 1 -1
Его метка будет отображаться как «1». По окончании выходим из программы через команду quit.
Генерация ключа
Создадим ключ шифрования, используя команду DD:
sudo dd if=/dev/urandom of=/root/secret.key bs=1024 count=2
, где /root/secret.key — имя генерируемого ключа. Пользователь указывает его самостоятельно.
Теперь отредактируем права доступа к файлу:
sudo chmod 0400 /root/secret.key
Присвоим ему функцию на чтение «без редактирования».
Шифрование раздела
Следующий этап — шифрование данных. По умолчанию LUKS интегрирована в большинство дистрибутивов. Если утилита недоступна, установим её, используя стандартные средства установки. Для примера с Ubuntu Server:
sudo apt-get install cryptsetup
После установки создадим шифрованный том с использованием сгенерированного ключа:
cryptsetup luksFormat /dev/sdb1 /root/secret.key
Программа выдаст предупреждающее сообщение о том, что информация будет удалена после форматирования. Вводим «Yes»:
Скриншот №3. Стирание диска.
Теперь свяжем сгенерированный ключ с томом /dev/sdb1. Это позволит в дальнейшем пропускать процедуру авторизации при каждом обращении к разделу, а также скроет подсказки к паролю от других пользователей:
cryptsetup luksAddKey /dev/sdb1 /root/secret.key —key-file=/root/secret.key
После выполнения операции перейдем к конфигурированию. Сначала создадим промежуточный вариант подключения к шифрованному объекту. Для этого воспользуемся функцией LuksOpen:
cryptsetup luksOpen /dev/sdb1 secret —key-file=/root/secret.key
По окончании устройство будет доступно по пути /dev/mapper/secret.
Следующим шагом указываем размер тома, который требуется зашифровать:
cryptsetup resize secret
В нашем примере имя тома — secret. Если для опции resize не указываются дополнительные флаги, она занимает весь раздел. Смонтируем точку доступа для файловой структуры ext4 с именем тома secret:
Для проверки правильности выполненных действий вводим:
cryptsetup -v status secret
Монтирование тома
Раздел зашифрован — смонтируем его для операционной системы. Указываем точку входа для тома secret, а потом задаем права на доступ:
sudo mkdir -p /secret
sudo chmod 755 /secret
Через команду mount подключим раздел к ОС:
mount /dev/mapper/secret /secret
После этого проверим выполнение операции:
Чтобы постоянно не подключить LUKS-раздел к операционной системе, активируем автомонтирование тома.
Важно! Использование автоматического режима монтирования также автоматически расшифровывает том в момент его подключения к ОС.
Определим идентификатор защищенного диска:
ls -l /dev/disk/by-uuid
Он обозначается термином UUID. Поскольку наименование сложное для запоминания, выгрузим его имя в отдельный файл:
Важно! После знака «=» идет идентификатор, который используется в примере.
Создадим ссылку на сгенерированный ранее ключ secret.key:
sudo echo «secret UUID=$
Последний шаг — добавляем соответствующие строки в файл монтирования разделов fstab:
sudo echo «/dev/mapper/secret /secret auto» >> /etc/fstab
Указав в терминале команду sudo mount –a, зашифрованный раздел автоматически будет подключаться при старте операционной системы.
Источник