NtCreateLowBoxToken
Dérive un token LowBox (AppContainer) à partir d'un token existant — fixe le SID de package et les capabilities qui régulent l'accès au broker IPC.
Prototype
NTSTATUS NtCreateLowBoxToken( PHANDLE TokenHandle, HANDLE ExistingTokenHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PSID PackageSid, ULONG CapabilityCount, PSID_AND_ATTRIBUTES Capabilities, ULONG HandleCount, PHANDLE Handles );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| TokenHandle | PHANDLE | out | Reçoit le handle du nouveau token LowBox. |
| ExistingTokenHandle | HANDLE | in | Token source. Requiert TOKEN_DUPLICATE. Le LowBox est dérivé de son user/groupes. |
| DesiredAccess | ACCESS_MASK | in | Masque d'accès demandé sur le nouveau token. TOKEN_ALL_ACCESS est typique si l'appelant va ensuite l'assigner. |
| ObjectAttributes | POBJECT_ATTRIBUTES | in | Attributs d'objet. SecurityQualityOfService contrôle le niveau d'impersonation si utilisé ainsi. |
| PackageSid | PSID | in | SID de package AppContainer — encode l'identité unique de l'app sandboxée, sert à scoper les noms d'objets sous \Sessions\...\AppContainerNamedObjects. |
| CapabilityCount | ULONG | in | Nombre d'entrées dans Capabilities. Peut être 0. |
| Capabilities | PSID_AND_ATTRIBUTES | in | Tableau de SIDs de capability (internetClient, picturesLibrary, etc.) accordées au LowBox. |
| HandleCount | ULONG | in | Nombre de handles dans Handles. Habituellement 0. |
| Handles | PHANDLE | in | Tableau optionnel de handles d'objets à rattacher au LowBox pour qu'il y accède malgré les restrictions AC normales. |
IDs de syscalls par version de Windows
| Version de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0xA5 | win10-1507 |
| Win10 1607 | 0xA7 | win10-1607 |
| Win10 1703 | 0xAA | win10-1703 |
| Win10 1709 | 0xAB | win10-1709 |
| Win10 1803 | 0xAC | win10-1803 |
| Win10 1809 | 0xAC | win10-1809 |
| Win10 1903 | 0xAD | win10-1903 |
| Win10 1909 | 0xAD | win10-1909 |
| Win10 2004 | 0xB1 | win10-2004 |
| Win10 20H2 | 0xB1 | win10-20h2 |
| Win10 21H1 | 0xB1 | win10-21h1 |
| Win10 21H2 | 0xB2 | win10-21h2 |
| Win10 22H2 | 0xB2 | win10-22h2 |
| Win11 21H2 | 0xB5 | win11-21h2 |
| Win11 22H2 | 0xB6 | win11-22h2 |
| Win11 23H2 | 0xB6 | win11-23h2 |
| Win11 24H2 | 0xB8 | win11-24h2 |
| Server 2016 | 0xA7 | winserver-2016 |
| Server 2019 | 0xAC | winserver-2019 |
| Server 2022 | 0xB4 | winserver-2022 |
| Server 2025 | 0xB8 | winserver-2025 |
Module noyau
APIs liées
Stub du syscall
4C 8B D1 mov r10, rcx B8 B8 00 00 00 mov eax, 0xB8 F6 04 25 08 03 FE 7F 01 test byte ptr [0x7FFE0308], 1 75 03 jne short +3 0F 05 syscall C3 ret CD 2E int 2Eh C3 ret
Notes non documentées
Sous-tend le bac à sable AppContainer introduit dans Windows 8 et utilisé par toute app Windows Runtime, les processus de contenu d'Edge, le bac à sable du renderer Chrome (quand le mode AppContainer est activé), le runtime Android WSA, et l'hôte WebView2 moderne. La routine noyau SepCreateLowBoxToken prend le token source, copie User/Groups/Privileges, puis empile les attributs LowBox : le SID de package (stocké dans `LowBoxNumberEntry` / `Package` du token) et le tableau de capabilities (stocké comme un groupe spécial avec des attributs adjacents à SE_GROUP_INTEGRITY). Une fois le token résultant assigné à un processus via NtAssignProcessToken ou utilisé avec NtCreateUserProcess, le noyau insère des contrôles d'accès AppContainer : la résolution de nom d'objet est redirigée vers le namespace `\Sessions\<n>\AppContainerNamedObjects\<PackageSid>` par AC ; l'accès réseau est régulé par les capabilities `internetClient` / `privateNetworkClientServer` ; les canaux IPC broker (interfaces RPC réservées aux AppContainers) deviennent atteignables.
Usage courant par les malwares
Majoritairement légitime ; l'usage malveillant existe mais reste rare. Pattern : un processus qui serait sinon bloqué par une interface RPC broker (certains endpoints WinRT broker vérifient le PackageSid de l'appelant et n'acceptent que les principaux AppContainer) appelle NtCreateLowBoxToken avec le PackageSid *attendu* d'une app AC légitime, assigne le token à un enfant helper, et passe par le broker comme s'il était cette app. C'est l'essence de plusieurs PoCs d'évasion sandbox publiés par Project Zero, des chasseurs Google et quelques writeups CTF (notamment des évasions du content process Edge et du RuntimeBroker WSA). Aussi utilisé occasionnellement par l'outillage red-team pour *entrer* volontairement dans un AppContainer — pratique quand l'opérateur veut que son tradecraft ressemble à un enfant Edge normal pour un outil défensif qui whitelist les processus AC.
Opportunités de détection
Microsoft-Windows-Threat-Intelligence n'émet pas d'événement dédié à NtCreateLowBoxToken ; la détection doit s'appuyer sur les effets de bord. Pivot fiable : *création d'un token AppContainer par un processus non AC-aware*. Tout ce qui n'est pas dans une petite liste blanche (svchost.exe hébergeant l'infra AC, MicrosoftEdgeUpdate.exe, RuntimeBroker.exe, BackgroundTaskHost.exe, smsvchost.exe) appelant ce syscall est anormal. Sysmon Event 25 (process tampering) se déclenche parfois sur le NtAssignProcessToken qui suit, quand le nouveau token contredit les déclarations du manifeste du processus. Le provider ETW Microsoft-Windows-Win32k-Power peut montrer le remapping de namespace quand l'enfant LowBox touche `\Sessions\...\AppContainerNamedObjects\<package-inattendu>`. La forge de PackageSid (mismatch entre PackageSid et identité signée de l'exécutable) est l'indicateur comportemental le plus fort.
Exemples de syscalls directs
cVoluntarily enter a LowBox (red-team blend pattern)
// Build a LowBox token tagged with an arbitrary PackageSid, then assign it
// to a helper process so it executes as if it were that AppContainer app.
typedef NTSTATUS(NTAPI* fnNtCreateLowBoxToken)(
PHANDLE, HANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES,
PSID, ULONG, PSID_AND_ATTRIBUTES, ULONG, PHANDLE);
HANDLE BuildLowBox(HANDLE hSourceToken, PSID packageSid) {
HMODULE n = GetModuleHandleA("ntdll.dll");
fnNtCreateLowBoxToken p = (fnNtCreateLowBoxToken)
GetProcAddress(n, "NtCreateLowBoxToken");
OBJECT_ATTRIBUTES oa = { sizeof(oa) };
HANDLE hLowBox = NULL;
NTSTATUS s = p(&hLowBox, hSourceToken, TOKEN_ALL_ACCESS, &oa,
packageSid, 0, NULL, 0, NULL);
return (s == 0) ? hLowBox : NULL;
}asmx64 direct stub (Win11 24H2 SSN)
; SSN 0xB8 on win11-24h2 / winserver-2025. 9 args — most spill to the stack.
NtCreateLowBoxToken PROC
mov r10, rcx
mov eax, 0B8h
syscall
ret
NtCreateLowBoxToken ENDPrustAdd internetClient capability to a LowBox
// Capability SIDs are derived from cap names via DeriveCapabilitySidsFromName.
// Here we hard-code S-1-15-3-1 (internetClient) for brevity.
use windows_sys::Win32::Security::PSID;
use windows_sys::Win32::System::LibraryLoader::{GetModuleHandleA, GetProcAddress};
#[repr(C)]
struct SidAndAttributes { sid: PSID, attributes: u32 }
type NtCreateLowBoxToken = unsafe extern "system" fn(
out: *mut isize, src: isize, access: u32, oa: *mut u8,
package: PSID, cap_count: u32, caps: *mut SidAndAttributes,
handle_count: u32, handles: *mut isize,
) -> i32;
pub unsafe fn lowbox_with_internet(
src: isize, package: PSID, internet_client: PSID,
) -> Option<isize> {
let n = GetModuleHandleA(b"ntdll.dll\0".as_ptr());
let addr = GetProcAddress(n, b"NtCreateLowBoxToken\0".as_ptr())?;
let f: NtCreateLowBoxToken = std::mem::transmute(addr);
let mut cap = SidAndAttributes { sid: internet_client, attributes: 0x04 /* SE_GROUP_ENABLED */ };
let mut oa = [0u8; 48];
let mut out: isize = 0;
if f(&mut out, src, 0xF01FF, oa.as_mut_ptr(), package, 1, &mut cap, 0, core::ptr::null_mut()) == 0 {
Some(out)
} else { None }
}Mappings MITRE ATT&CK
Last verified: 2026-05-20