Windows Sandbox

Pas trop chaud en ce moment ? Si vous êtes en vacances, ne lisez pas cet article ! Mais si vous êtes au bureau … un fichier douteux a peut-être atterri sur votre poste, ou un exe reçu par mail, ou encore un script « lance ça pour réparer ton VPN ». Bref, vous devez l’ouvrir pour vérifier sans risquer votre machine ni le réseau de l’entreprise.

Cet article est fait pour vous. On va monter, étape par étape, une sandbox Windows jetable et durcie pour analyser un fichier hors-ligne, puis sa variante pour le dev, avant de voir comment pousser tout ça sur un parc de Cloud PC via Intune.

Pour vous guider plus facilement dans cet article, voici des liens rapides :

1. Le principe : un réglage par intention

Pour rappel, Windows Sandbox est un environnement Windows jetable : du Hyper-V, une seule instance à la fois, calée sur le même build que l’hôte, et tout l’état part à la poubelle à la fermeture (seul un redémarrage lancé depuis l’intérieur est conservé) :

Bac à sable Windows (WSB) offre un environnement de bureau léger et isolé pour exécuter des applications en toute sécurité. Il est idéal pour tester, déboguer, explorer des fichiers inconnus et expérimenter des outils.

Les applications installées dans le bac à sable restent isolées de l’ordinateur hôte à l’aide de la virtualisation basée sur l’hyperviseur.

Source : Microsoft Learn

Vous ouvrez, vous testez, vous fermez, il ne reste rien :

En tant que machine virtuelle jetable, Bac à sable Windows garantit la persistance du redémarrage, des temps de lancement rapides et un encombrement mémoire inférieur par rapport aux machines virtuelles complètes. Sa configuration en un clic simplifie l’expérience utilisateur.

Source : Microsoft Learn

L’erreur, c’est de croire qu’il existe un seul bon réglage. En réalité, on configure la sandbox selon l’intention. Pour de l’analyse, on veut la couper du monde : réseau désactivé, entrée en lecture seule, presse-papier coupé. Pour du dev, on veut au contraire le réseau actif pour tirer les paquets. C’est le fil rouge de cet article, et ça change tout, surtout sur un Cloud PC branché sur le réseau de l’entreprise.

Le piège classique : dans Windows Sandbox, le réseau est activé par défaut :

Enabling networking can expose untrusted applications to the internal network.

Source : Windows Sandbox overview, Microsoft Learn

Microsoft recommande d’ailleurs, pour lancer une appli non fiable, exactement la posture qu’on va construire : réseau désactivé et dossier de l’application mappé en lecture seule.

2. Étape 0 : les prérequis

Avant de commencer, trois choses côté hôte :

Savoir si la fonctionnalité Windows Sandbox est déjà activée :

Get-WindowsOptionalFeature -Online -FeatureName Containers-DisposableClientVM

Si elle ne l’est pas encore, passez par Activer ou désactiver des fonctionnalités Windows (ou en PowerShell) :

Enable-WindowsOptionalFeature -Online -FeatureName Containers-DisposableClientVM -All

L’installation de la Sandbox Windows nécessite un redémarrage du poste :

Et cela prendra une quinzaine de minutes environ :

Attention, la virtualisation doit être disponible sur la machine !

Sur un Cloud PC Windows 365, ce dernier point mérite attention : Windows Sandbox y passe par la virtualisation imbriquée, qui n’est disponible que si le Cloud PC a 4 vCPU ou plus :

Pour utiliser des charges de travail basées sur la virtualisation, le Cloud PC doit répondre à ces exigences :

  • PC cloud de 4 processeurs virtuels ou plus (le fait de réduire à 2 processeurs virtuels les PC cloud désactive la virtualisation imbriquée).
  • Être approvisionné dans l’une des régions prises en charge pour Windows 365. (La virtualisation imbriquée n’est actuellement pas prise en charge en Afrique du Sud Nord).
  • Certains utilisateurs peuvent rencontrer une baisse des performances de leur PC cloud 4 processeurs virtuels lors de l’utilisation de la virtualisation imbriquée. Pour plus d’informations sur la résolution de ces problèmes de performances.

