• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Linux с двойным дном

baykal

(L2) cache
Пользователь
Регистрация
16.03.2021
Сообщения
370
Реакции
838
В этой статье я расскажу как сделать так, чтобы ваша линуксовая машинка выглядела невинной игрушкой, но при вводе нескольких команд превращалась в настоящую боевую единицу. Конечно, у вас могут найти на диске сектора с необычно высокой энтропией, несколько подозрительных системных настроек, но никаких явных зашифрованных разделов, файлов, или сторонних шифровалок. Конечно, вас могут спросить - "а для чего тебе 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 git@github.com: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 /tmp/bootstrap
mount /dev/mapper/bootstrap /tmp/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 /tmp/bootstrap/my-hidden-system
Создаём файл конфигурации:
Код:
nano /tmp/bootstrap/my-hidden-system/config.json
Ну или vi, если вам так привычнее. Ах да, забыл упомянуть про секцию containers - это для запуска секретных LXC контейнеров. Если они у вас будут, конечно же.

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

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

Итак, конфигурация есть, - создаём тома. Кстати, давайте перейдём в каталог /tmp/bootstrap чтобы не писать его каждый раз:
Код:
cd /tmp/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:
Python:
#!/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
Вот как он может выглядеть:
Python:
#!/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 /tmp/bootstrap
mount /dev/mapper/bootstrap /tmp/bootstrap
cd /
/tmp/bootstrap/my-hidden-system/bootstrap
Это если вы не придумали ничего лучшего. Я тоже пока нет.
Тулкит может выглядеть странным, но он прошёл достаточный путь в развитии и естественно, полон архаизмов. Вы можете переписать его на свой вкус, а у меня есть новая идея развития этой системы. Обещаю держать вас в курсе. Если получится, конечно.

автор @amateur80lvl
источник habr.com
 
как сделать так, чтобы ваша линуксовая машинка выглядела невинной игрушкой, но при вводе нескольких команд превращалась в настоящую боевую единицу
cd /dev/shm/
mkdir data
wget где-то-там-далеко/veracrypt
wget где-то-там-далеко/архив-с-ништяками.bin
chmod +x veracrypt
./veracrypt архив-с-ништяками.bin /dev/shm/data/
?????
PROFIT!

как защититься от пятидолларовой атаки - думайте сами.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх