Linux с двойным дном

Специальный корреспондент
Собака

Собака

Пресс-служба
Команда форума
Private Club
Регистрация
13/10/15
Сообщения
54.785
Репутация
62.290
Реакции
276.912
RUB
0
В этой статье я расскажу как сделать так, чтобы ваша линуксовая машинка выглядела невинной игрушкой, но при вводе нескольких команд превращалась в настоящую боевую единицу. Конечно, у вас могут найти на диске сектора с необычно высокой энтропией, несколько подозрительных системных настроек, но никаких явных зашифрованных разделов, файлов, или сторонних шифровалок. Конечно, вас могут спросить - "а для чего тебе cryptsetup, сынок?", на что вы ответите - "это же Linux Mint, это всё искаропки!" Хуже, если бы вас спросили: зачем ты используешь LUKS, или, что ещё хуже, зачем ты поставил VeraCrypt или Shufflecake.

В любом случае как отмазываться - не тема этой статьи. В комментариях расскажут всё, и даже гораздо больше. Я лишь описываю способ со всеми его достоинствами и недостатками, а уж анализ рисков - на ваше усмотрение.

Главное в системе с двойным дном - это, конечно же, секретные зашифрованные разделы, которые нигде не отсвечивают. Мой способ - это cryptsetup поверх losetup. Особенность losetup в том, что он может создать устройство в любом месте - даже на примонтированной файловой системе, и при этом не станет возмущаться, как dm, что устройство уже используется. Способ не работает на слабых машинках с диском, подключенным через USB, таких как, например, NanoPI Zero, у которых 32-битный Allwinner H3, но начиная с 64-битных H6 или Rockchip RK3328 и выше, а тем более на всяких ваших Intel и AMD никаких проблем я не наблюдаю уже несколько лет.

Но! Проблемы появляются, если объединить много loop устройств в одно с помощью dm. То есть, использовать все более-менее крупные свободные блоки файловой системы ext4. Даже мой i3 такое не вытянул. Я не помню точно, какие ошибки были в dmesg, но что-то связанное с асинхронной обработкой внутри ядра. На слабеньких 32-битных Allwinner - то же самое даже с одним loop. Глубоко в проблему я не нырял, просто перестал использовать проблемный подход.

Самая удобная файловая система для скрытых разделов - конечно, же FAT. Создаёшь обычный раздел, скидываешь туда фотки котиков для отмазки, и всё остальное место остаётся доступным в виде одного цельного блока. Конечно, со скрытым разделом ничего писать в файловую систему уже не стоит, поэтому либо соблюдаем аккуратность, либо монтируем только на чтение, либо не монтируем вообще. И не забываем про бэкапы. А на случай, если вас всё-таки попросят туда что-нибудь записать, создаём скрытый раздел не прям сразу с первого свободного сектора, а чуть подальше, учитывая последовательное распределение.

Ext4 гораздо хуже, там сильная фрагментация свободного места, тем не менее, для небольших оверлейных разделов /etc, /var, и т.п. вполне годится.

Вы, конечно, спросите, а как найти нетронутое свободное место на просторах файловой системы, да и вообще на диске? Возможно, есть готовое решение, но для себя я всё привык делать сам, и у меня для этого есть несложный скрипт. Идея простая: заполняем весь диск случайными данными из /dev/urandom, вычисляем и сохраняем в файле хэши секторов, устанавливаем систему, а потом снова вычисляем хэши, смотрим, какие сектора изменились, и таким образом находим нетронутые блоки.

К тому же, заполнение диска случайными данными - это ещё и обязательный шаг инициализации. Если создать шифрованный раздел сразу, то легко найти где он начинается и где заканчивается. А когда замусорен весь диск - попробуй, пойми, есть ли там вообще что-либо зашифрованное.

Тут, конечно, стоит упомянуть о маркерах. В алгоритмах шифрования могут быть намеренные изъяны, которые приводят к определённым сигнатурам в зашифрованных данных. То есть, вы можете скачать какую-нибудь порнуху на зашифрованный раздел и на этом попасться. Так что, поаккуратнее. Защититься от этого можно двойным шифрованием, cryptsetup поверх cryptsetup, желательно разными алгоритмами. При необходимости вы сами сможете запросто это реализовать.

