Райтап на задачу «Секреты мертвого шпиона»

dikiyponos

Автор:

dikiyponos

5 ноября 2026

5

05.11.2026
Секреты мертвого шпиона
Категория:

Forensic

Задача «Секреты мертвого шпиона»

dikiyponos

Автор:

dikiyponos

Первая кровь:
Term1nal
Сложность:
hard
Дата публикации:
24.10.2026
Решений:
14
Рейтинг:
4.67/5
Ссылка:
Решать

Скачаем файлы, необходимые для решения задания.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 1

Сначала определимся, что у нас вообще за файлы. hard_drive.qcow2 — образ диска в формате qcow для виртуальных машин на основе QEMU/KVM (Linux). Загуглим, что такое tpm2-00.permall файл.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 2
Решение задачи «Секреты мертвого шпиона»: иллюстрация 3

По публично доступной информации понимаем, что это файл состояний TPM. Смонтируем диск и посмотрим, что он из себя представляет. Для этого подключим модуль ядра nbd (network block device) и используем qemu-nbd.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 4

У нас есть 3 раздела. Смонтируем первый и посмотрим, что там.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 5

Мы видим, что это EFI-раздел для Ubuntu, тут лежат всякие загрузчики, необходимые для загрузки системы.

Теперь посмотрим, что у нас на втором разделе. Тут у нас лежит раздел /boot, именно тут находится ядро и initramfs.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 6

Третий раздел смонтировать просто так не получится. Он зашифрован с помощью LUKS2. Посмотрим метаданные раздела с помощью cryptsetup luksDump.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 7

Посмотрим внимательно вывод этой команды. LUKS поддерживает несколько паролей для разблокирования, они называются слоты. У нас есть два keyslot: 0) argon2id с тяжёлыми параметрами 1) pbkdf-sha512 с тысячей итераций. Первый слот, вероятно, предназначен для человеческого пароля, а второй — для systemd-токена, о котором ниже.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 8

Видим, что у нас есть токен для разблокировки диска с TPM2+PIN (tpm2-pin: true). Что вообще из себя модуль TPM? Это криптографический чип, который отвечает за безопасность компьютера или сервера. Делает он это посредством того, что является изолированным «криптопроцессором» для защиты ключей. Выдаёт он ключи только при определённом условии, например верный PIN-код или у системы правильные значения PCR (Platform Configuration Registers). В этой задаче PCR не рассматривается.

В параметрах видим keyslot: 1, это и есть тот слот с pbkdf-sha512 1000.

Попробуем загрузить этот диск на виртуальной машине virt-manager с EFI.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 9

Действительно, для загрузки необходим PIN-код.

Какой же вектор атаки выбрать? У нас стоит какой-то неизвестный PIN-код для расшифровки диска, который проверяется виртуальным TPM. Можно попробовать его брутить, и это даже возможно, когда у тебя локально есть дамп, но вдруг автор поставил пароль в 32+ символа?

Если вы не знакомы с внутренней кухней LUKS, TPM 2.0 и systemd с линуксами, то можно просто загуглить, что можно сделать, когда TPM скомпрометирован и используется TPM2+PIN.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 10

Результату нейронки не доверяем, открываем первые попавшиеся GitHub issues.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 11

Тут говорится, что при использовании LUKS с TPM2+PIN (systemd-cryptenroll) при компрометации TPM можно взломать шифрование, не зная PIN-кода. PIN-код нужен только для проверки на стороне TPM. В issue это рассматривается на реальном примере с уязвимостью faulTPM (Ryzen 1000–5000). Получается, с этим таском мы могли бы быть на этапе пост-эксплуатации faulTPM, когда имеем расшифрованный дамп. Кто знает, может, криминалисты и использовали эту уязвимость, чтобы сделать дамп fTPM этого шпиона.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 12

(пример атаки faulTPM)

Теперь мы знаем, что при использовании схемы TPM+PIN, если мы имеем полный доступ к TPM, мы можем расшифровать диск. Теперь надо понять, как оно работает под капотом. Для этого придётся читать исходники swtpm, libtpms (на чём основывается swtpm) и systemd. Если вы захотите прорешать с нуля ручками, то в таблице указаны функции и константы, которые объясняют, как работает процесс запечатывания (seal) и распечатывания (unseal).

Таблица файлов и функций systemd, swtpm и libtpms