3. Étape 1 : préparer les outils côté hôte

Créez un dossier de travail sur l’hôte, par exemple C:\Sandbox, qui servira à passer les fichiers et les scripts à la sandbox :

Puisque la sandbox d’analyse sera hors-ligne, on lui prépare tout depuis l’hôte. Je suis parti du repo mdevolde/windows-sandbox-malware, qui pose bien la mécanique, mais j’ai dû l’adapter : les scripts d’origine ne fonctionnaient pas tels quels chez moi.

Voici ma version, testée de bout en bout :

Le script preinstall.ps1 crée C:\Sandbox\install et y télécharge, via winget, six outils d’analyse : Notepad++, Sysinternals Suite, 7-Zip, x64dbg, Wireshark et PE-bear :

$currentUser = [Environment]::UserName
$dl = Join-Path -Path $env:USERPROFILE -ChildPath "Downloads"
$ProgressPreference = 'SilentlyContinue'

Write-Host "Current user: $currentUser"

$procArch = $env:PROCESSOR_ARCHITECTURE
switch -Wildcard ($procArch) {
    "AMD64"   { $arch = "x64" }
    "x86"     { $arch = "x86" }
    "*ARM64*" { $arch = "arm64" }
    "*ARM*"   { $arch = "arm" }
    default {
        $arch = "x64"
        Write-Warning "Unrecognized architecture: $procArch. Defaulting to x64."
    }
}

Write-Host "`nDetected architecture: $arch"

# Install winget if not already installed
if (Get-Command winget -ErrorAction SilentlyContinue) {
    Write-Host "winget is already installed. Skipping installation."
} else {
    Write-Host "Downloading winget."
    $release = Invoke-RestMethod https://api.github.com/repos/microsoft/winget-cli/releases/latest
    $depsUrl = $release.assets | Where-Object { $_.name -eq "DesktopAppInstaller_Dependencies.zip" } | Select-Object -ExpandProperty browser_download_url
    $msixUrl = $release.assets | Where-Object { $_.name -like "*.msixbundle" } | Select-Object -ExpandProperty browser_download_url
    Invoke-WebRequest $depsUrl -OutFile "$dl\winget-deps.zip"
    Invoke-WebRequest $msixUrl -OutFile "$dl\winget.msixbundle"

    Expand-Archive "$dl\winget-deps.zip" -DestinationPath "$dl\winget-deps" -Force
    Get-ChildItem "$dl\winget-deps\$arch\*.appx" | ForEach-Object {
        Add-AppxPackage -Path $_.FullName
    }
    Add-AppxPackage -Path "$dl\winget.msixbundle"

    Remove-Item "$dl\winget-deps.zip", "$dl\winget.msixbundle" -Force
    Remove-Item "$dl\winget-deps" -Recurse -Force
}

New-Item -ItemType Directory -Force -Path 'C:\Sandbox\install' | Out-Null

# Telecharge les outils pour installation offline dans le bac a sable
winget download Notepad++.Notepad++              --accept-source-agreements --accept-package-agreements --download-directory C:\Sandbox\install\
winget download Microsoft.Sysinternals.Suite     --accept-source-agreements --accept-package-agreements --download-directory C:\Sandbox\install\
winget download 7zip.7zip                        --accept-source-agreements --accept-package-agreements --download-directory C:\Sandbox\install\
winget download x64dbg.x64dbg                    --accept-source-agreements --accept-package-agreements --download-directory C:\Sandbox\install\
winget download WiresharkFoundation.Wireshark    --accept-source-agreements --accept-package-agreements --download-directory C:\Sandbox\install\
winget download hasherezade.PE-bear              --accept-source-agreements --accept-package-agreements --download-directory C:\Sandbox\install\

Write-Host "`nOK -> C:\Sandbox\install"
Get-ChildItem C:\Sandbox\install -Recurse -File | Select-Object Name, @{n='MB';e={[math]::Round($_.Length/1MB,1)}}

Lancez le script depuis votre poste local :

powershell -ExecutionPolicy Bypass -File .\scripts\preinstall.ps1
Attention : ne placez que du matériel de confiance dans C:\Sandbox. Ce dossier sera lu par la sandbox, il ne doit pas contenir l’échantillon suspect au moment de préparer les outils.