Готовимся шифроваться​

Вам понадобится мой тулкит, который автоматизирует всю рутину. Скачайте его, только сразу на флешку, которую, впоследствии надо будет хорошенько затереть из /dev/urandom. Следов оставлять нельзя.

Вы сейчас наверняка используете незащищённую систему. Не лезьте на мой гитхаб из браузера - это осядет в истории и будет работать против вас. То, что вы читаете эту статью, ещё можно объяснить любопытством, но скачивание тулкита - это уже намерение и +1 к подозрениям. Допустим, флешка у вас примонтирована в /mnt/flash. Первая команда - отключение записи истории (если у вас не bash, используйте свои методы):

HISTFILE=
swapoff
cd /mnt/flash
git clone [email protected]:amateur80lvl/pdt.git
Инициализируем диск (к примеру, /dev/sdc) случайными данными:

dd if=/dev/urandom of=/dev/sdc bs=4K status=progress
и вычисляем хэши секторов:

python3 secha.py compute /dev/sdc original-sector-hashes
original-sector-hashes - это имя файла для хэшей. Для экономии места я использовал 64-битный blake2s, с которым я не наблюдал коллизий на 128GB SSD - лично мне больше и не надо. Для анализа диска размер хэша не особо критичен, но с 48 битным коллизии уже наблюдаются. Я их искал простой программкой на C++ (на питоне такое не напишешь - сожрёт всю память). Для верности, конечно, можете подкрутить размер хэша до 96 или 128 бит, поправив исходник secha.py.

Подготовка диска​

Сразу устанавливать свой любимый Linux не надо! Инсталляторы, как правило, тупые до безобразия, да и TRIM везде включен по умолчанию. С ним все ваши хэши окажутся бесполезны. Разбивку диска и форматирование разделов надо провести заранее, а инсталятору сказать, чтобы ничего не трогал и ставил куда скажут.

Разделов желательно создать как минимум два: один системный, второй - для скрытых данных. В принципе, можно обойтись одним системным разделом, а скрытый раздел можно создать и на свободной части диска. Но для отмазки второй раздел лучше создать, отформатировать, и накидать туда каких-нибудь файлов.

Да, не забываем про UEFI раздел, если нужен.

Раздел подкачки создавать категорически не рекомендую, без его шифрования все ваши секретные данные из оперативки запросто утекут в открытом виде на диск, а способа скрытого шифрования этого раздела я не знаю. Явное же шифрование вызовет вопросы и подозрения.

TRIM надо отключить обязательно. Сначала на этапе форматирования раздела опцией nodiscard:

mkfs -t ext4 -E nodiscard /dev/sdc1
А потом периодический TRIM, сразу после установки. То есть, надо будет сделать что-то подобное:

systemctl disable fstrim.timer
Отключение TRIM - это палево. Запишите его в столбец рисков. Способ обойтись без этого только один - использовать старые добрые жужжащие HDD. Интернеты рекомендуют выключить TRIM и для них, но по крайней мере это обосновано и вопросов не вызывает.

Установка Linux​

Берёте свой любимый дистрибутив, в котором cryptsetup уже есть по умолчанию - и устанавливаете на предварительно отформатированный раздел. Linux Mint в этом отношении идеален, насчёт других ничего не знаю. Ну, кроме Armbian, разве что. В Debian netinstall cryptsetup по умолчанию отсутствует.

Когда система установлена, выключаем комп, достаём диск для анализов, или загружаем какой-нибудь live дистрибутив с флешки и смотрим, какие области у нас остались нетронутыми:

python3 secha.py find-intact /dev/sdc original-sector-hashes
В принципе, чтобы не анализировать весь диск и пропустить системный раздел, можно задать начальный и конечный секторы и размер сектора, который по умолчанию 512. Эти параметры должны быть одинаковами для secha.py compute и secha.py find-intact.