По-человечески, когда вы используете systemd-cryptenroll, чтобы привязать TPM к LUKS, то оно работает следующим образом:

  1. systemd-cryptenroll получает мастер-ключ LUKS после ввода пароля пользователем.

  2. Генерирует случайный 32-байтовый секрет.

  3. Берёт/создаёт Storage Root Key (SRK) в TPM по адресу 0x81000001. SRK — это главный «родительский» ключ, под которым TPM хранит и защищает ключи и различные секреты.

  4. Запечатывает секрет в TPM под SRK с определённой политикой, например, использование PIN-кода. TPM возвращает TPM2B_PRIVATE и TPM2B_PUBLIC. TPM2B_PRIVATE всегда покидаёт TPM только в зашифрованном виде.

    TPM2B_PRIVATE состоит из трёх блоков: integrity (HMAC), IV (вектор инициализации для AES) и ciphertext (AES-CFB). Если расшифровать ciphertext, мы получим структуру TPMT_SENSITIVE, внутри которой находится: sensitiveType — тип секретных данных (keyedhash), authValue — хеш PIN-кода, seedValue — случайное число для генерации внутренних ключей и sensitive.data — сам секрет для разблокировки диска.

    Обратим внимание, что authValue хранится отдельно от sensitive.data. PIN-код используется только для удовлетворения политики доступа TPM и никак не используется в шифровании. Если TPM скомпрометирован, то ничто не мешает получить секрет как есть (до версии systemd 262, но это уже отдельная тема).

  5. Полученные структуры склеивает в tpm2-blob и записывает LUKS JSON-токен. Его мы и видели на этапе разведки в cryptsetup luksDump.

  6. Кодирует тот же 32-байтовый секрет в base64 и использует как пароль нового LUKS-keyslot.

  7. LUKS2 создаёт keyslot из base64-пароля через KDF с минимальными параметрами, получает keyslot-ключ и шифрует им volume key. Используется самый простой pbkdf2-sha512, потому что нет необходимости в ресурсоёмкой функции формирования ключа, так как это случайные 32 байта.

Пора писать скрипты. Весь исходный код каждого этапа можете посмотреть на GitHub (https://github.com/kolbanidze/swtpm-poc).

1) Извлечём systemd-токен из LUKS2-заголовка.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 13

2) Распарсим файл состояния swtpm — tpm2-00.permall, вытащим из него все объекты.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 14

3) Мы помним, что TPM не хранит сам секрет — все делается с помощью уникального SRK, который никогда его не покидаёт в открытом виде (по задумке). Сам SRK тоже, как tpm2-blob, состоит из TPMT_PUBLIC и TPMT_SENSITIVE. Публичная часть нас не особо интересует, нам интересна закрытая часть, в которой хранится seedValue, с помощью которого TPM выводит ключи для шифрования дочерних объектов. Примечание: не путайте с seedValue в TPM2B_PRIVATE, где хранится ключ для разблокировки диска.

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

KDFa(SHA256, SRK.seedValue, "STORAGE", child.Name, 128)

KDFa — это специальная функция формирования ключа по спецификации TPM 2.0. Child.Name — это хеш от публичной части, склеенный с названием алгоритма.

Теперь мы понимаем, как зашифрован blob и что надо для извлечения секрета. Надо извлечь из SRK его seedValue, с помощью KDFa и публичных данных сформировать ключ, расшифровать blob и извлечь из него секрет.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 15

Приватный ключ SRK нам не понадобится, он нужен для других целей.

4) Теперь только остаётся восстановить логику KDFa и расшифровать TPM2B_PRIVATE.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 16

5) Отлично, мы получили Base64-строку (именно она и является ключом к слоту). Остаётся только расшифровать диск.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 17

При попытке смонтировать диск понимаем, что внутри LUKS находится не сразу ext4 раздел, а LVM (Logical Volume Manager). Поэтому активируем volume group (vg) и монтируем раздел. По умолчанию Ubuntu всегда использует LUKS+LVM.

Решение задачи «Секреты мертвого шпиона»: иллюстрация 18

Флаг находится в /root/flag.txt.

Разумеется, у задачи есть несколько способов решения. Можно запустить локальный эмулятор (сервер) swtpm и уже с помощью gdb или чего-то похожего вырезать проверку authValue и штатным образом сделать unseal().

Если вы нашли опечатку или ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter

Также по теме:

Райтап на задачу «Анализ трафика»

/Разборы задач

Райтап на задачу «Анализ трафика»

Находим отладочный запрос с флагом в сетевом трафике с помощью Wireshark.
Прочитать

5

Райтап на задачу «Цензура»

/Разборы задач

Райтап на задачу «Цензура»

Открываем PDF в LibreOffice Draw и убираем фигуру, которая закрывает флаг.
Прочитать

36