Les installeurs sont alors visibles dans le dossier suivant :

Continuons avec le fichier de configuration du premier test de la Sandbox.

4. Étape 2 : le .wsb durci d’analyse

Voici le fichier de configuration Sandbox dans le cadre d’un essai pour analyse :

Il coupe le réseau et le vGPU, désactive presse-papier, imprimante, micro et webcam, monte C:\Sandbox en lecture seule, et lance le script de préparation au démarrage. Il reprend les réglages du malware.wsb du repo mdevolde, avec deux ajouts de mon cru : le chemin vers le sous-dossier scripts, et l’option -NoExit qui garde la fenêtre PowerShell ouverte. Sans elle, la fenêtre se ferme aussitôt et vous ne voyez jamais les erreurs.

<Configuration>
  <VGpu>Disable</VGpu>
  <Networking>Disable</Networking>
  <AudioInput>Disable</AudioInput>
  <VideoInput>Disable</VideoInput>
  <PrinterRedirection>Disable</PrinterRedirection>
  <ClipboardRedirection>Disable</ClipboardRedirection>
  <MemoryInMB>8192</MemoryInMB>
  <MappedFolders>
    <MappedFolder>
      <HostFolder>C:\Sandbox</HostFolder>
      <SandboxFolder>C:\Users\WDAGUtilityAccount\Desktop\HostShared</SandboxFolder>
      <ReadOnly>true</ReadOnly>
    </MappedFolder>
  </MappedFolders>
  <LogonCommand>
    <Command>powershell.exe -NoProfile -NoExit -ExecutionPolicy Bypass -File C:\Users\WDAGUtilityAccount\Desktop\HostShared\scripts\SandboxSetup.ps1</Command>
  </LogonCommand>
</Configuration>

Chaque réglage a un rôle : Networking à Disable coupe toute comm réseau (pas de C2 possible) :

ReadOnly à true fait de C:\Sandbox une entrée à sens unique :

Et couper le presse-papier ferme un canal d’exfiltration.

Le compte intégré de la sandbox s’appelle WDAGUtilityAccount, d’où le chemin du bureau en sortie :

Une variante utile : on peut laisser au contraire le presse-papier actif, pour faire glisser le fichier depuis l’hôte avant de tout couper. Deux écoles : entrée par dossier en lecture seule (plus stricte) ou par presse-papier (plus pratique). À vous de choisir selon votre niveau de paranoïa.

Attention : ces configs communautaires désactivent parfois Smart App Control (WDAC) au démarrage, parce que la validation de chaque binaire écrit par un installeur peut ajouter plus de 15 minutes à une install. C’est pratique, mais c’est un abaissement de sécurité assumé, à ne pas copier sans comprendre ce que vous coupez.

5. Étape 3 : le script de démarrage

Au démarrage, la LogonCommand exécute SandboxSetup.ps1 (copié dans C:\Sandbox\scripts). Ce script installe en silence les outils depuis HostShared\install, puis prépare l’environnement pour l’analyse : affichage des extensions et des fichiers cachés, activation des chemins longs, entrées de menu contextuel (ouvrir PowerShell ou l’invite de commandes ici, éditer avec Notepad++), et quelques réglages d’ergonomie.

Voici le script tournant au démarrage de la Sandbox :

Le détail complet du script SandboxSetup.ps1 est juste ici :

Start-Transcript -Path C:\SandboxLog.txt -Force

Write-Host "Setting up Windows environment (context menu, explorer options, etc.)"

# Menu contextuel ancien style
reg.exe add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve | Out-Null
# Afficher les extensions
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "HideFileExt" /t REG_DWORD /d 0 /f | Out-Null
# Afficher les fichiers caches
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "Hidden" /t REG_DWORD /d 1 /f | Out-Null
# Chemins longs
reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem" /v "LongPathsEnabled" /t REG_DWORD /d 1 /f | Out-Null

