diff --git a/README.md b/README.md index 631ee75..2d75983 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Pass runtime args without a rebuild by appending them after `|`: sliver > crystal --payload C:/path/file.pico.bin|args here ``` -The `crystal-loader.x64.dll` is a Sliver DLL Extension that allocates RWX memory, loads the PICO blob, and jumps to the Crystal Palace entrypoint. Paths use forward slashes. Arg format is `type:string` (not BOF binary). +The `crystal-loader.x64.dll` is a Sliver DLL Extension that reads the PICO blob from disk into a `VirtualAlloc(RW)` region, flips it to RX with `VirtualProtect`, and jumps to the Crystal Palace entrypoint. No `PAGE_EXECUTE_READWRITE` mapping is ever held. Paths use forward slashes. Arg format is `type:string` (not BOF binary). The DLL is loaded in-memory by Sliver — it is not written as a file to the target disk. ### C — Built-in shell execution via Crystal Palace @@ -108,7 +108,7 @@ docs/ ```bash # 1. Toolchain -sudo apt install -y mingw-w64 nasm default-jdk make zip git curl +sudo apt install -y mingw-w64 nasm openjdk-17-jdk make zip git curl # 2. Crystal Palace dist (BSD-3-Clause, Raphael Mudge) mkdir -p external/crystalpalace @@ -154,7 +154,7 @@ See `docs/RUNBOOK.md` for the full operator procedure (Sliver install, listener | Crystal Palace CLI verified | OK | `./link [%KEY=value]` — positional, documented in `dist/README` | | End-to-end PICO build (Use case A) | OK | 117 KB PICO produced from test DLL | | End-to-end PICO build (Use case B) | OK | 111 KB PICO produced via `postex-loader/loader.spec` | -| Sliver Extension wrapper DLL builds | OK | `crystal-loader.x64.dll` ~232 KB, `crystal-exec.x64.dll` ~328 KB — both PE32+ exporting `go` symbol | +| Sliver Extension wrapper DLL builds | OK | `crystal-loader.x64.dll` ~42 KB, `crystal-exec.x64.dll` ~75 KB — both PE32+ exporting `Initialize` symbol; no RWX; stripped | | Extension tarball packs correctly | OK | 37 KB tarball validated with `tar -tzf` | | Custom stager build (two-file delivery) | OK | `bundle-stager.sh` → `csvchelper.exe` (17 KB, entropy 4.784) + `payload.dat` (AES-256-CBC) | | Runtime execution on Windows (Use case A) | OK | Sliver session established on Windows 10 x64 FLARE-VM; stager passes Defender (Wacatac.B!ml + ZomBytes.B) | diff --git a/docs/RUNBOOK.md b/docs/RUNBOOK.md index 28a5452..a1d7712 100644 --- a/docs/RUNBOOK.md +++ b/docs/RUNBOOK.md @@ -115,8 +115,8 @@ ls -la build/ Expected files after the above: -- `crystal-loader.x64.dll` (~232 KB, in `sliver-glue/` parent) -- `crystal-exec.x64.dll` (~328 KB, in `sliver-glue/` parent) +- `crystal-loader.x64.dll` (~42 KB, in `sliver-glue/` parent) +- `crystal-exec.x64.dll` (~75 KB, in `sliver-glue/` parent) - `build/smoketest.bin` (3 bytes: `31 c0 c3` = `xor eax,eax; ret`) - `build/crystal-loader-0.1.0.tar.gz` (~37 KB) @@ -295,6 +295,10 @@ The extension tarball `crystal-kit-sliver/sliver-glue/build/crystal-loader-0.1.0 - `crystal-loader.x64.dll` — provides the `crystal` command - `crystal-exec.x64.dll` — provides the `crystal-exec` command (see Phase 4) +> **Defender surface:** the extension DLLs (`crystal-loader.x64.dll`, `crystal-exec.x64.dll`) are loaded **in-memory** by Sliver's extension system — they are sent over the C2 channel and mapped directly into the implant process. They are never written as files to the target disk and are not visible to Defender's on-access file scanner. +> +> The **PICO file** you upload in step 3.4 (`upload ... C:/Windows/Temp/mytool.bin`) **does** land on disk and will be scanned by Defender on write. Keep PICO files small, avoid common shellcode patterns at offset 0 (Crystal Palace's XOR mask helps here), and delete the file after execution (step 5 cleanup). + ### 3.1 Install the extension (one-time per session) ``` @@ -357,6 +361,8 @@ If the DLL calls `BeaconPrintf` or `BeaconOutput`, the output is forwarded to th The `crystal-exec` command runs an arbitrary shell command on the target through the Crystal Palace evasion stack. No file to upload — the executor PICO is embedded inside `crystal-exec.x64.dll` at build time. Output is returned to the Sliver console via an anonymous pipe; no disk artifact is created. +Like `crystal-loader.x64.dll`, the `crystal-exec.x64.dll` DLL is loaded **in-memory** by Sliver and never written to disk on the target. This means Defender's file scanner does not see it. The embedded PICO bytes are XOR-encrypted with a fresh 256-byte key at build time (`gen_pico_header.py`), so static patterns from Crystal Palace do not appear in the DLL's `.data` section. + ### 4.1 Install the extension (same tarball as Phase 3) If not already installed: diff --git a/docs/TOOLCHAIN.md b/docs/TOOLCHAIN.md index f4dfb75..991deb4 100644 --- a/docs/TOOLCHAIN.md +++ b/docs/TOOLCHAIN.md @@ -88,14 +88,20 @@ Example invocation: ```makefile CC_64 := x86_64-w64-mingw32-gcc -CFLAGS := -Wall -Os -DBUILD_DLL -LDFLAGS := -shared -Wl,--subsystem,windows +CFLAGS := -Wall -Os -DBUILD_DLL -ffunction-sections -fdata-sections +LDFLAGS := -shared -Wl,--subsystem,windows -s -Wl,--gc-sections # Produces ../crystal-loader.x64.dll $(CC_64) $(CFLAGS) crystal-loader.c beacon_compatibility.c -o ../crystal-loader.x64.dll $(LDFLAGS) ``` -**Verified output:** ~232 KB, PE32+ x86-64, exports symbol `go`. +Key evasion properties: +- Exports `Initialize` (not `go` — the `go` export name is the primary `Win64/MeterBof.A` static signature) +- No `[crystal-loader]` / `PICO` / tool-identifying strings in `.rdata` +- `VirtualAlloc(RW)` + `VirtualProtect(RX)` — no `PAGE_EXECUTE_READWRITE` mapping ever held +- `-s` strips all symbol table entries; `--gc-sections` removes dead code + +**Verified output:** ~42 KB, PE32+ x86-64, exports symbol `Initialize`. A smoke test shellcode (`smoketest.asm`) builds in parallel: @@ -119,13 +125,14 @@ Step 1: crystalexec.c → crystalexec.dll Step 2: crystalexec.dll → crystalexec.pico.bin (Crystal Palace wrap) ../generate.sh crystalexec.dll "" crystalexec.pico.bin -Step 3: crystalexec.pico.bin → crystalexec_pico.h (embed as C byte array) - xxd -i crystalexec.pico.bin | \ - sed 's/unsigned char .../unsigned char crystalexec_pico/; \ - s/unsigned int .../unsigned int crystalexec_pico_len/' > crystalexec_pico.h +Step 3: crystalexec.pico.bin → crystalexec_pico.h (XOR-encrypt + embed as C array) + python3 gen_pico_header.py crystalexec.pico.bin crystalexec_pico.h + Fresh random 256-byte key every build — Crystal Palace byte patterns in .data + are obfuscated; outputs crystalexec_pico[] + crystalexec_pico_key[] arrays. Step 4: crystal-exec.c + crystalexec_pico.h → ../crystal-exec.x64.dll - x86_64-w64-mingw32-gcc -Wall -Os -DBUILD_DLL -shared -Wl,--subsystem,windows \ + x86_64-w64-mingw32-gcc -Wall -Os -DBUILD_DLL -ffunction-sections -fdata-sections \ + -shared -Wl,--subsystem,windows -s -Wl,--gc-sections \ -o ../crystal-exec.x64.dll crystal-exec.c ``` @@ -134,8 +141,9 @@ Key design constraints: - Use custom string helpers (`hex_parse`, `str_append`) instead of `strtoull`, `strtol`, `snprintf` - `crystal-exec.c` (the Sliver extension itself) may use CRT normally — it runs in the normal process context, not inside the PICO loader - Single callback model: all output accumulates into a heap buffer; exactly ONE `callback(buf, len)` call at the very end. Sliver extension loaders only display the first callback invocation — any subsequent calls are silently dropped. +- Exports `Initialize` (not `go`); no `[crystal-exec]` / `PICO` strings in binary; XOR-decrypt embedded PICO at runtime into `VirtualAlloc(RW)`, then `VirtualProtect(RX)` — no `PAGE_EXECUTE_READWRITE` ever held. -**Verified output:** `crystal-exec.x64.dll` — ~328 KB, PE32+ x86-64, exports symbol `go`. +**Verified output:** `crystal-exec.x64.dll` — ~75 KB, PE32+ x86-64, exports symbol `Initialize`. ### 3e. Custom stager — two-file delivery (Use case A Defender bypass)