A manifest-driven, profile-aware Windows Sandbox environment for defensive malware analysis and reverse engineering.
Safety first. Networking is disabled by default. The sandbox is disposable by design. See SAFETY.md before enabling networking or mapping additional host folders.
This repository is maintained as a standalone project under incredincomp/sandbox-toolkit.
It is manifest-driven (tools.json), profile-aware (minimal, reverse-engineering, network-analysis, triage-plus, reverse-windows, behavior-net, dev-windows, full), and built for defensive malware analysis, reverse engineering, and sample triage.
The default posture is safer-by-default: disposable fresh sandbox sessions, read-only mapped scripts, and networking disabled unless a networked profile is explicitly selected.
- Added curated first-wave analysis tools to the catalog:
dependencies(GitHub-release portable)api-monitor(manual/advanced)procdot(manual/advanced)
- Added curated first-wave developer tools to the catalog:
visual-studio-community(manual/heavy)windows-sdk(manual/workflow-coupled)
- Added new built-in profiles:
triage-plusreverse-windowsbehavior-netdev-windows
- Integrated new tools/profiles across existing command surfaces:
-ListTools,-ListProfiles,-DryRun,-Validate,-CheckForUpdates- custom profiles, runtime
-AddTools/-RemoveTools, and saved templates
- Added explicit manual-source handling for
source_type: manualso manual/advanced tools are represented honestly without fake automation.
- Hardened integration characterization coverage for release readiness:
- list-mode invalid combinations (
-NoLaunch,-SkipPrereqCheck) now explicitly characterized end-to-end. - cleanup-scope characterization now asserts local profile/template config surfaces are not mutated by
-CleanDownloads.
- list-mode invalid combinations (
- Standardized non-JSON fatal handling for clearer user-facing failures with consistent exit code
1. - Expanded deterministic CI smoke/exit-code contract coverage to include:
-CheckForUpdates -OutputJsonsuccess path- list-mode invalid-parameter failure paths
- Added richer optional per-tool update metadata in
tools.jsonviatool.update. - Added read-only
-CheckForUpdatesmode to report configured vs discovered latest versions for the resolved effective selection. - Added centralized update adapters with deterministic source types:
github_releaserssstatic- explicit
unsupportedstate
- Added test coverage for update metadata validation, version comparison, adapter behavior, CLI reporting, and read-only guarantees.
-CheckForUpdates does not modify tools.json or bump versions automatically.
- Hardened integrated command-surface characterization for:
-DryRunwith built-in/custom profiles plus runtime add/remove combinations.-Validatewith built-in/custom profiles plus invalid profile/tool inputs.- list-mode reflection against live manifest + custom profile state.
- cleanup scope boundaries for
-CleanDownloads.
- Tightened CLI parameter-combination validation for list mode:
-NoLaunchand-SkipPrereqCheckare now rejected with-ListTools/-ListProfiles.
- Expanded CI smoke coverage with deterministic non-launch combinations:
- validate custom profile success
- validate invalid tool failure
- dry-run built-in remove-only flow
Release guidance:
- Precedence remains explicit and unchanged for profile selection:
- base profile
- custom-profile deltas
- runtime
-AddTools/-RemoveTools
- Use
-Validatefor readiness checks and-DryRunfor effective selection + artifact-generation preview.
- Hardened integrated CLI surfaces for:
-ListTools-ListProfiles-DryRun-Validate- custom profiles (
custom-profiles.local.json) - runtime tool overrides (
-AddTools,-RemoveTools) - bounded artifact cleanup (
-CleanDownloads)
- Made selection precedence explicit and test-backed:
- base profile selection
- custom-profile resolution (when selected)
- runtime add/remove overrides (
-AddToolsthen-RemoveTools)
- Standardized high-signal errors for:
- unknown profile
- unknown tool id
- malformed custom profile config
- invalid parameter combinations
- unsafe shared-folder paths
- Added CI hardening for release readiness:
- Pester test execution
- CLI smoke matrix using deterministic
-Validate/-DryRun/list/cleanup seams
Use this when you need to transfer files from host to sandbox for triage.
- Opt-in only: no extra folder is mapped unless you pass
-UseDefaultSharedFolderor-SharedFolder. - Default access is read-only (safer).
- Writable mapping requires explicit
-SharedFolderWritableplus one shared-folder selection switch. - In-sandbox destination is fixed:
C:\Users\WDAGUtilityAccount\Desktop\shared. - Reparse-point/junction-backed targets are rejected, and paths that traverse reparse/junction parent ancestry are also rejected.
.\Start-Sandbox.ps1 -UseDefaultSharedFolder
.\Start-Sandbox.ps1 -SharedFolder "C:\Lab\Ingress"
.\Start-Sandbox.ps1 -SharedFolder "C:\Lab\Ingress" -SharedFolderWritableThe default shared folder is repo-local shared/ (auto-created, gitignored).
Do not map broad/sensitive host paths such as repo root, C:\, %WINDIR%, Program Files roots, %USERPROFILE%, Desktop, Documents, or Downloads.
Some synced/managed host locations (for example OneDrive-backed or redirected folders) may be rejected if their path chain uses reparse points/junctions. This also applies to -UseDefaultSharedFolder when the repo itself is under such a path. Prefer a plain local, non-synced ingress folder.
Clipboard paste and drag/drop support can vary by environment and policy. Treat the mapped shared folder as the primary transfer path.
For supportability, you can add -SharedFolderValidationDiagnostics to print ancestry segments checked during shared-folder validation.
Use these optional switches when you want tighter host-interaction settings in generated sandbox.wsb:
-DisableClipboardrequests<ClipboardRedirection>Disable</ClipboardRedirection>.-DisableAudioInputexplicitly requests disabled audio input (<AudioInput>Disable</AudioInput>).-DisableStartupCommandssuppresses generated<LogonCommand>startup injection (scripts/autostart.cmdis not auto-invoked).
Default behavior remains unchanged unless you opt in:
- Clipboard redirection remains enabled by default.
- Audio input remains disabled by default.
- Startup command automation remains enabled by default.
Quick safety verification workflow:
.\Start-Sandbox.ps1 -Validate -Profile minimal -DisableClipboard -DisableStartupCommands
.\Start-Sandbox.ps1 -DryRun -Profile minimal -DisableClipboard -DisableStartupCommands
.\Start-Sandbox.ps1 -Audit -Profile minimal -DisableClipboard -DisableStartupCommandsTrust boundary reminder:
-DryRunand-Auditconfirm configured/requested policy state in generated artifacts.- This does not prove runtime enforcement inside Windows Sandbox unless explicitly stated.
-SessionMode Fresh(default): launches a clean disposable sandbox session via generated.wsb.-SessionMode Warm: uses Windows Sandbox CLI (wsb) to reuse a running session when discoverable, otherwise creates a new CLI-managed session.
Important boundaries:
- Warm mode is an operational speed/convenience tradeoff, not a stronger security mode.
- Fresh mode is the cleaner default for isolation hygiene.
- Warm mode support depends on Windows Sandbox CLI availability (documented by Microsoft for Windows 11 24H2+).
- Warm mode discovery also depends on parsable
wsb list --rawoutput; unsupported/malformed raw output is surfaced deterministically (validation warns, warm launch fails).
Current supported wsb list --raw envelope shapes:
| Shape | Expected location of records |
|---|---|
| top-level array | root array elements |
object with sessions |
sessions[] |
object with items |
items[] |
| single session object | root object |
Required source fields per record:
idorIDstatusorState
Normalized internal fields currently used by warm discovery:
IdStatusUptime
Deterministic failure categories:
- blank raw output: returns empty warm-session inventory.
- malformed JSON: parse error.
- unsupported envelope shape: unsupported-shape error.
- recognized record missing required fields: missing-field error.
Contract note:
- This is the current supported parser contract only.
- New upstream
wsbraw JSON shapes are not implicitly accepted until explicitly implemented and tested.
Examples:
.\Start-Sandbox.ps1 -SessionMode Fresh -Profile minimal
.\Start-Sandbox.ps1 -SessionMode Warm -Profile minimal
.\Start-Sandbox.ps1 -Validate -SessionMode Warm
.\Start-Sandbox.ps1 -DryRun -SessionMode Warm -OutputJson-UseWslHelper enables bounded helper-side tasks (staging + metadata artifacts) in a WSL distro. This is optional and does not replace Windows Sandbox as the execution boundary.
- Helper layer: speed/convenience for prep/orchestration tasks.
- Execution/isolation boundary: Windows Sandbox.
- No claim that WSL preprocessing makes unknown samples safe.
Use a dedicated helper distro/profile and narrow staging paths.
Tracked sample:
wsl-helper.example.wsl.confis a recommended starting point for a dedicated helper distro.- It targets reduction of broad host-drive exposure (
[automount] enabled=false), Windows binary launching from Linux ([interop] enabled=false), and unintended Windows path injection into Linux$PATH(appendWindowsPath=false). - This sample is not a universal WSL profile and is intentionally minimal.
Manual application workflow (non-invasive):
- Create/select a dedicated helper distro (for example
Ubuntu). - Review
wsl-helper.example.wsl.conf. - Inside that distro, manually apply the reviewed content to
/etc/wsl.conf(per-distro config file). - Fully stop and restart the distro so
wsl.confchanges take effect. - Re-run toolkit validation with helper options:
.\Start-Sandbox.ps1 -Validate -UseWslHelper -WslDistro Ubuntu
Boundary reminder:
wsl.confis per distro, not global across all WSL distros.- This toolkit does not auto-edit
/etc/wsl.confand does not auto-restart distros. - WSL helper remains an optional orchestration layer; Windows Sandbox remains the primary execution/isolation boundary.
WSL helper troubleshooting:
- Symptom: Windows drives still appear under
/mnt/c,/mnt/d, etc. Likely cause: edits were applied to the wrong distro, only to the tracked sample file (not/etc/wsl.conf), or the target distro never fully stopped so the new config was not reloaded. What to do: confirm helper target (-WslDistro), confirm/etc/wsl.confinside that same distro, check running distros withwsl --list --running, terminate the target distro withwsl --terminate <DistroName>, restart it, then re-run.\Start-Sandbox.ps1 -Validate -UseWslHelper -WslDistro <DistroName>. - Symptom: interop still appears enabled.
Likely cause:
[interop] enabled=falseandappendWindowsPath=falsewere not applied in the helper distro, or checks were performed in a shell session started before full distro restart. What to do: ensure/etc/wsl.confin the helper distro contains the tracked sample settings fromwsl-helper.example.wsl.conf, fully restart that distro, then re-check and re-run toolkit validation. - Note:
wsl --shutdownis also valid when needed, but it stops all running WSL distros.
Examples:
.\Start-Sandbox.ps1 -DryRun -UseWslHelper
.\Start-Sandbox.ps1 -Validate -UseWslHelper -WslDistro Ubuntu
.\Start-Sandbox.ps1 -Profile minimal -UseWslHelper -WslDistro Ubuntu -WslHelperStagePath ~/.sandbox-toolkit-helper# 1. Clone the repo
git clone https://github.com/incredincomp/sandbox-toolkit.git
cd sandbox-toolkit
# 2. Run the setup (default profile: reverse-engineering)
.\Start-Sandbox.ps1
# 3. Windows Sandbox opens and installs all tools automaticallySee QUICKSTART.md for a step-by-step guide including prerequisites.
Use this sequence for the safest host-side workflow:
- Discover available profiles and tools.
.\Start-Sandbox.ps1 -ListProfiles .\Start-Sandbox.ps1 -ListTools
- If you need a custom profile, start from the tracked example and edit locally.
Copy-Item .\custom-profiles.example.json .\custom-profiles.local.json
- Confirm your custom profile is discoverable.
.\Start-Sandbox.ps1 -ListProfiles
- Run preflight readiness checks (
-Validate= input/host readiness; no artifact generation or launch)..\Start-Sandbox.ps1 -Validate -Profile net-re-lite
- Run generation preview (
-DryRun= effective selection + generated artifacts; no download or launch)..\Start-Sandbox.ps1 -DryRun -Profile net-re-lite
- Run host-side sanity audit (
-Audit= configured/requested artifact evidence and trust signals; not runtime enforcement proof)..\Start-Sandbox.ps1 -Audit -Profile net-re-lite
- Execute the actual sandbox run.
.\Start-Sandbox.ps1 -Profile net-re-lite
Automation note: use -OutputJson with -Validate, -DryRun, -Audit, or -CheckForUpdates in CI/wrappers.
Use saved templates when you run the same sandbox workflow repeatedly.
Commands:
# Save a template
.\Start-Sandbox.ps1 -SaveTemplate daily-re -Profile reverse-engineering -SessionMode Warm -SkipPrereqCheck
# List templates
.\Start-Sandbox.ps1 -ListTemplates
# Inspect one template
.\Start-Sandbox.ps1 -ShowTemplate daily-re
# Execute from template defaults (runtime flags still allowed)
.\Start-Sandbox.ps1 -Template daily-re
.\Start-Sandbox.ps1 -Template daily-re -DryRun -OutputJson
.\Start-Sandbox.ps1 -Template daily-re -ValidateStorage model:
- Templates are stored in repo-local
saved-sessions.local.json. - File is deterministic JSON (
schema_version,templates[]) and is gitignored. - Each template stores invocation defaults (profile, tool deltas, session/helper/shared-folder/policy switches).
Precedence rules when using -Template <name>:
- Template base values.
- Profile/custom-profile resolution referenced by the template.
- Template tool deltas (
add_tools, thenremove_tools). - Runtime overrides (
-AddTools, then-RemoveTools). - Explicit command-line flags override template defaults for matching options (
-Profile, shared-folder/session/helper/policy switches when specified).
Validation behavior:
- Save-time: template references are validated against manifest/custom profiles and shared-folder safety rules.
- Execute-time: template is revalidated before run/validate/dry-run/audit execution proceeds.
- Unknown template names, malformed template files, and unknown profile/tool references fail fast with actionable errors.
Safety guidance:
- Templates can persist risky defaults (for example network-enabled profiles, writable shared folders, warm mode).
- Run
-ShowTemplate <name>before reuse and keep template names purpose-specific. - Prefer separate templates for internet-enabled workflows and strict offline/restricted workflows.
Workflow recipes:
# Recipe: repeat reverse-engineering prep with warm reuse
.\Start-Sandbox.ps1 -SaveTemplate re-warm -Profile reverse-engineering -SessionMode Warm -SkipPrereqCheck
.\Start-Sandbox.ps1 -Template re-warm -DryRun
.\Start-Sandbox.ps1 -Template re-warm
# Recipe: preserve a safer ingress workflow with explicit shared folder
.\Start-Sandbox.ps1 -SaveTemplate triage-ro -Profile minimal -SharedFolder "C:\Lab\Ingress" -SkipPrereqCheck
.\Start-Sandbox.ps1 -Template triage-ro -Validate
.\Start-Sandbox.ps1 -Template triage-ro
# Recipe: temporary runtime override on top of a template
.\Start-Sandbox.ps1 -Template triage-ro -AddTools floss -RemoveTools notepadpp -DryRunUse -CheckForUpdates to evaluate effective selected tools against configured update metadata.
# Check the default profile selection
.\Start-Sandbox.ps1 -CheckForUpdates
# Check with explicit profile + runtime overrides
.\Start-Sandbox.ps1 -CheckForUpdates -Profile minimal -RemoveTools notepadpp
# Machine-readable output
.\Start-Sandbox.ps1 -CheckForUpdates -Profile reverse-engineering -OutputJsonStatus categories:
up-to-date: configured version matches discovered latest (or configured as floatinglatest).outdated: configured version compares older than discovered latest.unknown: latest version could not be discovered or compared reliably.unsupported-for-checking: tool has no automation metadata (or is explicitly marked unsupported).
Metadata model (tools.json, per tool, optional):
{
"update": {
"strategy": "github_release|rss|static|unsupported",
"source_confidence": "high|medium|low",
"github_repo": "owner/repo",
"asset_pattern": "tool-*-win64.zip",
"rss_url": "https://vendor.example/releases.xml",
"version_regex": "v(\\d+\\.\\d+\\.\\d+)",
"static_latest_version": "1.2.3",
"notes": "maintainer notes"
}
}Current practical coverage:
- Auto-checkable today: tools configured with
update.strategy = github_release. - Maintainer-tracked static markers: tools configured with
update.strategy = static. - Not currently auto-checkable: tools without
updatemetadata (reported asunsupported-for-checking).
Future foundation:
- Update adapter seams are in place for a separate optional bump workflow.
- This release intentionally keeps update checking read-only.
# Show built-in profiles/tools from current tools.json
.\Start-Sandbox.ps1 -ListProfiles
.\Start-Sandbox.ps1 -ListTools
# Simulate profile selection + config generation without downloading or launching
.\Start-Sandbox.ps1 -DryRun -Profile network-analysis -SkipPrereqCheck
# Warm session flow (requires Windows Sandbox CLI support on host)
.\Start-Sandbox.ps1 -DryRun -Profile minimal -SessionMode Warm
# Optional WSL helper sidecar
.\Start-Sandbox.ps1 -Validate -UseWslHelper -WslDistro Ubuntu
# Audit generated artifacts and configured/requested settings without launching
.\Start-Sandbox.ps1 -Audit -Profile minimal -SharedFolder "C:\Lab\Ingress"
# Remove repo-owned disposable download/session artifacts
.\Start-Sandbox.ps1 -CleanDownloads-CleanDownloads removes only toolkit-owned disposable artifacts:
- cached setup payloads under
scripts/setups/ - generated
scripts/install-manifest.json - generated
sandbox.wsb
-CleanDownloads does not remove:
tools.json, custom profiles, docs/tests, or source files- optional shared folder content
- any paths outside the repository-owned locations above
If nothing exists yet, cleanup succeeds and reports "Nothing to clean."
# Preflight checks only (no downloads, no .wsb/install-manifest generation, no launch)
.\Start-Sandbox.ps1 -Validate
# Validate a specific profile and shared-folder input
.\Start-Sandbox.ps1 -Validate -Profile network-analysis -SharedFolder "C:\Lab\Ingress"-Validate checks:
- CLI compatibility for the current invocation.
- Manifest/profile/tool-selection readiness.
- Shared-folder safety using the same hardened path rules.
- Host prerequisite checks (PowerShell version, Windows Sandbox feature state when detectable).
- Host-interaction policy readiness (including explicit warning when startup command automation is disabled).
- Session lifecycle mode readiness (
Fresh/Warm, warm-support detection and reuse visibility). - Optional WSL helper readiness and helper-distro hardening guidance checks.
-Validate does not check:
- In-sandbox runtime behavior (installer success, clipboard/audio policy behavior, sample behavior).
- Download/source availability.
- Generated artifact contents (use
-DryRunfor generation-preview workflows).
Exit behavior:
0: validation passed (warnings may still be present).1: one or more validation checks failed.
General exit-code expectations:
-ListTools/-ListProfiles:0on success,1on fatal input/config errors.-CheckForUpdates:0on successful report generation,1on fatal input/config errors.-DryRun:0on successful resolution/artifact generation preview,1on fatal input/config errors.-Audit:0when no audit failures are present,1when audit failures are present.-CleanDownloads:0when cleanup completes (including "Nothing to clean"),1on deletion failures.- Invalid parameter combinations, unknown profiles/tools, malformed custom-profile config, and unsafe shared-folder inputs return
1.
# Generate artifacts and audit host-visible/config-visible evidence (no downloads, no launch)
.\Start-Sandbox.ps1 -Audit
.\Start-Sandbox.ps1 -Audit -Profile reverse-engineering
.\Start-Sandbox.ps1 -Audit -Profile minimal -SharedFolder "C:\Lab\Ingress"
.\Start-Sandbox.ps1 -Audit -OutputJson-Audit checks:
- Effective request context (selected/base profile, effective tools, requested networking mode).
- Generated artifact presence and parseability (
scripts/install-manifest.json,sandbox.wsb). - Requested vs generated
sandbox.wsbsettings (networking, clipboard redirection, audio input, logon command, mapped folder/read-only state). - Shared-folder mapping intent versus generated mapping.
- Session lifecycle request evidence (
Fresh/Warm) and warm-support visibility. - Optional WSL helper request/config evidence, including helper hardening status when detectable.
-Audit does not prove runtime enforcement:
- Checks are host-side/config-side evidence only.
- Findings are reported as configured/requested and present in generated artifacts.
- Runtime sandbox behavior is not verified unless explicitly stated.
Use -OutputJson with -Validate, -Audit, -DryRun, -CheckForUpdates, -ListTools, or -ListProfiles to emit JSON to stdout for CI/automation.
Human-readable console output remains the default.
.\Start-Sandbox.ps1 -Validate -OutputJson
.\Start-Sandbox.ps1 -Validate -Profile net-re-lite -OutputJson
.\Start-Sandbox.ps1 -Audit -Profile minimal -OutputJson
.\Start-Sandbox.ps1 -DryRun -Profile net-re-lite -AddTools floss -OutputJson
.\Start-Sandbox.ps1 -CheckForUpdates -Profile reverse-engineering -OutputJson
.\Start-Sandbox.ps1 -ListTools -OutputJson
.\Start-Sandbox.ps1 -ListProfiles -OutputJsonSupported modes:
-Validate -OutputJson-Audit -OutputJson-DryRun -OutputJson-CheckForUpdates -OutputJson-ListTools -OutputJson-ListProfiles -OutputJson
Intentional exclusions:
-OutputJsonis not enabled for normal launch mode.
JSON stability notes:
- Validate JSON includes stable
checks[]records (id,status,summary,remediation) plus overall status andexit_code. - Audit JSON includes effective request context, generated artifact paths, and audit checks over configured/requested artifact evidence.
- Dry-run JSON includes profile resolution, runtime overrides, final effective tool list, effective networking, stage skip details, and generated artifact paths.
- Validate/DryRun/Audit JSON includes additive
sessionandwsl_helpercontext/effective state fields. - List-tools JSON includes
command.modeandtools[]with stable catalog fields (id,display_name,installer_type,install_order,category,profiles). - List-profiles JSON includes
command.modeandprofiles[]with explicittype(built-inorcustom) andbase_profile.
Invoke with:
.\Start-Sandbox.ps1 -Audit -OutputJsonStable top-level fields (automation-safe):
command.mode(audit)overall_status(PASS,WARN,FAIL)exit_code(0or1)profile(selected,resolved_type,base_profile)overrides(add_tools,remove_tools)effective(networking_requested,tools[])artifacts(install_manifest_path,wsb_path)checks[](required per-check fields:id,status,summary,remediation)context(skip_prereq_check,requested_shared_folder,resolved_shared_folder,shared_folder_writable,runtime_verification)
Trust-boundary semantics:
- Audit evidence is host-side/config-side and reflects configured/requested artifact state.
context.runtime_verificationis currentlynot_performed.- Check summaries may include wording like
configured/requestedandnot runtime-verified.
Stability guidance:
- Breaking changes: renaming/removing stable fields above, changing their object/array shape, or changing
exit_code/overall_statussemantics. - Additive changes: adding new top-level fields, adding optional nested fields, adding new check IDs, and extending human-readable summary text.
Example payload:
{
"command": {
"mode": "audit"
},
"overall_status": "WARN",
"exit_code": 0,
"profile": {
"selected": "minimal",
"resolved_type": "built-in",
"base_profile": "minimal"
},
"overrides": {
"add_tools": [],
"remove_tools": []
},
"effective": {
"networking_requested": "Disable",
"tools": [
{
"id": "vscode",
"display_name": "Visual Studio Code",
"installer_type": "exe",
"install_order": 20
}
]
},
"artifacts": {
"install_manifest_path": "C:\\repo\\scripts\\install-manifest.json",
"wsb_path": "C:\\repo\\sandbox.wsb"
},
"checks": [
{
"id": "wsb-networking",
"status": "PASS",
"summary": "Networking setting 'Disable' is present in generated artifact as requested (configured/requested, not runtime-verified).",
"remediation": null
}
],
"context": {
"skip_prereq_check": true,
"requested_shared_folder": null,
"resolved_shared_folder": null,
"shared_folder_writable": false,
"runtime_verification": "not_performed"
}
}User-defined profiles live in optional repo-local custom-profiles.local.json.
Start from the tracked example:
Copy-Item .\custom-profiles.example.json .\custom-profiles.local.jsonExample source: custom-profiles.example.json.
Supported custom-profile shape:
- Top-level
profilesarray is required when the file exists. - Each profile entry requires:
name(non-empty, unique, must not conflict with built-in profile names)base_profile(one of built-in profiles:minimal,reverse-engineering,network-analysis,triage-plus,reverse-windows,behavior-net,dev-windows,full)
- Optional per-profile arrays:
add_tools(tool IDs to add)remove_tools(tool IDs to remove)
{
"schema_version": "1.0",
"profiles": [
{
"name": "net-re-lite",
"base_profile": "reverse-engineering",
"add_tools": ["wireshark"],
"remove_tools": ["ghidra"]
}
]
}Common authoring mistakes:
- Unknown
base_profilevalue. - Unknown tool IDs in
add_toolsorremove_tools. - Missing
profilesproperty or malformed array/object shapes. - Duplicate custom profile names or a custom
namethat matches a built-in profile.
The example is illustrative. Runtime validation (Import-CustomProfileConfig + Test-CustomProfileConfigIntegrity) remains the source of truth.
Custom profile troubleshooting:
- Symptom:
-ListProfilesor-Validatefails after editing local custom profiles. Likely cause: malformed JSON or missing required top-levelprofilesproperty. Fix: start fromcustom-profiles.example.jsonagain and reapply edits incrementally. - Symptom: validation reports unknown
base_profile. Likely cause:base_profileis not one of built-in profiles. Fix: use one ofminimal,reverse-engineering,network-analysis,triage-plus,reverse-windows,behavior-net,dev-windows, orfull. - Symptom: validation reports unknown tool IDs in
add_tools/remove_tools. Likely cause: typo or unsupported tool ID. Fix: run.\Start-Sandbox.ps1 -ListToolsand copy exact IDs. - Symptom: validation reports duplicate or conflicting custom profile names.
Likely cause: repeated
namevalues ornamematching a built-in profile. Fix: ensure each custom profile name is unique and not one of the built-in names. - Symptom: profile does not appear in
-ListProfiles. Likely cause: local file is malformed or not loaded from repo root. Fix: confirm file path/name is exactly.\custom-profiles.local.jsonand rerun-ListProfilesto surface load/validation errors.
Recommended authoring workflow:
Copy-Item .\custom-profiles.example.json .\custom-profiles.local.json
.\Start-Sandbox.ps1 -ListProfiles
.\Start-Sandbox.ps1 -Validate -Profile net-re-lite
.\Start-Sandbox.ps1 -DryRun -Profile net-re-liteRuntime override parameters:
-AddTools <id[]>-RemoveTools <id[]>
Precedence rules:
- Resolve base profile from selected built-in profile (or custom profile
base_profile). - Resolve custom-profile tool deltas when applicable (
add_tools, thenremove_tools). - Apply runtime overrides (
-AddTools, then-RemoveTools). - Final selection is deduplicated and ordered by manifest
install_order.
Examples:
# Built-in profile + add tools
.\Start-Sandbox.ps1 -Profile minimal -AddTools ghidra,wireshark
# Built-in profile + remove tools
.\Start-Sandbox.ps1 -Profile reverse-engineering -RemoveTools ghidra,hxd
# Custom profile run
.\Start-Sandbox.ps1 -Profile net-re-lite
# Custom profile validate
.\Start-Sandbox.ps1 -Validate -Profile net-re-lite
# Custom profile dry-run
.\Start-Sandbox.ps1 -DryRun -Profile net-re-lite -AddTools floss| Profile | Networking | Tools |
|---|---|---|
minimal |
❌ Disabled | VSCode, Notepad++, Python 3, Sysinternals |
reverse-engineering (default) |
❌ Disabled | + Ghidra, x64dbg, dnSpyEx, DIE, UPX, PE-bear, pestudio, HxD, FLOSS |
network-analysis |
✅ Enabled | + Wireshark, Npcap |
triage-plus |
✅ Enabled | Sysinternals, Detect-It-Easy, Dependencies, Wireshark (+ 7-Zip bootstrap) |
reverse-windows |
❌ Disabled | x64dbg, Detect-It-Easy, Dependencies, API Monitor (manual), ProcDOT (manual) (+ 7-Zip bootstrap) |
behavior-net |
✅ Enabled | Sysinternals, Wireshark, API Monitor (manual), ProcDOT (manual) (+ 7-Zip bootstrap) |
dev-windows |
❌ Disabled | Sysinternals, Visual Studio Community (manual), Windows SDK (manual) (+ 7-Zip bootstrap) |
full |
✅ Enabled | All tools |
.\Start-Sandbox.ps1 -Profile minimal
.\Start-Sandbox.ps1 -Profile network-analysisSee PROFILES.md for full details.
| Tool | Category | Source | Notes |
|---|---|---|---|
| 7-Zip | Utility | Vendor | Required; extracts all zip tools |
| Visual Studio Code | Editor | Vendor | Latest stable |
| Notepad++ | Editor | GitHub | Latest stable |
| Python 3 | Runtime | Vendor | 3.13.2 |
| Amazon Corretto JDK 21 | Runtime | Vendor | Required for Ghidra 11.x |
| Visual C++ Redist x64 | Runtime | Vendor | Required for PE-bear |
| Sysinternals Suite | Sysanalysis | Vendor | Always latest |
| Ghidra | Reversing | GitHub | Latest stable |
| x64dbg | Reversing | SourceForge | Latest snapshot |
| dnSpyEx | Reversing | GitHub | Active fork of archived dnSpy |
| Detect-It-Easy | Reversing | GitHub | Latest stable |
| Dependencies | Reversing | GitHub | Portable dependency viewer |
| UPX | Reversing | GitHub | Latest stable |
| PE-bear | Reversing | GitHub | Latest stable |
| pestudio | Reversing | Vendor | Latest |
| HxD | Reversing | Vendor | Latest |
| FLARE FLOSS | Reversing | GitHub | Latest stable |
| Wireshark | Network | Vendor | Included in network-capable profiles |
| Npcap | Network | Vendor | Manual install (no silent mode) |
| API Monitor | Sysanalysis | Manual | Advanced/manual acquisition and setup |
| ProcDOT | Sysanalysis | Manual | Advanced/manual acquisition and setup |
| Visual Studio Community | Developer | Manual | Heavy interactive installer/workloads |
| Windows SDK | Developer | Manual | Manual install (often via VS Installer workflow) |
| Tool | Reason |
|---|---|
| Python 2 | EOL since January 2020. |
| DosBox | Not relevant to malware analysis workflows. |
| Sublime Text | Redundant with VSCode and Notepad++. |
| AutoIT Extractor | Sourced from unverified GitLab CI artifact; no stable release. |
| dnSpy (original) | Archived by original author in 2022. Replaced by dnSpyEx. |
sandbox-toolkit/
├── Start-Sandbox.ps1 # Main entry point — run this on the host
├── tools.json # Tool manifest: versions, URLs, profiles, install behavior
├── sandbox.wsb.template # Reference template (actual .wsb is generated)
├── src/
│ ├── Manifest.ps1 # Manifest loading and profile filtering
│ ├── Download.ps1 # Download with retry and GitHub release resolution
│ ├── SandboxConfig.ps1 # .wsb generation
│ └── Workflow.ps1 # Session lifecycle + optional WSL helper sidecar
├── scripts/
│ ├── autostart.cmd # Thin launcher (runs on sandbox startup)
│ ├── Install-Tools.ps1 # In-sandbox install orchestrator
│ └── setups/ # Downloaded files (gitignored, populated at runtime)
├── shared/ # Optional ingress folder (gitignored, created on demand)
├── schemas/
│ └── tools.schema.json # JSON Schema for tools.json validation
├── .github/
│ └── workflows/
│ └── validate.yml # CI: PSScriptAnalyzer + manifest validation
│ └── release.yml # Tag-triggered GitHub release publication (v* tags)
├── README.md
├── docs/
│ ├── QUICKSTART.md
│ ├── PROFILES.md
│ ├── TROUBLESHOOTING.md
│ └── SAFETY.md
└── CHANGELOG.md
-
Start-Sandbox.ps1runs on your host:- Checks that Windows Sandbox is enabled.
- Loads
tools.jsonand filters tools to the selected profile. - Downloads missing tool installers to
scripts/setups/(with retry). - Writes
scripts/install-manifest.json(ephemeral session file). - Generates
sandbox.wsbwith profile-appropriate settings (networking, etc.). - Launches Windows Sandbox using selected lifecycle mode (
Freshdefault, optionalWarmvia CLI when supported). - Optionally runs bounded WSL helper staging/metadata tasks when
-UseWslHelperis requested.
-
scripts/autostart.cmdruns automatically on sandbox startup, invokingscripts/Install-Tools.ps1(in-sandbox):- Reads
install-manifest.jsonfrom the read-only mapped scripts folder. - Installs all tools in dependency order.
- Writes a detailed log to
%USERPROFILE%\Desktop\install-log.txt.
- Reads
- Networking is disabled by default (all profiles except
network-analysisandfull). - The
scripts/host folder is mapped read-only. - An optional extra shared folder can be mapped at
Desktop\shared(read-only by default). - No samples from the host are auto-executed.
- The sandbox is fully disposable; nothing persists after it closes.
See SAFETY.md for full safety guidance.
- Tool version bumps: edit
tools.json. The CI validates the manifest on every push. - New tools: add an entry following the schema in
schemas/tools.schema.json. - Profiles: update the
profilesarray on each relevant tool entry. - Releases: push an annotated
vX.Y.Ztag after updating changelog/release notes (artifacts/releases/X.Y.Z.md). The release workflow publishes/updates the GitHub release object automatically.
See LICENSE if present, or check the repository root.