# Contournement des installs MSI tres lentes (desactive la validation WDAC / Smart App Control)
reg add "HKLM\SYSTEM\CurrentControlSet\Control\CI\Policy" /v "VerifiedAndReputablePolicyState" /t REG_DWORD /d 0 /f | Out-Null
CiTool.exe --refresh --json | Out-Null

try { Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope LocalMachine -ErrorAction Stop | Out-Null } catch {}

# Historique du presse-papier
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Clipboard" -Name "EnableClipboardHistory" -Value 1 -Type DWord -Force

# Buffer console
New-Item -Path "HKCU:\Console" -Force | Out-Null
Set-ItemProperty -Path "HKCU:\Console" -Name "ScreenBufferSize" -Value 2147418232 -Type DWord

# Fond d'ecran fixe
New-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\DesktopSpotlight\Settings" -Force | Out-Null
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\DesktopSpotlight\Settings" -Name "EnabledState" -Value 0 -Type DWord
Copy-Item -Path "C:\Windows\Web\Wallpaper\Windows\img0.jpg" -Destination "$env:APPDATA\Microsoft\Windows\Themes\TranscodedWallpaper" -Force

# 'Open PowerShell Here' / 'Open CMD Here'
$powershellPath = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
$cmdPath        = "C:\Windows\System32\cmd.exe"
reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\MyPowerShell" /ve /d "Open PowerShell Here" /f | Out-Null
reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\MyPowerShell" /v "Icon" /t REG_SZ /d "$powershellPath,0" /f | Out-Null
reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\MyPowerShell\command" /ve /d "powershell.exe -noexit -command Set-Location -literalPath '%V'" /f | Out-Null
reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\Mycmd" /ve /d "Open CMD Here" /f | Out-Null
reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\Mycmd" /v "Icon" /t REG_SZ /d "$cmdPath,0" /f | Out-Null
reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\Mycmd\command" /ve /d 'cmd.exe /s /k cd /d "%V"' /f | Out-Null

# Nouveau > .txt
reg add "HKEY_CLASSES_ROOT\txtfile" /ve /d "Text Document" /f | Out-Null
reg add "HKEY_CLASSES_ROOT\.txt\ShellNew" /f | Out-Null
reg --% add "HKEY_CLASSES_ROOT\.txt\ShellNew" /v "NullFile" /t REG_SZ /d "" /f
reg add "HKEY_CLASSES_ROOT\.txt\ShellNew" /v "ItemName" /t REG_SZ /d "New Text Document" /f | Out-Null

# Nouveau > .ps1
reg add "HKEY_CLASSES_ROOT\.ps1" /ve /d "ps1file" /f | Out-Null
reg add "HKEY_CLASSES_ROOT\ps1file" /ve /d "PowerShell Script" /f | Out-Null
reg add "HKEY_CLASSES_ROOT\ps1file\DefaultIcon" /ve /d "%SystemRoot%\System32\imageres.dll,-5372" /f | Out-Null
reg add "HKEY_CLASSES_ROOT\.ps1\ShellNew" /ve /d "ps1file" /f | Out-Null
reg --% add "HKEY_CLASSES_ROOT\.ps1\ShellNew" /v "NullFile" /t REG_SZ /d "" /f
reg add "HKEY_CLASSES_ROOT\.ps1\ShellNew" /v "ItemName" /t REG_SZ /d "script" /f | Out-Null

# ---------- Installation des outils (offline) ----------
Write-Host "Installing offline tools..."
$ProgressPreference = 'SilentlyContinue'

$source   = "C:\Users\WDAGUtilityAccount\Desktop\HostShared\install"
$destRoot = "C:\Program Files\"

if (-not (Test-Path $source)) { Write-Error "Dossier introuvable : $source"; Stop-Transcript; return }

$currentUserPath = [Environment]::GetEnvironmentVariable("Path", "User")
$userPathEntries = @($currentUserPath -split ';' | Where-Object { $_ -and $_.Trim() -ne "" })

# 1) Dependances .exe (VC Redist dans Dependencies\) : -Recurse indispensable
Get-ChildItem -Path $source -Filter *.exe -Recurse | ForEach-Object {
    Write-Host "Installing dependency $($_.Name)"
    Start-Process -FilePath $_.FullName -ArgumentList '/install','/quiet','/norestart' -Wait
}