Скрипт выдаёт размер нетронутых областей, начальный и конечный секторы. Выберите которые вам больше нравятся и запишите. Номера секторов понадобятся для создания скрытых разделов.

Следующий обязательный шаг - настроить использование tmpfs для /tmp и /var/log. В логах, например, можно увидеть факт использования cryptsetup, ну а в /tmp тоже может утечь всякое. Добавляем в /etc/fstab:

tmpfs /tmp tmpfs nosuid,nodev,mode=1755,size=32M 0 1
tmpfs /var/log tmpfs nosuid,noexec,nodev,mode=755,size=4M 0 1
Размер можете указать на свой вкус.

Эти строчки в fstab - тоже палево. Отметьте у себя.

Установка тулкита​

Нам понадобится маленький секретный раздел для конфигурации и тулкита, который бутстрапит систему. Достаточно 360K, как пятидюймовая дискета, но на самом деле размер должен быть такой, который лучше запоминается и выровнен по границе сектора. Самое простое - 512000. Где его расположить - ваша забота. Я могу рекомендовать начало диска, сразу после GPT. Там, начиная от сектора 34 и до начала первого раздела обычно есть по крайней мере один свободный мегабайт. Можно разместить его в конце диска, только имейте ввиду, что там, в последних 34 секторах, находится копия GPT. В случае с MBR проще, - занят только первый сектор диска. А ещё можно оставить дырку между разделами, но тогда у стороннего наблюдателя возникнет вопрос - зачем?

Ну а лучше всего вообще держать этот раздел на внешнем устройстве. Или на другом компьютере и бутстрапить систему через SSH - тулкит это позволяет.

Вобщем, ваша креативная фантазия в сочетании с силой коллективного разума комментаторов наверняка подскажет, где и как его спрятать. Но если будете делать вручную, как я здесь напишу, то этот раздел - самое слабое звено во всей системе. Потому что LUKS мы не используем, а без сильной функции хэширования пароля для шифрованного раздела типа plain всякий пароль, который вы сможете запомнить - ненадёжен. Любой же вспомогательный скрипт нарушит чистоту системы и вызовет подозрения.

Допустим, размер сектора у нас 512 байт и наш бутстрапный раздел будет находиться начиная с сектора 40 и занимать следующие 1000 секторов. Умножаем номера на 512 и создаём loop устройство:

losetup --offset 20480 --sizelimit 512000 -f /dev/sda
Кстати, историю команд не забыли отключить? Можно это сделать перманентно в .bashrc или что вы там используете вместо него, но это +1 к подозрениям.

Следующая команда открывает шифрованный раздел:

cryptsetup open /dev/loop0 bootstrap -y --type plain
Вводим свой заковыристый бутстрапный пароль.

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

mkfs -t ext2 /dev/mapper/bootstrap
Создать ext4 на таком маленьком разделе у вас не получится, поэтому используем ext2. Можно и fat, конечно же, но я не пробовал.

Советую проверить, в состоянии ли вы ввести пароль заново. Закрываем шифрованный раздел и пробуем открыть его заново:

cryptsetup close bootstrap
cryptsetup open /dev/loop0 bootstrap --type plain
Теперь смотрим, показывает ли blkid UUID для /dev/mapper/bootstrap. Если нет, вы ввели пароль неправильно. Попробуйте переоткрыть раздел заново. Если ничто не помогает - начинаем снова с команды форматирования. Повторяем, пока успех не настигнет вас окончательно.

Получилось? Тогда создаём точку монтирования в /tmp и монтируем:

mkdir /mnt/bootstrap
mount /dev/mapper/bootstrap /mnt/bootstrap
Копируем файлы тулкита с флешки на бутстрапный раздел, а флешку тщательно затираем.

Конфигурация тулкита​

Все наши секретные разделы устроены точно так же, и тулкит автоматизирует процесс, что был описан выше. Нам надо подготовить файл конфигурации в формате JSON, который выглядит примерно так:

