NtCreateLowBoxToken
Deriva un token LowBox (AppContainer) a partir de un token existente — fija el SID de paquete y las capabilities que gobiernan el acceso al broker IPC.
Prototipo
NTSTATUS NtCreateLowBoxToken( PHANDLE TokenHandle, HANDLE ExistingTokenHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PSID PackageSid, ULONG CapabilityCount, PSID_AND_ATTRIBUTES Capabilities, ULONG HandleCount, PHANDLE Handles );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| TokenHandle | PHANDLE | out | Recibe el handle del nuevo token LowBox. |
| ExistingTokenHandle | HANDLE | in | Token origen. Requiere TOKEN_DUPLICATE. El LowBox se deriva de su user/groups. |
| DesiredAccess | ACCESS_MASK | in | Máscara de acceso solicitada sobre el nuevo token. TOKEN_ALL_ACCESS es típica si el llamante luego lo asignará. |
| ObjectAttributes | POBJECT_ATTRIBUTES | in | Atributos de objeto. SecurityQualityOfService controla el nivel de impersonación si se usa así. |
| PackageSid | PSID | in | SID de paquete AppContainer — codifica la identidad única de la app sandbox, usado para acotar nombres de objeto bajo \Sessions\...\AppContainerNamedObjects. |
| CapabilityCount | ULONG | in | Número de entradas en Capabilities. Puede ser 0. |
| Capabilities | PSID_AND_ATTRIBUTES | in | Array de SIDs de capability (internetClient, picturesLibrary, etc.) otorgadas al LowBox. |
| HandleCount | ULONG | in | Número de handles en Handles. Normalmente 0. |
| Handles | PHANDLE | in | Array opcional de handles de objeto a adjuntar al LowBox para acceder a ellos pese a las restricciones normales AC. |
IDs de syscalls por versión de Windows
| Versión 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 |
Módulo del kernel
APIs relacionadas
Stub del 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
Notas no documentadas
Es la base del sandbox AppContainer introducido en Windows 8 y usado por toda app del Windows Runtime, los procesos de contenido de Edge, el sandbox del renderer de Chrome (cuando está activo el modo AppContainer), el runtime Android WSA y el host WebView2 moderno. La rutina kernel SepCreateLowBoxToken toma el token origen, copia User/Groups/Privileges y luego apila los atributos LowBox: el SID de paquete (almacenado en `LowBoxNumberEntry` / `Package` del token) y el array de capabilities (almacenado como un grupo especial con atributos cercanos a SE_GROUP_INTEGRITY). Una vez asignado el token resultante a un proceso vía NtAssignProcessToken o usado con NtCreateUserProcess, el kernel inserta comprobaciones de acceso AppContainer: la resolución de nombres de objeto se redirige al namespace `\Sessions\<n>\AppContainerNamedObjects\<PackageSid>` por AC; el acceso a red lo gobiernan las capabilities `internetClient` / `privateNetworkClientServer`; los canales IPC de broker (interfaces RPC reservadas a AppContainers) pasan a ser accesibles.
Uso común por malware
Mayormente legítimo; el uso malicioso existe pero es raro. Patrón: un proceso que de otro modo estaría bloqueado en una interfaz RPC broker (algunos endpoints WinRT broker chequean el PackageSid del llamante y solo aceptan principales AppContainer) llama a NtCreateLowBoxToken con el PackageSid *esperado* de una app AC legítima, asigna el token a un hijo helper y atraviesa el broker como si fuera esa app. Esa es la esencia de varios PoCs de escape de sandbox publicados por Project Zero, cazadores de Google y algunos writeups de CTF (notablemente escapes del proceso de contenido de Edge y del RuntimeBroker WSA). También se ve ocasionalmente en herramientas red-team para *entrar* voluntariamente en un AppContainer — útil cuando el operador quiere que su tradecraft parezca un hijo normal de Edge para una herramienta defensiva que whitelist procesos AC.
Oportunidades de detección
Microsoft-Windows-Threat-Intelligence no emite evento dedicado a NtCreateLowBoxToken; la detección depende de efectos secundarios. Pivote fiable: *creación de un token AppContainer por un proceso no consciente de AC*. Cualquier cosa fuera de una pequeña lista blanca (svchost.exe hospedando infra AC, MicrosoftEdgeUpdate.exe, RuntimeBroker.exe, BackgroundTaskHost.exe, smsvchost.exe) invocando este syscall es anómalo. Sysmon Event 25 (process tampering) a veces se dispara en el NtAssignProcessToken subsiguiente cuando el nuevo token contradice las declaraciones del manifiesto del proceso. El proveedor ETW Microsoft-Windows-Win32k-Power puede mostrar el remapeo de namespace cuando el hijo LowBox toca `\Sessions\...\AppContainerNamedObjects\<paquete-inesperado>`. La forja de PackageSid (mismatch entre PackageSid e identidad firmada del ejecutable) es el indicador conductual más fuerte.
Ejemplos de syscalls directos
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 }
}Mapeos MITRE ATT&CK
Last verified: 2026-05-20