# 2) MSI : c'est ce que winget download produit (Notepad++, 7-Zip, Wireshark)
Get-ChildItem -Path $source -Filter *.msi | ForEach-Object {
    Write-Host "Installing MSI $($_.Name)"
    $log = "C:\msi_" + ($_.BaseName -replace '[^\w]','_') + ".log"
    $p = Start-Process msiexec.exe -ArgumentList '/i',"`"$($_.FullName)`"",'/qn','/norestart','/l*v',"`"$log`"" -Wait -PassThru
    Write-Host ("   exit code: " + $p.ExitCode)
}

# 3) ZIP portables (Sysinternals, x64dbg, PE-bear) -> Program Files + PATH
Get-ChildItem -Path $source -Filter *.zip | ForEach-Object {
    $dest      = $destRoot + $_.BaseName
    $pathToAdd = $dest
    Write-Host "Extracting $($_.Name) to $dest"
    Expand-Archive -Path $_.FullName -DestinationPath $dest -Force

    if ($dest -like "*x64dbg*") { $pathToAdd = Join-Path -Path $dest -ChildPath "release\x64" }
    if ($userPathEntries -notcontains $pathToAdd) {
        $userPathEntries += $pathToAdd
        Write-Host "Adding to PATH: $pathToAdd"
    }
}

$newUserPath = ($userPathEntries -join ';') + ';'
[Environment]::SetEnvironmentVariable("Path", $newUserPath, "User")
$env:Path = $newUserPath + [Environment]::GetEnvironmentVariable("Path", "Machine")
Write-Host "Done."

# ---------- Notepad++ : uniquement s'il est bien installe ----------
$notepadPlusPlusPath = (Get-Command notepad++.exe -ErrorAction SilentlyContinue).Source
if (-not $notepadPlusPlusPath -and (Test-Path "C:\Program Files\Notepad++\notepad++.exe")) {
    $notepadPlusPlusPath = "C:\Program Files\Notepad++\notepad++.exe"
}

if ($notepadPlusPlusPath) {
    Write-Host "Adding Notepad++ to context menu"
    $nppCmd     = '"{0}" -settingsDir="%appdata%" "%1"' -f $notepadPlusPlusPath
    $nppOpenCmd = '"{0}" -settingsDir="%appdata%"'      -f $notepadPlusPlusPath

    reg add "HKEY_CLASSES_ROOT\*\shell\Edit with Notepad++" /f | Out-Null
    reg add "HKEY_CLASSES_ROOT\*\shell\Edit with Notepad++" /v "Icon" /t REG_SZ /d "$notepadPlusPlusPath,0" /f | Out-Null
    reg add "HKEY_CLASSES_ROOT\*\shell\Edit with Notepad++\command" /ve /t REG_EXPAND_SZ /d $nppCmd /f | Out-Null
    reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\Notepad++" /ve /d "Open Notepad++" /f | Out-Null
    reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\Notepad++" /v "Icon" /t REG_SZ /d "$notepadPlusPlusPath,0" /f | Out-Null
    reg add "HKEY_CLASSES_ROOT\Directory\Background\shell\Notepad++\command" /ve /t REG_EXPAND_SZ /d $nppOpenCmd /f | Out-Null

    cmd /c assoc .txt=txtfile | Out-Null
    if (!(Test-Path 'HKLM:\SOFTWARE\Classes\txtfile\shell\open\command')) {
        New-Item -Path 'HKLM:\SOFTWARE\Classes\txtfile\shell\open\command' -Force | Out-Null
    }
    Set-ItemProperty -Path 'HKLM:\SOFTWARE\Classes\txtfile\shell\open\command' -Name '(Default)' -Value $nppCmd -Type ExpandString -Force
} else {
    Write-Warning "Notepad++ introuvable, association et menu contextuel ignores."
}

Write-Host ""
Write-Host "=== Recap ==="
Get-ChildItem $source -Recurse -File | Select-Object Name, @{n='MB';e={[math]::Round($_.Length/1MB,1)}} | Format-Table -AutoSize
Write-Host "Log complet : C:\SandboxLog.txt"
Stop-Transcript