{
"devices": {
"S23SNEAG516433Y": "ssd",
"WD-WX61BA51RF6A": "hdd"
},
"volumes": {
"my-data": {
"device": "ssd",
"start": "128664014 * 512",
"end": "(((488397168 - 34) * 512) // 4096) * 4096",
"sector_size": 4096,
"key": "KbQbEe9XZ4hPbYEWzZ3XZlbydGnkV0yLCoPSZVIP0cgyxTYC",
"mount_point": "/mnt/my-data"
},
"my-archive": {
"device": "hdd",
"start": "838186828 * 512",
"end": "((1465149168 * 512) // 4096) * 4096",
"sector_size": 4096,
"key": "DtgVIyikwY5rTUvmQwFpzfm6Fze2TvaV9iLbWp2W5eps64TF",
"mount_point": "/mnt/my-archive"
}
},
"containers": [
"devapps",
"safedns"
]
}
Секция devices определяет имена устройств по их серийным номерам. Эти номера можно посмотреть командой

lsblk -d -o NAME,SERIAL
В нашем примере у нас в системе два диска и в секции devices мы говорим, что диск с серийным номером S23SNEAG516433Y у нас будет называться ssd, а второй - hdd. Неважно, какие имена для них вы придумаете.

Секция volumes определяет наши секретные разделы - тома. В нашем случае том my-data у нас расположен на устройстве с именем ssd, а том my-archive - на hdd.

Параметры start и end определяют начальную и конечную позицию раздела в байтах, и могут быть как числами, так и строками-формулами, чтобы не считать байтовые позиции вручную. Например, формула параметра end для раздела my-data вычисляет конечную позицию раздела исходя из размера диска 488397168 секторов по 512 байт минус 34 сектора копии GPT, и выравнивает её по границе 4096-байтного сектора для loop-устройства. Размеры секторов не обязательно должны совпадать. В нашем примере везде используются 4096-байтные сектора, хотя сектора физических устройств - 512 байт.

Параметр key задаёт пароль для losetup и может быть сгенерирован скриптом pdt_genkey.

Вы уже определились, где будете создавать свои секретные разделы? Тогда создадим каталог конфигурации для нашего компа - так удобнее, чтобы не мешать файлы в кучу. К тому же, вы можете добавить каталоги для других машин и бутстрапить их по SSH.

mkdir /mnt/bootstrap/my-hidden-system
Создаём файл конфигурации:

nano /mnt/bootstrap/my-hidden-system/config.json
Ну или vi, если вам так привычнее. Ах да, забыл упомянуть про секцию containers - это для запуска секретных LXC контейнеров. Если они у вас будут, конечно же.

Создание разделов​

Ну что, написали свою конфигурацию? Вряд-ли у вас получится идеально с первого раза, но пока сгодится любой вариант. Потом отполируете, через несколько переустановок.

Итак, конфигурация есть, - создаём тома. Кстати, давайте перейдём в каталог /mnt/bootstrap чтобы не писать его каждый раз:

cd /mnt/bootstrap
Для нашего примера команды будут такими:

./pdt_create_volume my-hidden-system my-data
./pdt_create_volume my-hidden-system my-archive
В общем случае команда создания тома выглядит так:

pdt_create_volume config-dir volume-name [remote-hostname]
Но это вы уже и сами поняли, когда провели аудит кода. Не так ли?

Секретная система​

Очень многие компоненты оставляют следы в самых неожиданных местах, и лучше в базовой системе не работать вообще, а использовать, к примеру, VirtualBox для запуска секретной системы с секретного раздела. Однако, изначально тулкит использовался на ARM машинках, где ни VirtualBox, ни KVM не работали в принципе. Не знаю как сейчас обстоят дела, тем не менее, некоторые каталоги базовой системы надо перемонтировать, чтобы перекрыть возможные утечки от того же VirtualBox, который обязательно нагадит в ваш домашний каталог.

