> Windows Syscalls
ntoskrnl.exeT1087.002T1069.002T1106

NtAccessCheckByType

Realiza una verificación de acceso de seguridad contra un descriptor de seguridad respetando una jerarquía de objetos tipados (OBJECT_TYPE_LIST).

Prototipo

NTSTATUS NtAccessCheckByType(
  PSECURITY_DESCRIPTOR  SecurityDescriptor,
  PSID                  PrincipalSelfSid,
  HANDLE                ClientToken,
  ACCESS_MASK           DesiredAccess,
  POBJECT_TYPE_LIST     ObjectTypeList,
  ULONG                 ObjectTypeListLength,
  PGENERIC_MAPPING      GenericMapping,
  PPRIVILEGE_SET        PrivilegeSet,
  PULONG                PrivilegeSetLength,
  PACCESS_MASK          GrantedAccess,
  PNTSTATUS             AccessStatus
);

Argumentos

NameTypeDirDescription
SecurityDescriptorPSECURITY_DESCRIPTORinDescriptor de seguridad del objeto tipado a verificar. Debe contener owner, group y DACL.
PrincipalSelfSidPSIDinSID opcional sustituido para entradas PRINCIPAL_SELF en la DACL (p. ej. ACE de auto-cuenta AD).
ClientTokenHANDLEinToken de impersonación del cliente cuyo acceso se evalúa.
DesiredAccessACCESS_MASKinMáscara de bits de derechos solicitados (específicos + estándar + genéricos; MAXIMUM_ALLOWED soportado).
ObjectTypeListPOBJECT_TYPE_LISTinArray que describe la jerarquía de object-type (raíz + hijos). Cada nivel puede conceder un subconjunto distinto de DesiredAccess.
ObjectTypeListLengthULONGinNúmero de entradas OBJECT_TYPE_LIST. Limitado a 256 por el kernel.
GenericMappingPGENERIC_MAPPINGinMapea GENERIC_READ/WRITE/EXECUTE/ALL a los derechos específicos del objeto.
PrivilegeSetPPRIVILEGE_SEToutRecibe los privilegios usados durante la verificación (p. ej. SeSecurityPrivilege para acceso SACL).
PrivilegeSetLengthPULONGin/outA la entrada: tamaño del buffer PrivilegeSet; a la salida: bytes realmente usados / requeridos.
GrantedAccessPACCESS_MASKoutRecibe la máscara de acceso realmente concedida (subconjunto de DesiredAccess).
AccessStatusPNTSTATUSoutRecibe STATUS_SUCCESS si se concede el acceso, STATUS_ACCESS_DENIED en caso contrario.

IDs de syscalls por versión de Windows

Versión de WindowsID de syscallBuild
Win10 15070x63win10-1507
Win10 16070x63win10-1607
Win10 17030x63win10-1703
Win10 17090x63win10-1709
Win10 18030x63win10-1803
Win10 18090x63win10-1809
Win10 19030x63win10-1903
Win10 19090x63win10-1909
Win10 20040x63win10-2004
Win10 20H20x63win10-20h2
Win10 21H10x63win10-21h1
Win10 21H20x63win10-21h2
Win10 22H20x63win10-22h2
Win11 21H20x63win11-21h2
Win11 22H20x63win11-22h2
Win11 23H20x63win11-23h2
Win11 24H20x63win11-24h2
Server 20160x63winserver-2016
Server 20190x63winserver-2019
Server 20220x63winserver-2022
Server 20250x63winserver-2025

Módulo del kernel

ntoskrnl.exeNtAccessCheckByType

APIs relacionadas

AccessCheckByTypeAccessCheckByTypeResultListAccessCheckByTypeAndAuditAlarmNtAccessCheckMapGenericMaskGetEffectiveRightsFromAclW

Stub del syscall

4C 8B D1            mov r10, rcx
B8 63 00 00 00      mov eax, 0x63
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

Primo extendido de `NtAccessCheck` que entiende una *jerarquía de objeto tipado*. Donde `NtAccessCheck` evalúa una DACL contra un DesiredAccess, `NtAccessCheckByType` recorre una `OBJECT_TYPE_LIST` (raíz → tipos hijos, identificados por GUID) y puede conceder el acceso solicitado a nivel padre mientras lo deniega en un tipo hijo específico — el modelo que usa AD para permisos finos como 'Read All Properties' frente a 'Read PasswordLastSet'. Las ACE object-specific (`ACCESS_ALLOWED_OBJECT_ACE`, `ACCESS_DENIED_OBJECT_ACE`) llevan un GUID que coincide con una de las entradas OBJECT_TYPE_LIST. El número de syscall `0x63` es estable desde Windows 10 1507 hasta Server 2025 y Win11 24H2 — uno de los syscalls más estables de la tabla.