# Decommentez ces 2 lignes une fois le debug termine
# Stop-Process -Name explorer -Force
# Start-Process explorer.exe C:\Users\WDAGUtilityAccount\Desktop\HostShared

Vous le copiez, vous le posez dans C:\Sandbox\scripts\, et la sandbox fait le reste. Concrètement, ça donne quoi ? En quelques secondes après le démarrage, vous avez un poste d’analyse prêt, hors-ligne, avec vos outils.

Un mot sur le débogage : le Start-Transcript en tête de script écrit tout dans C:\SandboxLog.txt. En cas de souci, c’est le premier endroit à regarder. Vous y verrez d’ailleurs une ligne TerminatingError(Set-ExecutionPolicy) qui n’est pas un plantage : c’est le message habituel indiquant qu’une policy plus spécifique prime, et le script continue sans problème.

6. Étape 4 : le moment de vérité

Le moment de vérité. Vous analysez avec ce dont vous avez besoin, sans jamais auto-exécuter le payload : c’est vous qui décidez quoi lancer et quand.

Déposez l’échantillon suspect dans C:\Sandbox côté hôte :

Double-cliquez sur le .wsb :

La sandbox démarre déjà outillé et coupé du monde :

Les applications s’installent :

Et voilà !

Il ne reste qu’à travailler dessus :

Et quand vous avez fini, vous fermez la fenêtre. Tout disparaît, l’échantillon compris. Rien n’a touché votre poste, rien n’a atteint le réseau.

7. Variante : une sandbox de dev auto-provisionnée

Le même mécanisme, posture inversée. Pour du dev, on veut le réseau actif pour tirer les paquets, et un script qui installe la toolchain au démarrage. Le repo M-d3bug fournit un Sandbox_opencode.wsb qui, au logon, installe Python, Node LTS, Git et Windows Terminal, puis opencode via npm.

Microsoft propose aussi de son côté un exemple officiel d’installation de VS Code au lancement.

J’ai retenu la seconde piste, et pour une bonne raison : je voulais la même approche hors-ligne que pour l’analyse. Or opencode passe par npm, qui a besoin du réseau pour aller chercher son paquet. VS Code, lui, existe en installeur autonome, donc on peut le stager sur l’hôte et l’installer sans la moindre connexion. Le principe reste le même que pour la sandbox d’analyse : tout ce qui touche à Internet se fait côté hôte, une bonne fois.

Voici le fichier de préinstall :

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$ProgressPreference = 'SilentlyContinue'
$installDir = 'C:\SandboxDev\install'
New-Item -ItemType Directory -Force -Path $installDir | Out-Null

Write-Host 'Resolution des versions...'
# Python (dernier amd64 stable)
$listing = Invoke-WebRequest 'https://www.python.org/ftp/python/' -UseBasicParsing
$py = $null
foreach ($v in ([regex]::Matches($listing.Content,'\d+\.\d+\.\d+/') |
        ForEach-Object { [Version]($_.Value -replace '/$','') } |
        Sort-Object -Descending | Select-Object -Unique)) {
    $u = "https://www.python.org/ftp/python/$v/python-$v-amd64.exe"
    try { if ((Invoke-WebRequest $u -Method Head -UseBasicParsing -ErrorAction Stop).StatusCode -eq 200) { $py=$u; break } } catch {}
}
if (-not $py) { throw 'Python introuvable.' }
# Git
$gitRel = Invoke-RestMethod 'https://api.github.com/repos/git-for-windows/git/releases/latest' -Headers @{'User-Agent'='Mozilla/5.0'}
$git = ($gitRel.assets | Where-Object {$_.name -match 'Git-.*-64-bit\.exe'} | Select-Object -First 1).browser_download_url
# Windows Terminal + deps
$wtRel = Invoke-RestMethod 'https://api.github.com/repos/microsoft/terminal/releases/latest' -Headers @{'User-Agent'='Mozilla/5.0'}
$wt = ($wtRel.assets | Where-Object {$_.name -match '^Microsoft\.WindowsTerminal_\d+\.\d+\.\d+\.\d+_8wekyb3d8bbwe\.msixbundle$'} | Select-Object -First 1).browser_download_url
$xamlRel = Invoke-RestMethod 'https://api.github.com/repos/microsoft/microsoft-ui-xaml/releases?per_page=100' -Headers @{'User-Agent'='Mozilla/5.0'}
$xaml = $null
foreach ($r in $xamlRel) {
    if ($r.tag_name -notmatch '^v2\.8\.') { continue }
    $a = $r.assets | Where-Object {$_.name -eq 'Microsoft.UI.Xaml.2.8.x64.appx'} | Select-Object -First 1
    if ($a) { $xaml = $a.browser_download_url; break }
}
$vclibs = 'https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx'
# VS Code (installeur autonome user-setup x64)
$vscode = 'https://update.code.visualstudio.com/latest/win32-x64-user/stable'