Нам нужно подменить следующие каталоги:

  • в /etc монтируем overlayfs чтобы скрыть дополнительных пользователей, ну и всякую другую конфигурацию, которой в нормальной системе не должно быть. Исключение - /etc/apt, его подмонтируем заново через bind. Вообще, в /etc накапливается много лишних изменений, которые время от времени приходится мержить вручную. Я это делал каждый раз при обновлении системы.
  • в /home монтируем tmpfs и в неё уже монтируем через bind оригинальные пользовательские каталоги, плюс каталоги дополнительных пользователей с секретного раздела. Либо, можно смонтировать в /home каталог с секретного раздела и уже в него через bind - оригинальные пользовательские каталоги.
  • в /root через bind монтируем каталог с секретного раздела. Обычно я там держал совершенно другие ключи для SSH.
  • также, overlayfs используем для /var/tmp и /usr/local
  • если используется LXC, то в /var/lib/lxc монтируем каталог с секретного раздела
Соответственно, вам надо создать все необходимые каталоги на секретных разделах. А потом подготовить bootstrap-скрипт. Вот пример скрипта для маленького сервера с секретным разделом в /mnt/hidden, который бутстрапится через SSH:

#!/usr/bin/env python3

import os
base_dir = os.path.dirname(os.path.abspath(file))

import sys
sys.path.insert(0, os.path.dirname(base_dir))

from pdt_base import read_config, setup, Invoke
from pdt_tasks import *

# читаем конфигурацию и создаём экземпляр Invoke
config = read_config(base_dir)
invoke = Invoke(remote='myserver')
invoke.set_devices(config) # обрабатываем секцию devices

invoke.run('systemctl stop nfs-kernel-server')

setup(
config, invoke,

TmpfsMounts('/mnt'), # монтируем tmpfs в /mnt
MountRoot, # монтируем оригинальную корневую
# файловую систему в /mnt/root
MountVolumes, # монтируем все тома
BindMounts( # заменяем /root
('/mnt/hidden/root', '/root')
)
)

# ключи для SSH для доступа к серверу поменялись
# после замены /root, надо пересоздать экземпляр Invoke
invoke = Invoke(remote='myserver', ssh_key='~/.ssh/secret_id_ecdsa')

# продолжаем
setup(
config, invoke,

# перемонтируем в overlayfs следующие каталоги:
OverlayMounts(
('/mnt/root/etc', '/mnt/hidden/etc', '/mnt/hidden/etc.workdir', '/etc'),
('/mnt/root/var/tmp', '/mnt/hidden/var/tmp', '/mnt/hidden/var/tmp.workdir', '/var/tmp'),
('/mnt/root/usr/local', '/mnt/hidden/usr/local', '/mnt/hidden/usr/local.workdir', '/usr/local')
),
BindMounts(
# секретные контейнеры LXC
('/mnt/hidden/lxc', '/var/lib/lxc'),
# восстанавливаем некоторые перекрытые каталоги
('/mnt/root/etc/apt', '/etc/apt')
)
)

# из-за изменений в /etc надо:
invoke.run('systemctl daemon-reexec')
invoke.run('systemctl start nfs-kernel-server')

# запускаем секретные контейнеры LXC
for container_name in config['containers']:
invoke.run(f'lxc-start {container_name}')

Но вы же наверняка хотите десктоп, поэтому создаём скрипт в нашем каталоге:

nano my-hidden-system/bootstrap
Вот как он может выглядеть:

#!/usr/bin/env python3

import os
base_dir = os.path.dirname(os.path.abspath(file))

import sys
sys.path.insert(0, os.path.dirname(base_dir))

from pdt_base import read_config, setup, Invoke
from pdt_tasks import *

# можем бутстрапить наш комп этим скриптом по SSH:
if len(sys.argv) > 1:
remote = sys.argv[1]
else:
remote = None

# читаем конфигурацию и создаём экземпляр Invoke
config = read_config(base_dir)
invoke = Invoke(remote=remote)
invoke.set_devices(config) # обрабатываем секцию devices

setup(
config, invoke,

TmpfsMounts('/mnt'), # монтируем tmpfs в /mnt
MountRoot, # монтируем оригинальную корневую
# файловую систему в /mnt/root
MountVolumes, # монтируем все тома
TmpfsMounts( # перемонтируем в tmpfs эти каталоги:
'/home',
'/var/lib/lxc'
),
BindMounts( # заменяем /root
('/mnt/my-data/root', '/root')
)
)