Uso común por malware

Señal de malware débil — esta es una primitiva de seguridad self-service usada por **software del lado servidor**, no por malware. Aparece en rutas de autorización de Active Directory (lsass.exe, dsamain.exe), en COM+/Component Services (catsrv.dll), en Authorization Manager (azroles.dll) y en algún código broker de acceso MS-SQL. Los usos ofensivos documentados se limitan a: (1) reconocimiento AD de privilegio, donde un atacante invoca AccessCheckByType localmente contra su propio token más un descriptor de seguridad obtenido de un DC para enumerar exactamente qué atributos AD el principal actual puede leer/escribir — equivalente a la recolección de derechos de usuario de BloodHound pero del lado cliente y sin ruido de red; (2) cierta investigación de bypass UAC basada en COM lo ha usado para verificar que una cadena de exploit realmente cambia un bit de acceso previamente denegado. No hay uso documentado de familia de malware mayor como primitiva principal.

Oportunidades de detección

La señal telemétrica es esencialmente nula para el syscall en sí — demasiado ruidoso en cargas legítimas AD y COM+ para alarmar. Los defensores se centran en cambio en: auditoría de acceso a objeto (Event ID 4663) del lado de los propietarios de recursos (AD vía 4662 'object operation', COM+ vía auditoría de Component Services), que se dispara *tras* la decisión AccessCheckByType. ETW Microsoft-Windows-Security-Auditing cubre la categoría de auditoría AD. Para apps COM y Authorization Manager, proveedores ETW AppLog custom pueden emitir veredictos de access-check. Desde la óptica EDR, AccessCheckByType invocado por un proceso no-servidor apuntando a un descriptor de objeto AD (reconocible por los GUIDs en el OBJECT_TYPE_LIST) es un indicador débil de reconocimiento estilo BloodHound.

Ejemplos de syscalls directos

asmx64 direct stub (all builds)

; Direct syscall stub for NtAccessCheckByType (SSN 0x63 stable across all builds)
NtAccessCheckByType PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 63h          ; SSN — stable everywhere
    syscall
    ret
NtAccessCheckByType ENDP

cAD attribute-level access check (server-side pattern)

// AD authorization sample: the caller wants to read the 'unicodePwd' attribute
// on a user object. OBJECT_TYPE_LIST has the user class at level 0 and the
// attribute at level 1; AccessCheckByType returns per-level masks.
#include <windows.h>
#include <accctrl.h>
#include <aclapi.h>

BOOL CheckCanReadAttribute(PSECURITY_DESCRIPTOR sd, HANDLE clientToken,
                           const GUID* classGuid, const GUID* attrGuid)
{
    OBJECT_TYPE_LIST otl[2] = {
        { ACCESS_OBJECT_GUID, 0, (GUID*)classGuid },
        { ACCESS_PROPERTY_GUID, 0, (GUID*)attrGuid },
    };
    GENERIC_MAPPING gm = { 0x20094, 0x20028, 0, 0xF01FF };
    PRIVILEGE_SET ps; ULONG psLen = sizeof(ps);
    ACCESS_MASK granted = 0; BOOL accessStatus = FALSE;

    return AccessCheckByType(sd, NULL, clientToken, ACTRL_DS_READ_PROP,
        otl, 2, &gm, &ps, &psLen, &granted, &accessStatus) && accessStatus;
}

cLocal AD-rights enumeration (red-team recon)

// Variant: caller passes its own impersonation token plus a DC-fetched SD
// for a target object and probes a list of attribute GUIDs to discover the
// effective rights without round-tripping LDAP queries to the DC.
#include <windows.h>
#include <stdio.h>

void EnumerateLocalRights(PSECURITY_DESCRIPTOR sd, HANDLE myToken,
                          const GUID* classGuid,
                          const GUID* attrGuids, size_t n)
{
    for (size_t i = 0; i < n; ++i) {
        if (CheckCanReadAttribute(sd, myToken, classGuid, &attrGuids[i])) {
            wprintf(L"+ readable attr %zu\n", i);
        }
    }
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20