$dl = @(
    @{Url=$py;     Dest="$installDir\python-installer.exe"},
    @{Url=$git;    Dest="$installDir\git-installer.exe"},
    @{Url=$vscode; Dest="$installDir\vscode-setup.exe"},
    @{Url=$wt;     Dest="$installDir\wt.msixbundle"},
    @{Url=$vclibs; Dest="$installDir\vclibs.appx"},
    @{Url=$xaml;   Dest="$installDir\xaml.appx"}
)
foreach ($d in $dl) {
    Write-Host ("  " + (Split-Path $d.Dest -Leaf))
    $wc = New-Object System.Net.WebClient
    $wc.Headers.Add('User-Agent','Mozilla/5.0')
    $wc.DownloadFile($d.Url, $d.Dest)
}
Write-Host "OK -> $installDir"

Je télécharge localement les différents installeurs :

powershell -ExecutionPolicy Bypass -File .\DevPreinstall.ps1

C:\SandboxDev\DevSetup.ps1 (install offline, tourne dans la sandbox) :

$src  = 'C:\Users\WDAGUtilityAccount\Desktop\SandboxDev\install'  # mappe lecture seule
$work = 'C:\SandboxSetup'
New-Item -ItemType Directory -Force -Path $work | Out-Null
Copy-Item "$src\*" $work -Force

# WDAC / Smart App Control coupe pour accelerer les installs
Start-Process PowerShell -ArgumentList '-NoProfile -ExecutionPolicy Bypass -Command Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\CI\Policy -Name VerifiedAndReputablePolicyState -Value 0; echo $null | CiTool.exe -r' -Verb RunAs -WindowStyle Hidden
Start-Sleep -Seconds 3

Set-Location $work
Write-Host 'Python...'
Start-Process "$work\python-installer.exe" -ArgumentList '/quiet','InstallAllUsers=1','PrependPath=1','Include_test=0','SimpleInstall=1' -Wait
Write-Host 'Git...'
Start-Process "$work\git-installer.exe" -ArgumentList '/VERYSILENT','/NORESTART' -Wait
Write-Host 'Windows Terminal...'
Add-AppxPackage -Path "$work\vclibs.appx" -ErrorAction SilentlyContinue
Add-AppxPackage -Path "$work\xaml.appx"   -ErrorAction SilentlyContinue
Start-Sleep -Seconds 2
Add-AppxPackage -Path "$work\wt.msixbundle" -ErrorAction SilentlyContinue
Write-Host 'VS Code...'
Start-Process "$work\vscode-setup.exe" -ArgumentList '/verysilent','/suppressmsgboxes','/norestart','/mergetasks=!runcode' -Wait
Write-Host ''
Write-Host 'Termine. Environnement de dev pret (offline).'

8. Pousser tout ça sur un parc via Intune

Jusqu’ici, tout s’est joué sur une seule machine, la vôtre. Sur un parc, la question change : comment rendre Windows Sandbox disponible sur des dizaines de postes ou de Cloud PC ?

Bonne nouvelle, il n’y a rien à déployer. Windows Sandbox n’est pas une application à installer, c’est une fonctionnalité déjà présente dans Windows qu’il suffit d’activer : Containers-DisposableClientVM. La méthode de référence est celle de Peter van der Woude : un script PowerShell Intune, exécuté en contexte SYSTEM, assigné aux appareils, avec un redémarrage pour finaliser.

