mirror of
https://github.com/licitrasimone/CrystalSliver
synced 2026-06-21 13:55:58 +00:00
feat: trying to do post-ex better
This commit is contained in:
@@ -25,6 +25,9 @@ build/
|
||||
*.jar
|
||||
libtcg.x64.zip
|
||||
|
||||
# Per-operator config (engagement-specific paths, never commit)
|
||||
.crystalenv
|
||||
|
||||
# OS / editor
|
||||
.DS_Store
|
||||
.vscode/
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
---
|
||||
task: plan post-ex UX improvements for CrystalSliver
|
||||
slug: 20260610-200618_postex-ux-plan
|
||||
effort: standard
|
||||
phase: plan
|
||||
progress: 0/8
|
||||
mode: interactive
|
||||
started: 2026-06-10T20:06:18Z
|
||||
updated: 2026-06-10T20:10:00Z
|
||||
---
|
||||
|
||||
## Context
|
||||
Operator post-ex flow currently requires 3 separate commands, manual env export, and temp file creation for args. Goal: collapse to 1 command + 1 paste into Sliver, no env setup required.
|
||||
|
||||
### Risks
|
||||
- sliver-client non-interactive mode uncertain — postex.sh explicitly avoids calling Sliver to sidestep this
|
||||
- Multi-version CRYSTAL_PALACE_HOME per engagement: .crystalenv is per-operator, env override always wins
|
||||
|
||||
## Criteria
|
||||
- [x] ISC-1: Plan identifies every friction point in current post-ex flow
|
||||
- [x] ISC-2: Plan proposes single-command wrapper for DLL → PICO step
|
||||
- [x] ISC-3: Plan proposes inline args support (no temp file required)
|
||||
- [x] ISC-4: Plan proposes persistent config for CRYSTAL_PALACE_HOME
|
||||
- [x] ISC-5: Plan proposes idempotent extension install check
|
||||
- [x] ISC-6: Plan proposes named PICO output (not generic default)
|
||||
- [x] ISC-7: Plan specifies what scripts to change vs add
|
||||
- [x] ISC-8: Plan specifies backward-compatibility constraints
|
||||
|
||||
## Decisions
|
||||
- postex.sh prints Sliver command rather than calling sliver-client directly — avoids non-interactive CLI uncertainty
|
||||
- .crystalenv is git-ignored per-operator — avoids committing engagement-specific paths
|
||||
- Named output default uses input DLL basename — human-readable, no timestamp churn during rapid iteration
|
||||
@@ -134,7 +134,8 @@ void go ( void * loader_arguments )
|
||||
DLLMAIN_FUNC entry_point = EntryPoint ( &dll_data, dll_dst );
|
||||
|
||||
/* Pointer to DLL arguments */
|
||||
char * dll_arguments = GETRESOURCE ( _DLLARGS_ );
|
||||
char * baked_args = GETRESOURCE ( _DLLARGS_ );
|
||||
char * dll_arguments = loader_arguments ? (char *)loader_arguments : baked_args;
|
||||
|
||||
/* free the unmasked copy */
|
||||
KERNEL32$VirtualFree ( dll_src, 0, MEM_RELEASE );
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Copy to .crystalenv and fill in your path.
|
||||
# .crystalenv is git-ignored — safe for engagement-specific paths.
|
||||
# All sliver-glue scripts source this automatically if present.
|
||||
|
||||
CRYSTAL_PALACE_HOME=/home/operator/external/crystalpalace/dist
|
||||
@@ -17,7 +17,10 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
: "${CRYSTAL_PALACE_HOME:?Set CRYSTAL_PALACE_HOME to the Crystal Palace dist/ dir}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
[[ -f "$SCRIPT_DIR/.crystalenv" ]] && source "$SCRIPT_DIR/.crystalenv"
|
||||
|
||||
: "${CRYSTAL_PALACE_HOME:?Set CRYSTAL_PALACE_HOME or create sliver-glue/.crystalenv with CRYSTAL_PALACE_HOME=<path>}"
|
||||
|
||||
IMPLANT_BIN="${1:?Usage: bundle-implant.sh <implant.bin> [output.zip]}"
|
||||
OUTPUT="${2:-./build/crystal-implant-drop.zip}"
|
||||
|
||||
@@ -20,6 +20,12 @@
|
||||
"desc": "Path on operator host to a Crystal Palace PICO .bin produced by generate.sh — Sliver client reads it and forwards bytes to the implant",
|
||||
"type": "file",
|
||||
"optional": false
|
||||
},
|
||||
{
|
||||
"name": "args",
|
||||
"desc": "Optional command/args string passed to the post-ex DLL at runtime as lpReserved — overrides any args baked into the PICO at link time",
|
||||
"type": "string",
|
||||
"optional": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -24,7 +24,10 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
: "${CRYSTAL_PALACE_HOME:?Set CRYSTAL_PALACE_HOME to the Crystal Palace dist/ dir}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
[[ -f "$SCRIPT_DIR/.crystalenv" ]] && source "$SCRIPT_DIR/.crystalenv"
|
||||
|
||||
: "${CRYSTAL_PALACE_HOME:?Set CRYSTAL_PALACE_HOME or create sliver-glue/.crystalenv with CRYSTAL_PALACE_HOME=<path>}"
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
|
||||
@@ -17,11 +17,14 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
: "${CRYSTAL_PALACE_HOME:?Set CRYSTAL_PALACE_HOME to the Crystal Palace dist/ dir (contains crystalpalace.jar + link script)}"
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
[[ -f "$SCRIPT_DIR/.crystalenv" ]] && source "$SCRIPT_DIR/.crystalenv"
|
||||
|
||||
INPUT_DLL="${1:?Usage: generate.sh <input.dll> [args-file] [output.bin]}"
|
||||
ARGS_FILE="${2:-}"
|
||||
OUTPUT="${3:-./build/crystal-postex.x64.bin}"
|
||||
: "${CRYSTAL_PALACE_HOME:?Set CRYSTAL_PALACE_HOME or create sliver-glue/.crystalenv with CRYSTAL_PALACE_HOME=<path>}"
|
||||
|
||||
INPUT_DLL="${1:?Usage: generate.sh <input.dll> [args-string-or-file] [output.bin]}"
|
||||
ARGS_INPUT="${2:-}"
|
||||
OUTPUT="${3:-./build/$(basename "$INPUT_DLL" .dll).pico.bin}"
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
POSTEX_DIR="$REPO_ROOT/postex-loader"
|
||||
@@ -32,9 +35,14 @@ mkdir -p "$BUILD_DIR"
|
||||
# Resolve OUTPUT to absolute path BEFORE we cd into postex-loader
|
||||
OUTPUT="$(cd "$BUILD_DIR" && pwd)/$(basename "$OUTPUT")"
|
||||
|
||||
if [[ -z "$ARGS_FILE" ]]; then
|
||||
if [[ -z "$ARGS_INPUT" ]]; then
|
||||
ARGS_FILE="$BUILD_DIR/empty.args"
|
||||
: > "$ARGS_FILE"
|
||||
elif [[ -f "$ARGS_INPUT" ]]; then
|
||||
ARGS_FILE="$ARGS_INPUT"
|
||||
else
|
||||
ARGS_FILE="$BUILD_DIR/args.tmp"
|
||||
printf '%s' "$ARGS_INPUT" > "$ARGS_FILE"
|
||||
fi
|
||||
ARGS_FILE_ABS="$(cd "$(dirname "$ARGS_FILE")" && pwd)/$(basename "$ARGS_FILE")"
|
||||
|
||||
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# postex.sh — one-shot post-ex wrapper: DLL → PICO → ready-to-paste Sliver command
|
||||
#
|
||||
# Usage:
|
||||
# ./postex.sh <dll> [args-string]
|
||||
#
|
||||
# Wraps the DLL through Crystal Palace into a named PICO, then prints the
|
||||
# ready-to-paste Sliver command. PICO is named after the input DLL
|
||||
# (e.g. mimikatz.dll → build/mimikatz.pico.bin).
|
||||
#
|
||||
# If args-string is given it is baked into the PICO at link time.
|
||||
# For runtime-dynamic args (no rebuild needed), use args= directly in Sliver.
|
||||
#
|
||||
# Required: CRYSTAL_PALACE_HOME in env OR set in sliver-glue/.crystalenv
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
[[ -f "$SCRIPT_DIR/.crystalenv" ]] && source "$SCRIPT_DIR/.crystalenv"
|
||||
|
||||
INPUT_DLL="${1:?Usage: postex.sh <dll> [args-string]}"
|
||||
ARGS_INPUT="${2:-}"
|
||||
|
||||
BASENAME="$(basename "$INPUT_DLL" .dll)"
|
||||
OUTPUT="$SCRIPT_DIR/build/${BASENAME}.pico.bin"
|
||||
|
||||
"$SCRIPT_DIR/generate.sh" "$INPUT_DLL" "$ARGS_INPUT" "$OUTPUT"
|
||||
|
||||
echo ""
|
||||
echo "══════════════════════════════════════════════════════════"
|
||||
echo "[+] PICO: $OUTPUT"
|
||||
echo ""
|
||||
echo " Paste into sliver-client:"
|
||||
echo " crystal payload=$OUTPUT"
|
||||
echo ""
|
||||
echo " With runtime args (overrides baked args, no rebuild):"
|
||||
echo " crystal payload=$OUTPUT args=\"<command string>\""
|
||||
echo ""
|
||||
|
||||
TARBALL="$SCRIPT_DIR/build/crystal-loader-0.1.0.tar.gz"
|
||||
if [[ -f "$TARBALL" ]]; then
|
||||
echo " One-time extension install (skip if already loaded):"
|
||||
echo " extensions install $TARBALL"
|
||||
echo ""
|
||||
fi
|
||||
echo "══════════════════════════════════════════════════════════"
|
||||
@@ -60,6 +60,10 @@ EXPORT int __cdecl go(char *argsBuffer, uint32_t bufferSize, goCallback callback
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* optional runtime args string (second extension argument, type string) */
|
||||
int args_len = 0;
|
||||
char *runtime_args = BeaconDataExtract(&parser, &args_len);
|
||||
|
||||
/*
|
||||
* Allocate RWX. The PICO does its own VirtualProtect calls internally
|
||||
* (see postex-loader/src/loader.c fix_section_permissions). Starting
|
||||
@@ -82,7 +86,7 @@ EXPORT int __cdecl go(char *argsBuffer, uint32_t bufferSize, goCallback callback
|
||||
emit(callback, "[crystal-loader] executing PICO\n");
|
||||
|
||||
pico_entry_t entry = (pico_entry_t)pico_mem;
|
||||
entry(NULL);
|
||||
entry((args_len > 0) ? runtime_args : NULL);
|
||||
|
||||
/*
|
||||
* Intentionally NOT freeing pico_mem. Crystal Palace's loader chain
|
||||
|
||||
Reference in New Issue
Block a user