
Эксплойт для уязвимости прошивки AMD SEV-SNP CVE-2024-21978, позволяющий расшифровать произвольную память гостевой системы путем повреждения памяти контекстных страниц.
Этот репозиторий содержит эксплойт для уязвимости в прошивке SEV. Эксплойт позволяет расшифровывать произвольную память работающего гостя SEV-SNP.
Протестировано на версии 1.55.16 (последней на момент написания).
Поле nv_paddr команды SEV_INIT_EX можно использовать для передачи фрагмента памяти прошивке, чтобы она могла использовать его вместо постоянной flash. Если SEV-SNP включен, эта память должна находиться в состоянии FIRMWARE. Прошивка проверяет это один раз во время выполнения команды SEV_INIT_EX. После этого прошивка предполагает, что данная память находится в состоянии FIRMWARE, и записывает в неё без каких-либо дополнительных проверок.
Предположение о том, что память всё ещё находится в состоянии FIRMWARE, не всегда верно — ничто не мешает хосту изменить состояние обратно на HYPERVISOR с помощью команды SNP_PAGE_RECLAIM. Как только страницы оказываются в состоянии HYPERVISOR, их можно перевести в другие состояния, например, CONTEXT. Даже если страницы больше не находятся в состоянии FIRMWARE, прошивка будет в них записывать, нарушая целостность, требуемую некоторыми состояниями страниц.
Мы можем использовать это повреждение памяти, нацелившись на страницы CONTEXT. Страницы CONTEXT — мощная цель, но есть некоторые проблемы:
CONTEXT шифруются другим ключом, чем остальная память. В результате сложно управлять открытым текстом, даже если мы можем управлять шифротекстом.Повреждение памяти фактически заполняет страницу CONTEXT случайными данными, поэтому нелегко повредить страницы CONTEXT таким образом, чтобы это было полезно для атакующего. Чтобы обойти это, мы можем многократно вызывать ошибку, вызывая повреждение, и использовать команду SNP_GUEST_STATUS для чтения соответствующих полей повреждённой страницы CONTEXT, пока не увидим полезные значения.
Команда SNP_DBG_DECRYPT может использоваться для расшифровки памяти гостя SEV-SNP с включённой политикой DEBUG. Если мы сможем сформировать CONTEXT так, чтобы в нём был установлен флаг DEBUG и содержался ASID другого гостя, мы сможем использовать его для расшифровки памяти другого гостя, даже если у него не установлена политика DEBUG.
Оказывается, SNP_DBG_DECRYPT игнорирует большинство полей на странице CONTEXT; она проверяет только gctx->guest.asid, gctx->guest.policy_snp и gctx->guest.guest_flags. Вероятность того, что эти поля окажутся корректными после повреждения памяти, невысока, но не за пределами возможного. Хорошая новость также в том, что мы можем прочитать все эти поля с помощью команды GUEST_STATUS.
Итак, мы можем использовать ошибку, выполнив следующие шаги:
nv_paddr в состояние FIRMWARE с помощью инструкции rmpupdate.SEV_INIT_EX.nv_paddr обратно в состояние HYPERVISOR с помощью команды SNP_RECLAIM_PAGE.CONTEXT по адресу nv_paddr.nv_paddr с помощью команды SEV_PDH_GEN. Это повреждает страницы CONTEXT.GUEST_STATUS для проверки, будет ли SNP_DBG_DECRYPT успешным; если нет, вернуться к шагу 5. Основное узкое место здесь в том, что ASID хранятся в 32-битном int, но допустимых ASID гораздо меньше (509 или 1006 в зависимости от CPU), поэтому потребуется довольно много попыток, чтобы добиться правильного результата.Эксплойт проводит большую часть времени на шагах 5 и 6. Шансы на выполнение всех правильных условий составляют примерно 1/20 000 000 на EPYC Milan, и мы можем делать около 100 попыток в секунду, поэтому ожидаем, что правильные условия будут достигнуты примерно раз в два дня (предупреждение: расчёты приблизительны, и я мог что-то напутать, но анекдотически оценка «раз в два дня» кажется верной). Мы можем ускорить это, атакуя не одну страницу CONTEXT за раз, а три страницы CONTEXT по адресам nv_paddr, nv_paddr+4096 и nv_paddr+8192 (SEV_PDG_GEN повредит три страницы). Удобно, что эти шаги можно выполнить до запуска гостя-жертвы, и их нужно выполнить успешно только один раз, чтобы атаковать произвольное количество гостей (однако обратите внимание, что PoC в настоящее время атакует только одного гостя).
Хотя я ещё не смог это проверить, я считаю, что после того, как атакующий использует эту уязвимость для получения ключей связи гостевой платформы виртуальной машины, он сможет отправлять гостевые сообщения прошивке от имени гостя и использовать это для запроса аттестационных отчётов. Это нарушает ключевой принцип SEV-SNP, согласно которому только гость может запрашивать аттестационные отчёты.
Несколько команд (например, SNP_RECLAIM_PAGE, SNP_GCTX_CREATE, RING_BUFFER, возможно, ещё, а может быть, для безопасности все?), которые принимают страницу FIRMWARE, должны проверять, перекрывается ли она с nv_paddr, и завершаться ошибкой в случае перекрытия.
Есть ещё одна проблема, которая меня беспокоит, и я не уверен, обоснована ли она; буду рад услышать ваше мнение: если я правильно понимаю, прошивку SEV можно обновлять без прерывания работы запущенных гостей. Это означает, что можно перенести повреждённую страницу CONTEXT из старой уязвимой версии прошивки в новую исправленную версию. Можно ли начать на старой уязвимой версии, выполнить описанный эксплойт, обновить и зафиксировать новую исправленную прошивку, запустить гостя на новой прошивке (чтобы старая версия прошивки не отображалась в аттестационном отчёте), а затем использовать повреждённую страницу CONTEXT, созданную с помощью старой прошивки, для атаки на гостя, созданного с помощью новой версии? Сможет ли потребитель аттестационных отчётов, созданных новым гостем, определить, что старая версия прошивки работала в какой-то момент до запуска нового гостя? Если нет, требуются ли дополнительные меры смягчения для предотвращения этого?
root@server:~/sev-exploit# cargo run --release
Finished release [optimized] target(s) in 0.12s
Running `target/release/sev-exploit`
Corrupt guest context page so that ASID is in range 1..510
Smallest ASID: 0x0000001f iterations: 14052175 zeros: 10539628 unique asids: 31500727 elapsed time: 1d 19h 20m 31s
Creating VM with same ASID
[03, 00, 00, 00, 00, 00, 00, 00, 11, 0f, a0, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, f0, 51, a5, 03, 3f, 69, 6b, 93, e8, d8, 61, 0d, 2e, 5a, 45, f1, ea, 6d, bf, 49, fe, e4, a9, 2d, 8d, af, 76, 5e, 2e, 56, e0, fa, a9, b3, a7, e0, bc, 09, d9, 4f, 28, 5c, 9f, 84, d2, 7e, 34, eb, ea, 3f, 29, 88, 30, 01, 28, 65, 8b, 73, 3c, 84, 00, ae, 4a, 74, a2, 7a, d1, c7, 4f, 63, 7f, 72, 7b, 3b, 2f, 08, b3, 1a, 8c, 99, 1b, ad, b5, 1d, 42, 0b, 4d, 98, d4, 7d, c1, 0b, d6, 2f, b4, 6c, 6b, 51, a2, 92, 17, 3b, 01, e8, 82, 11, 1e, cb, cb, a2, 8f, c9, b0, 52, 1d, 1d, b7, d2, 25, 8d, 32, a9, 7a, 6f, 86, e4, 40, 44, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 80, 00, 88, 00, 00, 00, 00, ee, ff, 00, 00, f0, ff, ff, ff, ff, ff, ff, ff, ff, 3f, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, <cut off zeros>]
thread 'main' panicked at src/main.rs:170:13:
not yet implemented: use the leaked secrets to send guest messages
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Обратите внимание, что эксплойт может работать значительное время (от нескольких часов, если повезёт, до нескольких дней, если не повезёт). На EPYC Genoa, вероятно, будет быстрее, так как допустимых ASID почти в два раза больше.
Во время шагов 5 и 6 PoC отображает некоторые метрики:
CONTEXT находится в состоянии, когда прошивка считает, что ей ещё не назначен ASID. В таких случаях SNP_GUEST_STATUS возвращает 0 в поле ASID.В большинстве случаев вывод из эксплуатации повреждённых страниц CONTEXT вызывает сбой прошивки (скорее всего, здесь). Насколько я понимаю, сбои прошивки приводят к перезагрузке всей системы. Чтобы избежать таких сбоев, патчи ядра предотвращают вывод страниц CONTEXT из эксплуатации. Один из недостатков этого заключается в том, что модуль ядра ccp не может быть выгружен. После запуска PoC всю систему необходимо перезагрузить, прежде чем его можно будет запустить снова (независимо от того, успешно ли выполнен PoC или прерван).
CONTEXT. Отслеживать страницу секретов. Это возможно, потому что прошивка SEV отслеживает активные ASID внутренне и не проверяет активные страницы CONTEXT на наличие дубликатов.CONTEXT для выполнения SNP_DBG_DECRYPT на странице секретов гостя-жертвы.