Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
skitter-creek-bath-salts — Unlocking _everything_ on the CPU with DRAM scrambling | Kitploit
Tools/GitHubGitHub/xoreaxeaxeax/skitter-creek-bath-salts
Vulnerability AnalysisExploitationReverse EngineeringHardware HackingHardware SecurityFirmware Analysis
GitHubxoreaxeaxeax/skitter-creek-bath-salts

skitter-creek-bath-salts

Unlocking _everything_ on the CPU with DRAM scrambling

View Repository
2.1k162311 month agoReviewed by Kitploit

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

skitter-creek-bath-salts

Unlocking everything on the CPU with DRAM scrambling — PSP, C6, microcode, SMM, and anything else the specs left out.

&x == &x.

Usually.

Unspaghettifying DRAM

Poke the DRAM controller and an address can be made to land wherever you want in memory. skitter-creek-bath-salts modifies the bottom layers of the memory hierarchy to rewire the physical DRAM address translations. This scrambles platform memory, exposing protected regions of DRAM — carveouts invisible even to the kernel. When the address translations break, so do the security primitives built on them, and we unlock everything.


TL;DR

  • Unlock your Platform Security Processor
  • Unlock System Management Mode
  • Unlock C6 DRAM
  • Unlock your CPU microcode

Target

Developed and tested on AMD Family 16h CPUs, the last generation whose datasheets document the DRAM controller's translation registers — and show that they can't be locked. 17h and beyond simply leave this information out. The odyssey of *p is similar across generations and architectures, and the underlying transforms extend even to ARM, RISC-V, and beyond; skitter-creek-bath-salts shows us only how to begin.


The odyssey of *p

It's a long way down.

Memory is built on layers of abstraction so deep they become almost absurd. When your code dereferences *p, it appears to access the DRAM at p. It does not — p is a virtual address, and before a single bit of DRAM is touched, it must survive the gauntlet below:

         ── 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)

This project works at the deepest levels of the *p pipeline, the MCT/DCT layer — where a physical address from the data fabric/interconnect enters the memory controller and is rewritten one final time into the raw DRAM coordinates that are issued to the DIMM.


Spaghettifying DRAM

Physical addresses are really more of a suggestion.

xor dword [0xf80c2094], 0x00400000

That's the exploit. All of it.

One bit-flip in the DRAM controller rewires the bottom of the *p pipeline, and the data that was at &x is now somewhere else mid-flight. Suddenly &x != &x. Every mechanism the CPU, firmware, uncore, and chipset use to wall off protected memory sits above the memory controller, and none of it sees what happens below. The fences guard physical addresses, not DRAM coordinates; rearrange the coordinates and the barriers above never notice.

Download Tool