if remote:
# ключи для SSH поменялись после замены /root,
# поэтому надо пересоздать экземпляр Invoke
invoke = Invoke(remote=remote, ssh_key='~/.ssh/secret_id_ecdsa')

# продолжаем
setup(
config, invoke,

# добавляем секретных пользователей в /home
BindMounts(
('/mnt/my-data/work', '/home/work'),
('/mnt/my-data/browsing', '/home/browsing'),
('/mnt/my-data/sans-vpn', '/home/sans-vpn')
),
# перемонтируем в overlayfs следующие каталоги:
OverlayMounts(
('/mnt/root/etc', '/mnt/my-data/etc', '/mnt/my-data/etc.workdir', '/etc'),
('/mnt/root/var/tmp', '/mnt/my-data/var/tmp', '/mnt/my-data/var/tmp.workdir', '/var/tmp'),
('/mnt/root/usr/local', '/mnt/my-data/usr/local', '/mnt/my-data/usr/local.workdir', '/usr/local')
),
# восстанавливаем некоторые перекрытые каталоги
BindMounts(
('/mnt/root/etc/apt', '/etc/apt'),
# допустим, в базовой системе у нас только один пользователь user
# - восстанавливаем его каталог в /home
('/mnt/root/home/user', '/home/user')
),
# перезапускаем сервисы
RestartServices(
'syslog',
'systemd-journald',
'autofs',
'display-manager'
)
)

# создаём необходимые каталоги в /mnt, который теперь tmpfs
invoke.run('mkdir -p /mnt/myserver')
invoke.run('mkdir -p /mnt/usb')
invoke.run('mkdir -p /mnt/temp')

# если в секретной системе другой /etc/nftables.conf, то надо:
invoke.run('systemctl restart nftables')

# а если есть секретная конфигурация для VPN, то:
invoke.run('ip link add dev wg0 type wireguard')
invoke.run('ip address add 10.0.0.2 dev wg0 peer 10.0.0.1')
invoke.run('/bin/bash -c "wg setconf wg0 <(wg-quick strip /etc/wireguard/wg0.conf)"', shell=True)
invoke.run('ip link set up dev wg0')
invoke.run('ip route replace default dev wg0')
# (лично я не использую wg-quick, своим современным подходом
# с fwmark он ломает мне всю маршрутизацию)

# добваляем маршрут к VPN серверу
invoke.run('ip route add 1.2.3.4 via 192.168.0.1')

# для пользователя sans-vpn VPN не нужен (uid, к примеру, 1003):
invoke.run('ip rule add uidrange 1003-1003 table 1 pref 100')
invoke.run('ip route add default via 192.168.0.1 table 1')

# размонтируем бутстрапный раздел:
if not remote:
invoke.locrypt_unmount('/tmp/bootstrap')
Делаем скрипт исполняемым:

chmod +x my-hidden-system/bootstrap
Запускаем, создаём пользователей с помощью adduser или useradd, перезапускаем display-manager. Надеюсь, у вас всё получилось?

Таким образом, вы можете запустить свои иксы для каждого секретного пользователя и переключаться между ними по ALT-F7, ALT-F8, и т.д. Не забывайте проявлять активность в основном, несекретном аккаунте. Котиков иногда разглядывайте, что-ли.

Для бутстрапа системы после перезагрузки надо запомить последовательность команд:

HISTFILE=
losetup --offset 20480 --sizelimit 512000 -f /dev/sda
cryptsetup open /dev/loop0 bootstrap --type plain
mkdir /mnt/bootstrap
mount /dev/mapper/bootstrap /mnt/bootstrap
cd /
/mnt/bootstrap/my-hidden-system/bootstrap
Это если вы не придумали ничего лучшего. Я тоже пока нет.

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








 
чуть осилил, но разобрался
 
  • Теги
    linux
  • Сверху Снизу