Enable-WindowsOptionalFeature -Online -FeatureName "Containers-DisposableClientVM" -All -NoRestart

Avant le déploiement du script :

Après le déploiement du script :

Il ne reste qu’à redémarrer le Cloud PC :

Mon retour terrain : le vrai travail n’est pas le déploiement, c’est le ciblage. Le script active la fonctionnalité même là où elle ne pourra jamais tourner. Sur un parc de Cloud PC, visez un groupe qui exclut les Cloud PC GPU et les 2 vCPU, sinon vous activez une fonctionnalité morte sur une partie du parc.

Autre usage Intune, plus discutable : enrôler une Sandbox comme device de test jetable pour valider un Win32 ou une remédiation, comme le montre Damien Van Robaeys. Ça marche, mais la sandbox s’efface à la fermeture : vous devez ré-enrôler à chaque lancement, et chaque cycle laisse un objet appareil fantôme et ingérable dans le tenant. Pratique pour un lab, salissant pour l’hygiène de production.

Quelques minutes plus tard, la sandbox devient visible sur Intune :

9. Jusqu’où faire confiance ?

Soyons honnêtes sur les limites. Windows Sandbox, c’est de l’isolation renforcée, pas un airgap.

  • Une évasion d’hyperviseur reste théoriquement possible : la sandbox partage le build et l’hyperviseur de l’hôte.
  • Certains malwares détectent qu’ils sont en sandbox et restent dormants. « Il ne s’est rien passé » ne prouve donc pas l’innocuité.
  • Réseau coupé, vous perdez l’analyse dynamique (pas d’observation des appels réseau).
  • Désactiver Smart App Control pour gagner du temps, c’est baisser une protection réelle.
Mon retour terrain : pour trancher « c’est louche ou pas ? » sur un fichier du quotidien, cette sandbox est parfaite. Pour disséquer un malware ciblé, montez un vrai labo isolé, hors du réseau et hors de votre poste de production.

10. Récap : prérequis et pièges

ÉlémentCe qu’il fautLe piège à éviter
vCPU (Cloud PC)4 vCPU ou plusRepasser à 2 vCPU désactive la virtualisation imbriquée
Type de Cloud PCNon-GPUUn Cloud PC GPU ne fait tourner ni Sandbox, ni WSL, ni Hyper-V
RégionUne région supportéeNon supporté en South Africa North
Réseau (analyse)Networking sur DisableRéseau activé par défaut, exposé au réseau interne
Réseau (dev)Networking sur EnableTélécharge et exécute des installeurs à chaque lancement
Activation IntuneCiblage GPU / 2 vCPU exclusLa fonctionnalité s’active même là où elle ne tourne pas
Enrôlement IntuneRéservé au labRé-enrôlement à chaque lancement, devices fantômes dans le tenant

Conclusion

Voilà, en quatre étapes plus une variante, vous avez une sandbox d’analyse jetable et durcie, une sandbox de dev auto-provisionnée, et de quoi déployer proprement la fonctionnalité sur un parc de Cloud PC. Concrètement, ce que vous y gagnez : ouvrir un truc pas net en isolation, en trente secondes, puis tout jeter, sans jamais toucher votre poste ni le réseau.

Les pièges à retenir :

  1. Réseau activé par défaut : pour l’analyse, coupez-le explicitement.
  2. Un réglage par intention : analyse hors-ligne, dev en ligne, ne les confondez jamais.
  3. Sur Cloud PC : 4 vCPU minimum, région supportée, jamais de GPU.
  4. Via Intune, la fonctionnalité s’active même là où elle ne tourne pas : ciblez.
  5. La sandbox n’est pas un labo d’analyse de malware ciblé.

Foncez tester : préparez C:\Sandbox, récupérez les scripts, collez le .wsb d’analyse, et lancez. Une fois le réflexe pris, on n’ouvre plus jamais un fichier douteux autrement.

🤖
Contenu assisté par IA Cet article a été rédigé avec l’assistance d’une intelligence artificielle et relu par l’auteur. Conformément à l’AI Act (UE) en vigueur depuis le 2 août 2026.