mirror of
https://github.com/naksyn/PythonMemoryModule
synced 2026-06-06 16:24:25 +00:00
db1893910c
This update include support to passing command line parameters to unmanaged exe via PEB stomping. This technique is not working with every executable since it depends on which functions are used to pass arguments. Generally, to get a universally working technique would be required to hook GetCommandlineA GetCommandlineW __getmainargs and __wgetmainargs since PEB stomping won't cover all cases, more details here: https://blog-30cm-tw.translate.goog/2020/08/windows-c-mainargc-argv.html?_x_tr_sl=auto&_x_tr_tl=en&_x_tr_hl=it&_x_tr_pto=wapp However, during my testing I found that mimikatz and several go binaries are working just by doing PEB stomping. On the other hand, cmdline passing via PEB stomping alone to mingw and VS compiled binaries won't likely work.
1193 lines
38 KiB
Python
1193 lines
38 KiB
Python
import ctypes
|
|
import sys
|
|
|
|
import windows
|
|
import windows.generated_def as gdef
|
|
from windows import winproxy
|
|
from windows.pycompat import basestring
|
|
|
|
from windows.winobject.token import Token, KNOW_INTEGRITY_LEVEL
|
|
|
|
|
|
# Specific access right
|
|
|
|
FILE_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.FILE_READ_DATA,
|
|
gdef.FILE_WRITE_DATA,
|
|
gdef.FILE_APPEND_DATA,
|
|
gdef.FILE_READ_EA,
|
|
gdef.FILE_WRITE_EA,
|
|
gdef.FILE_EXECUTE,
|
|
gdef.FILE_READ_ATTRIBUTES,
|
|
gdef.FILE_WRITE_ATTRIBUTES
|
|
)
|
|
|
|
DIRECTORY_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.FILE_LIST_DIRECTORY,
|
|
gdef.FILE_ADD_FILE,
|
|
gdef.FILE_ADD_SUBDIRECTORY,
|
|
gdef.FILE_READ_EA,
|
|
gdef.FILE_WRITE_EA,
|
|
gdef.FILE_TRAVERSE,
|
|
gdef.FILE_DELETE_CHILD,
|
|
gdef.FILE_READ_ATTRIBUTES,
|
|
gdef.FILE_WRITE_ATTRIBUTES,
|
|
)
|
|
NAMED_PIPE_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.FILE_READ_DATA,
|
|
gdef.FILE_WRITE_DATA,
|
|
gdef.FILE_CREATE_PIPE_INSTANCE,
|
|
gdef.FILE_READ_ATTRIBUTES,
|
|
gdef.FILE_WRITE_ATTRIBUTES,
|
|
)
|
|
|
|
TOKEN_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.TOKEN_ASSIGN_PRIMARY,
|
|
gdef.TOKEN_DUPLICATE,
|
|
gdef.TOKEN_IMPERSONATE,
|
|
gdef.TOKEN_QUERY,
|
|
gdef.TOKEN_QUERY_SOURCE,
|
|
gdef.TOKEN_ADJUST_PRIVILEGES,
|
|
gdef.TOKEN_ADJUST_GROUPS,
|
|
gdef.TOKEN_ADJUST_DEFAULT,
|
|
gdef.TOKEN_ADJUST_SESSIONID,
|
|
)
|
|
|
|
CLUSTER_API_ACCESS_RIGH = gdef.FlagMapper(
|
|
gdef.CLUSAPI_READ_ACCESS,
|
|
gdef.CLUSAPI_CHANGE_ACCESS,
|
|
gdef.CLUSAPI_NO_ACCESS,
|
|
)
|
|
|
|
FAX_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.FAX_JOB_SUBMIT,
|
|
gdef.FAX_JOB_QUERY,
|
|
gdef.FAX_CONFIG_QUERY,
|
|
gdef.FAX_CONFIG_SET,
|
|
gdef.FAX_PORT_QUERY,
|
|
gdef.FAX_PORT_SET,
|
|
gdef.FAX_JOB_MANAGE,
|
|
)
|
|
|
|
CALLBACK_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.CALLBACK_MODIFY_STATE,
|
|
)
|
|
|
|
MUTANT_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.MUTANT_QUERY_STATE,
|
|
)
|
|
|
|
EVENT_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.EVENT_QUERY_STATE,
|
|
gdef.EVENT_MODIFY_STATE,
|
|
)
|
|
|
|
SEMAPHORE_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.SEMAPHORE_QUERY_STATE,
|
|
gdef.SEMAPHORE_MODIFY_STATE,
|
|
)
|
|
|
|
TIMER_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.TIMER_QUERY_STATE,
|
|
gdef.TIMER_QUERY_STATE,
|
|
)
|
|
|
|
IO_COMPLETION_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.IO_COMPLETION_QUERY_STATE,
|
|
gdef.IO_COMPLETION_MODIFY_STATE,
|
|
)
|
|
|
|
PORT_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.PORT_CONNECT,
|
|
)
|
|
|
|
OBJECT_MANAGER_TYPE_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.OBJECT_TYPE_CREATE
|
|
)
|
|
|
|
OBJECT_MANAGER_DIRECTORY_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.DIRECTORY_QUERY,
|
|
gdef.DIRECTORY_TRAVERSE,
|
|
gdef.DIRECTORY_CREATE_OBJECT,
|
|
gdef.DIRECTORY_CREATE_SUBDIRECTORY,
|
|
)
|
|
|
|
OBJECT_MANAGER_SIMLINK_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.SYMBOLIC_LINK_QUERY,
|
|
)
|
|
|
|
PROCESS_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.PROCESS_TERMINATE ,
|
|
gdef.PROCESS_CREATE_THREAD ,
|
|
gdef.PROCESS_SET_SESSIONID ,
|
|
gdef.PROCESS_VM_OPERATION ,
|
|
gdef.PROCESS_VM_READ ,
|
|
gdef.PROCESS_VM_WRITE ,
|
|
gdef.PROCESS_DUP_HANDLE ,
|
|
gdef.PROCESS_CREATE_PROCESS ,
|
|
gdef.PROCESS_SET_QUOTA ,
|
|
gdef.PROCESS_SET_INFORMATION ,
|
|
gdef.PROCESS_QUERY_INFORMATION ,
|
|
gdef.PROCESS_SUSPEND_RESUME ,
|
|
gdef.PROCESS_QUERY_LIMITED_INFORMATION ,
|
|
gdef.PROCESS_SET_LIMITED_INFORMATION ,
|
|
)
|
|
|
|
THREAD_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.THREAD_TERMINATE,
|
|
gdef.THREAD_SUSPEND_RESUME,
|
|
gdef.THREAD_GET_CONTEXT,
|
|
gdef.THREAD_SET_CONTEXT,
|
|
gdef.THREAD_QUERY_INFORMATION,
|
|
gdef.THREAD_SET_INFORMATION,
|
|
gdef.THREAD_SET_THREAD_TOKEN,
|
|
gdef.THREAD_IMPERSONATE,
|
|
gdef.THREAD_DIRECT_IMPERSONATION,
|
|
)
|
|
|
|
JOB_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.JOB_OBJECT_ASSIGN_PROCESS,
|
|
gdef.JOB_OBJECT_SET_ATTRIBUTES,
|
|
gdef.JOB_OBJECT_QUERY,
|
|
gdef.JOB_OBJECT_TERMINATE,
|
|
gdef.JOB_OBJECT_SET_SECURITY_ATTRIBUTES,
|
|
)
|
|
|
|
KEY_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.KEY_QUERY_VALUE,
|
|
gdef.KEY_SET_VALUE,
|
|
gdef.KEY_CREATE_SUB_KEY,
|
|
gdef.KEY_ENUMERATE_SUB_KEYS,
|
|
gdef.KEY_NOTIFY,
|
|
gdef.KEY_CREATE_LINK,
|
|
gdef.KEY_WOW64_64KEY,
|
|
gdef.KEY_WOW64_32KEY,
|
|
# KEY_WOW64_RES (0x0300) # Just a mask of the 2 last
|
|
)
|
|
|
|
SECTION_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.SECTION_QUERY,
|
|
gdef.SECTION_MAP_WRITE,
|
|
gdef.SECTION_MAP_READ,
|
|
gdef.SECTION_MAP_EXECUTE,
|
|
gdef.SECTION_EXTEND_SIZE,
|
|
gdef.SECTION_MAP_EXECUTE_EXPLICIT,
|
|
)
|
|
|
|
COM_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.COM_EXECUTE,
|
|
gdef.COM_EXECUTE_LOCAL,
|
|
gdef.COM_EXECUTE_REMOTE,
|
|
gdef.COM_ACTIVATE_LOCAL,
|
|
gdef.COM_ACTIVATE_REMOTE,
|
|
)
|
|
|
|
SERVICE_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.SERVICE_QUERY_CONFIG,
|
|
gdef.SERVICE_CHANGE_CONFIG,
|
|
gdef.SERVICE_QUERY_STATUS,
|
|
gdef.SERVICE_ENUMERATE_DEPENDENTS,
|
|
gdef.SERVICE_START,
|
|
gdef.SERVICE_STOP,
|
|
gdef.SERVICE_PAUSE_CONTINUE,
|
|
gdef.SERVICE_INTERROGATE,
|
|
gdef.SERVICE_USER_DEFINED_CONTROL,
|
|
)
|
|
|
|
ADS_ACCESS_RIGHT = gdef.FlagMapper(
|
|
gdef.ADS_RIGHT_DS_CREATE_CHILD,
|
|
gdef.ADS_RIGHT_DS_DELETE_CHILD,
|
|
gdef.ADS_RIGHT_ACTRL_DS_LIST,
|
|
gdef.ADS_RIGHT_DS_SELF,
|
|
gdef.ADS_RIGHT_DS_READ_PROP,
|
|
gdef.ADS_RIGHT_DS_WRITE_PROP,
|
|
gdef.ADS_RIGHT_DS_DELETE_TREE,
|
|
gdef.ADS_RIGHT_DS_LIST_OBJECT,
|
|
gdef.ADS_RIGHT_DS_CONTROL_ACCESS,
|
|
)
|
|
|
|
|
|
SPECIFIC_ACCESS_RIGTH_BY_TYPE = {
|
|
None: gdef.FlagMapper(),
|
|
"file": FILE_ACCESS_RIGHT,
|
|
"directory": DIRECTORY_ACCESS_RIGHT,
|
|
"named_pipe": NAMED_PIPE_ACCESS_RIGHT,
|
|
"token": TOKEN_ACCESS_RIGHT,
|
|
"cluster_api": CLUSTER_API_ACCESS_RIGH,
|
|
"fax": FAX_ACCESS_RIGHT,
|
|
"callback": CALLBACK_ACCESS_RIGHT,
|
|
"mutant": MUTANT_ACCESS_RIGHT,
|
|
"event": EVENT_ACCESS_RIGHT,
|
|
"semaphore": SEMAPHORE_ACCESS_RIGHT,
|
|
"io_completion": IO_COMPLETION_ACCESS_RIGHT,
|
|
"port": PORT_ACCESS_RIGHT,
|
|
"object_manager_type": OBJECT_MANAGER_TYPE_ACCESS_RIGHT,
|
|
"object_manager_directory": OBJECT_MANAGER_DIRECTORY_ACCESS_RIGHT,
|
|
"object_manager_symlink": OBJECT_MANAGER_SIMLINK_ACCESS_RIGHT,
|
|
"process": PROCESS_ACCESS_RIGHT,
|
|
"thread": THREAD_ACCESS_RIGHT,
|
|
"job": JOB_ACCESS_RIGHT,
|
|
"key": KEY_ACCESS_RIGHT,
|
|
"com": COM_ACCESS_RIGHT,
|
|
"service": SERVICE_ACCESS_RIGHT,
|
|
"ad": ADS_ACCESS_RIGHT,
|
|
}
|
|
|
|
# SD
|
|
|
|
SD_CONTROL_FLAGS = gdef.FlagMapper(
|
|
gdef.SE_OWNER_DEFAULTED,
|
|
gdef.SE_GROUP_DEFAULTED,
|
|
gdef.SE_DACL_PRESENT,
|
|
gdef.SE_DACL_DEFAULTED,
|
|
gdef.SE_SACL_PRESENT,
|
|
gdef.SE_SACL_DEFAULTED,
|
|
gdef.SE_DACL_UNTRUSTED,
|
|
gdef.SE_SERVER_SECURITY,
|
|
gdef.SE_DACL_AUTO_INHERIT_REQ,
|
|
gdef.SE_SACL_AUTO_INHERIT_REQ,
|
|
gdef.SE_DACL_AUTO_INHERITED,
|
|
gdef.SE_SACL_AUTO_INHERITED,
|
|
gdef.SE_DACL_PROTECTED,
|
|
gdef.SE_SACL_PROTECTED,
|
|
gdef.SE_RM_CONTROL_VALID,
|
|
gdef.SE_SELF_RELATIVE,
|
|
)
|
|
|
|
# ACE
|
|
|
|
ACE_FLAGS = gdef.FlagMapper(
|
|
gdef.OBJECT_INHERIT_ACE ,
|
|
gdef.CONTAINER_INHERIT_ACE ,
|
|
gdef.NO_PROPAGATE_INHERIT_ACE ,
|
|
gdef.INHERIT_ONLY_ACE ,
|
|
gdef.INHERITED_ACE ,
|
|
gdef.VALID_INHERIT_FLAGS ,
|
|
gdef.SUCCESSFUL_ACCESS_ACE_FLAG,
|
|
gdef.FAILED_ACCESS_ACE_FLAG
|
|
)
|
|
|
|
ACE_MASKS = gdef.FlagMapper(
|
|
gdef.GENERIC_READ ,
|
|
gdef.GENERIC_WRITE ,
|
|
gdef.GENERIC_EXECUTE ,
|
|
gdef.GENERIC_ALL ,
|
|
gdef.READ_CONTROL ,
|
|
gdef.DELETE ,
|
|
gdef.WRITE_DAC ,
|
|
gdef.WRITE_OWNER ,
|
|
gdef.SYNCHRONIZE ,
|
|
)
|
|
|
|
MANDATORY_LABEL_ACE_MASK = gdef.FlagMapper(
|
|
gdef.SYSTEM_MANDATORY_LABEL_NO_WRITE_UP,
|
|
gdef.SYSTEM_MANDATORY_LABEL_NO_READ_UP,
|
|
gdef.SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP,
|
|
)
|
|
|
|
|
|
|
|
class AceHeader(gdef.ACE_HEADER):
|
|
"""Improved ACE_HEADER"""
|
|
def _to_ace_type(self, ace_type):
|
|
return ctypes.cast(ctypes.byref(self), ctypes.POINTER(ace_type))[0]
|
|
|
|
@property
|
|
def AceType(self):
|
|
"""The type of the Ace header"""
|
|
raw_type = super(AceHeader, self).AceType
|
|
return ACE_CLASS_TYPE_MAPPER[raw_type]
|
|
|
|
@property
|
|
def flags(self):
|
|
"""The flags of the Ace header
|
|
|
|
:type: [:class:`int`] - A list of :class:`int`
|
|
"""
|
|
return list(self._flags_generator())
|
|
|
|
def _flags_generator(self):
|
|
flags = self.AceFlags
|
|
for i in range(8): # Sizeof(AceFlags) * 8
|
|
v = flags & (1 << i)
|
|
if v:
|
|
yield ACE_FLAGS[v]
|
|
|
|
def subclass(self):
|
|
# ACE_CLASS_BY_ACE_TYPE is defined later in this file
|
|
subcls = ACE_CLASS_BY_ACE_TYPE[self.AceType]
|
|
return self._to_ace_type(subcls)
|
|
|
|
def __repr__(self):
|
|
return "<{0} type={1}>".format(type(self).__name__, self.AceType)
|
|
|
|
|
|
class AceBase(object): # Ca ou mettre flags extraction dans le ctypes generated
|
|
"""Base object for all ``ACE`` classes. provide access to an improved header"""
|
|
@property
|
|
def Header(self): # Override the ctypes Header for the struct -> return extended header
|
|
"""The Header of the ``ACE``
|
|
|
|
:type: :class:`AceHeader`
|
|
"""
|
|
addr = ctypes.addressof(self)
|
|
sheader = super(AceBase, type(self)).Header
|
|
return AceHeader.from_address(addr + sheader.offset)
|
|
|
|
|
|
class MaskAndSidACE(AceBase):
|
|
"""`Virtual` ACE for ACE struct with the following layout:
|
|
|
|
- ACE_HEADER Header
|
|
- ACCESS_MASK Mask
|
|
- DWORD SidStart
|
|
"""
|
|
|
|
def _sid_offset(self):
|
|
return type(self).SidStart.offset
|
|
|
|
@property
|
|
def sid(self):
|
|
"""The sid described by the ``ACE``
|
|
|
|
:type: :class:`windows.generated_def.winstructs.PSID`
|
|
"""
|
|
return gdef.PSID(ctypes.addressof(self) + self._sid_offset())
|
|
|
|
@property
|
|
def mask(self):
|
|
"""The list of flags described by the ``ACE``
|
|
|
|
:type: [:class:`int`] - A list of :class:`int`
|
|
"""
|
|
return list(self._mask_generator())
|
|
|
|
def _mask_generator(self):
|
|
mask = self.Mask
|
|
for i in range(32): # sizeof ACCESS_MASK * 8
|
|
v = mask & (1 << i)
|
|
if v:
|
|
yield ACE_MASKS[v]
|
|
|
|
def __repr__(self):
|
|
return "<{0} mask={1}>".format(type(self).__name__, self.Mask)
|
|
|
|
|
|
class CallbackACE(MaskAndSidACE):
|
|
@property
|
|
def application_data(self):
|
|
"""The application-specific data
|
|
|
|
see : https://msdn.microsoft.com/en-us/library/hh877860.aspx
|
|
|
|
:type: :class:`str`
|
|
"""
|
|
selfptr = ctypes.cast(ctypes.addressof(self), gdef.PUCHAR)
|
|
datastart = ctypes.sizeof(self) + self.sid.size - 4
|
|
dataend = self.Header.AceSize
|
|
return selfptr[datastart: dataend]
|
|
|
|
@property
|
|
def condition(self):
|
|
buff = windows.utils.BUFFER(gdef.BYTE).from_buffer_copy(self.application_data)
|
|
resstr = gdef.LPWSTR()
|
|
winproxy.GetStringConditionFromBinary(buff, StringAceCondition=resstr)
|
|
condition = resstr.value
|
|
winproxy.LocalFree(resstr)
|
|
return condition
|
|
|
|
|
|
class ObjectRelatedACE(MaskAndSidACE):
|
|
FLAGS_VALUES = (gdef.ACE_OBJECT_TYPE_PRESENT,
|
|
gdef.ACE_INHERITED_OBJECT_TYPE_PRESENT)
|
|
|
|
@property
|
|
def flags(self):
|
|
"""The flags of the ``ACE``
|
|
|
|
:type: [:class:`int`] - A list of :class:`int`
|
|
"""
|
|
flags = self.Flags
|
|
return [x for x in self.FLAGS_VALUES if flags & x]
|
|
|
|
@property
|
|
def object_type(self):
|
|
"""The ``ObjectType`` described in the ACE. ``None`` if `ACE`` has no ``ObjectType``
|
|
|
|
:type: :class:`~windows.generated_def.winstructs.PSID` or ``None``
|
|
"""
|
|
if not self.Flags & gdef.ACE_OBJECT_TYPE_PRESENT:
|
|
return None
|
|
return self.ObjectType
|
|
|
|
@property
|
|
def inherited_object_type(self):
|
|
"""The ``InheritedObjectType`` described in the ACE. ``None`` if `ACE`` has no ``InheritedObjectType``
|
|
|
|
:type: :class:`~windows.generated_def.winstructs.PSID` or ``None``
|
|
"""
|
|
if not self.Flags & gdef.ACE_INHERITED_OBJECT_TYPE_PRESENT:
|
|
return None
|
|
if self.Flags & gdef.ACE_OBJECT_TYPE_PRESENT:
|
|
# There is an ObjectType so our offset is the good one
|
|
return self.InheritedObjectType
|
|
# No ObjectType -> InheritedObjectType is at ObjectType offset
|
|
# Those are the same type so we can directly use ObjectType
|
|
return self.ObjectType
|
|
|
|
def _sid_offset(self):
|
|
base_offset = type(self).SidStart.offset
|
|
if not self.Flags & gdef.ACE_OBJECT_TYPE_PRESENT:
|
|
base_offset -= ctypes.sizeof(gdef.GUID)
|
|
if not self.Flags & gdef.ACE_INHERITED_OBJECT_TYPE_PRESENT:
|
|
base_offset -= ctypes.sizeof(gdef.GUID)
|
|
return base_offset
|
|
|
|
# DACL related ACE
|
|
|
|
# Allow the resolution of Header first
|
|
class AccessAllowedACE(MaskAndSidACE, gdef.ACCESS_ALLOWED_ACE):
|
|
ACE_TYPE = gdef.ACCESS_ALLOWED_ACE_TYPE
|
|
|
|
class AccessDeniedACE(MaskAndSidACE, gdef.ACCESS_DENIED_ACE):
|
|
ACE_TYPE = gdef.ACCESS_DENIED_ACE_TYPE
|
|
|
|
class AccessAllowedCallbackACE(CallbackACE, gdef.ACCESS_ALLOWED_CALLBACK_ACE):
|
|
ACE_TYPE = gdef.ACCESS_ALLOWED_CALLBACK_ACE_TYPE
|
|
|
|
class AccessDeniedCallbackACE(CallbackACE, gdef.ACCESS_DENIED_CALLBACK_ACE):
|
|
ACE_TYPE = gdef.ACCESS_DENIED_CALLBACK_ACE_TYPE
|
|
|
|
class AccessAllowedObjectACE(ObjectRelatedACE, gdef.ACCESS_ALLOWED_OBJECT_ACE):
|
|
ACE_TYPE = gdef.ACCESS_ALLOWED_OBJECT_ACE_TYPE
|
|
|
|
class AccessDeniedObjectACE(ObjectRelatedACE, gdef.ACCESS_DENIED_OBJECT_ACE):
|
|
ACE_TYPE = gdef.ACCESS_DENIED_OBJECT_ACE_TYPE
|
|
|
|
class AccessAllowedCallbackObjectACE(CallbackACE, gdef.ACCESS_ALLOWED_CALLBACK_OBJECT_ACE):
|
|
ACE_TYPE = gdef.ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE
|
|
|
|
# Strangly -> no SDDL for this one
|
|
class AccessDeniedCallbackObjectACE(CallbackACE, gdef.ACCESS_DENIED_CALLBACK_OBJECT_ACE):
|
|
ACE_TYPE = gdef.ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE
|
|
|
|
|
|
# SACL related ACE
|
|
|
|
class SystemAuditACE(MaskAndSidACE, gdef.SYSTEM_AUDIT_ACE):
|
|
ACE_TYPE = gdef.SYSTEM_AUDIT_ACE_TYPE
|
|
|
|
class SystemAlarmACE(MaskAndSidACE, gdef.SYSTEM_ALARM_ACE):
|
|
"""reserved for future use."""
|
|
ACE_TYPE = gdef.SYSTEM_ALARM_ACE_TYPE
|
|
|
|
class SystemAuditObjectACE(ObjectRelatedACE, gdef.SYSTEM_AUDIT_OBJECT_ACE):
|
|
ACE_TYPE = gdef.SYSTEM_AUDIT_OBJECT_ACE_TYPE
|
|
|
|
class SystemAlarmObjectACE(ObjectRelatedACE, gdef.SYSTEM_ALARM_OBJECT_ACE):
|
|
"""reserved for future use."""
|
|
ACE_TYPE = gdef.SYSTEM_ALARM_OBJECT_ACE_TYPE
|
|
|
|
class SystemAuditCallbackACE(CallbackACE, gdef.SYSTEM_AUDIT_CALLBACK_ACE):
|
|
ACE_TYPE = gdef.SYSTEM_AUDIT_CALLBACK_ACE_TYPE
|
|
|
|
class SystemAlarmCallbackACE(CallbackACE, gdef.SYSTEM_ALARM_CALLBACK_ACE):
|
|
"""reserved for future use."""
|
|
ACE_TYPE = gdef.SYSTEM_ALARM_CALLBACK_ACE_TYPE
|
|
|
|
class SystemAuditCallbackObjectACE(CallbackACE, gdef.SYSTEM_AUDIT_CALLBACK_OBJECT_ACE):
|
|
ACE_TYPE = gdef.SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE
|
|
|
|
class SystemAlarmCallbackObjectACE(CallbackACE, gdef.SYSTEM_ALARM_CALLBACK_OBJECT_ACE):
|
|
"""Reserved for future use"""
|
|
ACE_TYPE = gdef.SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE
|
|
|
|
class SystemMandatoryLabelACE(MaskAndSidACE, gdef.SYSTEM_MANDATORY_LABEL_ACE):
|
|
ACE_TYPE = gdef.SYSTEM_MANDATORY_LABEL_ACE_TYPE
|
|
|
|
class SystemResourceAttributeACE(MaskAndSidACE, gdef.SYSTEM_RESOURCE_ATTRIBUTE_ACE):
|
|
ACE_TYPE = gdef.SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE
|
|
|
|
@property
|
|
def attribute(self):
|
|
# Sid-size not in the initial struct
|
|
sid_size_over = self.sid.size - type(self).SidStart.size
|
|
sec_attr_addr = ctypes.addressof(self) + ctypes.sizeof(self) + sid_size_over
|
|
return ClaimSecurityAttributeRelativeV1.from_address(sec_attr_addr)
|
|
|
|
class SystemScopedPolicyIDACE(MaskAndSidACE, gdef.SYSTEM_SCOPED_POLICY_ID_ACE):
|
|
ACE_TYPE = gdef.SYSTEM_SCOPED_POLICY_ID_ACE_TYPE
|
|
|
|
class SystemProcessTrustLabelACE(MaskAndSidACE, gdef.SYSTEM_PROCESS_TRUST_LABEL_ACE):
|
|
"""Reserved. (from MSDC)"""
|
|
ACE_TYPE = gdef.SYSTEM_PROCESS_TRUST_LABEL_ACE_TYPE
|
|
|
|
|
|
ACE_CLASS_BY_ACE_TYPE = {cls.ACE_TYPE: cls for cls in (
|
|
# DACL
|
|
AccessAllowedACE,
|
|
AccessDeniedACE,
|
|
AccessAllowedCallbackACE,
|
|
AccessDeniedCallbackACE,
|
|
AccessAllowedObjectACE,
|
|
AccessDeniedObjectACE,
|
|
AccessAllowedCallbackObjectACE,
|
|
# SACL
|
|
SystemAuditACE,
|
|
SystemAlarmACE, # reserved for future use.
|
|
SystemAuditObjectACE,
|
|
SystemAlarmObjectACE, # reserved for future use.
|
|
SystemAuditCallbackACE,
|
|
SystemAlarmCallbackACE, # reserved for future use.
|
|
SystemAuditCallbackObjectACE,
|
|
SystemAlarmCallbackObjectACE, # reserved for future use.
|
|
SystemMandatoryLabelACE,
|
|
SystemResourceAttributeACE,
|
|
SystemScopedPolicyIDACE,
|
|
SystemProcessTrustLabelACE,
|
|
|
|
)}
|
|
|
|
ACE_CLASS_TYPE_MAPPER = gdef.FlagMapper(*ACE_CLASS_BY_ACE_TYPE.keys())
|
|
|
|
# CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1 follow the SYSTEM_RESOURCE_ATTRIBUTE_ACE
|
|
# For ACE of type SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE
|
|
|
|
def retrieve_long64_from_addr(addr):
|
|
return gdef.LONG64.from_address(addr).value
|
|
|
|
def retrieve_ulong64_from_addr(addr):
|
|
return gdef.ULONG64.from_address(addr).value
|
|
|
|
def retrieve_wstr_from_addr(addr):
|
|
return gdef.LPWSTR(addr).value
|
|
|
|
# https://msdn.microsoft.com/en-us/library/hh877847.aspx
|
|
def retrieve_psid_from_addr(addr):
|
|
psid_addr = addr + gdef.CLAIM_SECURITY_ATTRIBUTE_OCTET_STRING_RELATIVE.OctetString.offset
|
|
return gdef.PSID(psid_addr)
|
|
|
|
def retrieve_bool_from_addr(addr):
|
|
return bool(gdef.ULONG64.from_address(addr).value)
|
|
|
|
def retrieve_octet_string_from_addr(addr):
|
|
# Good doc: https://msdn.microsoft.com/en-us/library/hh877833.aspx
|
|
# Doc broken in: https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_claim_security_attribute_relative_v1
|
|
ostring = gdef.CLAIM_SECURITY_ATTRIBUTE_OCTET_STRING_RELATIVE.from_address(addr)
|
|
# Bypass the array limit
|
|
return ctypes.cast(ostring.OctetString, gdef.PUCHAR)[:ostring.Length]
|
|
|
|
class ClaimSecurityAttributeRelativeV1(gdef.CLAIM_SECURITY_ATTRIBUTE_RELATIVE_V1):
|
|
VALUE_ARRAY_PTR_BY_TYPE = {
|
|
gdef.CLAIM_SECURITY_ATTRIBUTE_TYPE_INT64:
|
|
("pInt64", retrieve_long64_from_addr),
|
|
gdef.CLAIM_SECURITY_ATTRIBUTE_TYPE_UINT64:
|
|
("pUint64", retrieve_ulong64_from_addr),
|
|
gdef.CLAIM_SECURITY_ATTRIBUTE_TYPE_STRING:
|
|
("ppString", retrieve_wstr_from_addr),
|
|
gdef.CLAIM_SECURITY_ATTRIBUTE_TYPE_SID:
|
|
# ppString is not the good one
|
|
# But none is doc for PSID
|
|
("ppString", retrieve_psid_from_addr),
|
|
gdef.CLAIM_SECURITY_ATTRIBUTE_TYPE_BOOLEAN:
|
|
("pUint64", retrieve_bool_from_addr),
|
|
gdef.CLAIM_SECURITY_ATTRIBUTE_TYPE_OCTET_STRING:
|
|
("pOctetString", retrieve_octet_string_from_addr),
|
|
}
|
|
|
|
@property
|
|
def name(self):
|
|
return gdef.LPWSTR(ctypes.addressof(self) + self.Name).value
|
|
|
|
@property
|
|
def values(self):
|
|
array_name, get_value = self.VALUE_ARRAY_PTR_BY_TYPE[self.ValueType]
|
|
base = ctypes.addressof(self)
|
|
array = getattr(self.Values, array_name)
|
|
# The pointer allow us to bypass the array _length_ of 1
|
|
array_ptr = ctypes.cast(array, ctypes.POINTER(array._type_))
|
|
offsets = array_ptr[:self.ValueCount]
|
|
# Cast values
|
|
return tuple(get_value(base + off) for off in offsets)
|
|
|
|
|
|
# ACL
|
|
class Acl(gdef.ACL):
|
|
"""All the ``ACE`` returned by :class:`Acl` methods/property are described in the :ref:`Ace section <security_ace>`"""
|
|
@property
|
|
def size_info(self):
|
|
size_info = gdef.ACL_SIZE_INFORMATION()
|
|
winproxy.GetAclInformation(self, ctypes.byref(size_info), ctypes.sizeof(size_info), gdef.AclSizeInformation)
|
|
return size_info
|
|
|
|
def get_ace(self, i):
|
|
"""Retrieve ``ACE`` number ``i``
|
|
|
|
:return: :class:`Ace`
|
|
"""
|
|
ace = gdef.PVOID()
|
|
winproxy.GetAce(self, i, ace)
|
|
# TODO: subclass ACL
|
|
return AceHeader.from_address(ace.value).subclass()
|
|
|
|
@property
|
|
def aces(self):
|
|
"""The list of ``ACE`` in the ACL :class:`Acl`
|
|
|
|
:type: [:class:`Ace`] - A list of ACE
|
|
"""
|
|
return list(self)
|
|
|
|
def __len__(self):
|
|
"""The number of ``ACE`` in the :class:`Acl`"""
|
|
return self.AceCount
|
|
|
|
def __getitem__(self, i):
|
|
"""Return ``ACE`` nb ``i``
|
|
|
|
:return: :class:`Ace`
|
|
"""
|
|
try:
|
|
return self.get_ace(i)
|
|
except WindowsError as e:
|
|
if e.winerror == gdef.ERROR_INVALID_PARAMETER:
|
|
raise IndexError("Invalid ACL index {0}".format(i))
|
|
raise
|
|
|
|
def __iter__(self):
|
|
"""Return an iterable over all the ``ACE`` in the :class:`Acl`
|
|
|
|
:yield: :class:`Ace`"""
|
|
for i in range(self.AceCount):
|
|
yield self.get_ace(i)
|
|
|
|
def __repr__(self):
|
|
return "<Acl count={0}>".format(self.AceCount)
|
|
|
|
PAcl = ctypes.POINTER(Acl)
|
|
|
|
# Security descriptor
|
|
class SecurityDescriptor(gdef.PSECURITY_DESCRIPTOR):
|
|
"""A Security Descriptor
|
|
|
|
.. warning::
|
|
|
|
TODO: free the underliying buffer when not needed anymore for now the underliying memory is never freed.
|
|
"""
|
|
DEFAULT_SECURITY_INFORMATION = (
|
|
gdef.OWNER_SECURITY_INFORMATION |
|
|
gdef.GROUP_SECURITY_INFORMATION |
|
|
gdef.DACL_SECURITY_INFORMATION |
|
|
gdef.ATTRIBUTE_SECURITY_INFORMATION |
|
|
gdef.LABEL_SECURITY_INFORMATION | # Integrity
|
|
# gdef.SACL_SECURITY_INFORMATION | # Need special rights
|
|
gdef.SCOPE_SECURITY_INFORMATION |
|
|
gdef.PROCESS_TRUST_LABEL_SECURITY_INFORMATION
|
|
)
|
|
|
|
# https://docs.microsoft.com/en-us/windows/win32/api/winsvc/nf-winsvc-queryserviceobjectsecurity
|
|
# Default flags for services
|
|
SERVICE_SECURITY_INFORMATION = (
|
|
gdef.OWNER_SECURITY_INFORMATION |
|
|
gdef.GROUP_SECURITY_INFORMATION |
|
|
gdef.DACL_SECURITY_INFORMATION
|
|
)
|
|
|
|
"""The default ``flags`` value for functions expecting a
|
|
`SECURITY_INFORMATION <https://docs.microsoft.com/en-us/windows/desktop/SecAuthZ/security-information>`_.
|
|
|
|
This value regroups the followings flags:
|
|
|
|
- ``OWNER_SECURITY_INFORMATION``
|
|
- ``GROUP_SECURITY_INFORMATION``
|
|
- ``DACL_SECURITY_INFORMATION``
|
|
- ``ATTRIBUTE_SECURITY_INFORMATION``
|
|
- ``SCOPE_SECURITY_INFORMATION``
|
|
- ``PROCESS_TRUST_LABEL_SECURITY_INFORMATION``
|
|
- ``LABEL_SECURITY_INFORMATION``
|
|
|
|
.. warning::
|
|
|
|
Note that the value ``SACL_SECURITY_INFORMATION`` needed to access the SACL is not present as it require the ``SeSecurityPrivilege``.
|
|
|
|
To query the SACL enable the ``SeSecurityPrivilege`` and use the parameter ``query_sacl=True`` on the functions expecting a ``flags``
|
|
|
|
see :ref:`Query SACL sample <sample_security_sacl>`
|
|
"""
|
|
|
|
_close_function = winproxy.LocalFree
|
|
|
|
@classmethod
|
|
def create(cls):
|
|
buff = windows.utils.BUFFER(gdef.SECURITY_DESCRIPTOR, 1)()
|
|
winproxy.InitializeSecurityDescriptor(buff, gdef.SECURITY_DESCRIPTOR_REVISION)
|
|
return buff.cast(windows.security.SecurityDescriptor)
|
|
|
|
@property
|
|
def control(self):
|
|
"""The security descriptor control
|
|
|
|
:type: :class:`~windows.generated_def.winstructs.SECURITY_DESCRIPTOR_CONTROL`
|
|
"""
|
|
lpdwRevision = gdef.DWORD()
|
|
control = gdef.SECURITY_DESCRIPTOR_CONTROL()
|
|
winproxy.GetSecurityDescriptorControl(self, control, lpdwRevision)
|
|
return control.value
|
|
|
|
@property
|
|
def revision(self):
|
|
"""The security descriptor's revision
|
|
|
|
:type: :class:`int`
|
|
"""
|
|
lpdwRevision = gdef.DWORD()
|
|
control = gdef.SECURITY_DESCRIPTOR_CONTROL()
|
|
winproxy.GetSecurityDescriptorControl(self, control, lpdwRevision)
|
|
return lpdwRevision.value
|
|
|
|
def get_owner(self):
|
|
"""The owner of the security descriptor
|
|
|
|
:type: :class:`~windows.generated_def.winstructs.PSID` or ``None``
|
|
"""
|
|
owner = gdef.PSID()
|
|
lpbOwnerDefaulted = gdef.BOOL()
|
|
winproxy.GetSecurityDescriptorOwner(self, owner, lpbOwnerDefaulted)
|
|
# Return None of owner is NULL
|
|
return owner or None
|
|
|
|
def set_owner(self, new_owner):
|
|
winproxy.SetSecurityDescriptorOwner(self, new_owner, bOwnerDefaulted=False)
|
|
|
|
owner = property(get_owner, set_owner)
|
|
|
|
@property
|
|
def group(self):
|
|
"""The group of the security descriptor
|
|
|
|
:type: :class:`~windows.generated_def.winstructs.PSID` or ``None``
|
|
"""
|
|
group = gdef.PSID()
|
|
lpbGroupDefaulted = gdef.BOOL()
|
|
winproxy.GetSecurityDescriptorGroup(self, group, lpbGroupDefaulted)
|
|
# Return None of group is NULL
|
|
return group or None
|
|
|
|
|
|
@property
|
|
def dacl(self):
|
|
"""The DACL of the security descriptor.
|
|
|
|
:type: :class:`Acl` or ``None`` if the DACL was ``NULL`` or not present
|
|
"""
|
|
dacl_present = gdef.BOOL()
|
|
pdacl = gdef.PACL()
|
|
lpbDaclDefaulted = gdef.BOOL()
|
|
winproxy.GetSecurityDescriptorDacl(self, dacl_present, pdacl, lpbDaclDefaulted)
|
|
if not dacl_present or not pdacl:
|
|
return None
|
|
return ctypes.cast(pdacl, PAcl)[0]
|
|
|
|
@property
|
|
def sacl(self):
|
|
"""The SACL of the security descriptor. You may need special attention to retrieve it (see :class:`DEFAULT_SECURITY_INFORMATION`)
|
|
|
|
:type: :class:`Acl` or ``None`` if the SACL was ``NULL`` or not present
|
|
"""
|
|
sacl_present = gdef.BOOL()
|
|
psacl = gdef.PACL()
|
|
lpbSaclDefaulted = gdef.BOOL()
|
|
winproxy.GetSecurityDescriptorSacl(self, sacl_present, psacl, lpbSaclDefaulted)
|
|
if not sacl_present or not psacl:
|
|
return None
|
|
return ctypes.cast(psacl, PAcl)[0]
|
|
|
|
# Constructors
|
|
@classmethod
|
|
def from_string(cls, sddl):
|
|
"""Return a new :class:`SecurityDescriptor` from the ``SDDL``.
|
|
|
|
:returns: :class:`SecurityDescriptor`
|
|
|
|
.. warning::
|
|
|
|
At the moment the underliying buffer is never freed.
|
|
|
|
See `ConvertStringSecurityDescriptorToSecurityDescriptorA <https://docs.microsoft.com/en-us/windows/desktop/api/sddl/nf-sddl-convertstringsecuritydescriptortosecuritydescriptora>`_
|
|
"""
|
|
self = cls()
|
|
winproxy.ConvertStringSecurityDescriptorToSecurityDescriptorA(
|
|
sddl.encode("ascii"),
|
|
gdef.SDDL_REVISION_1,
|
|
self,
|
|
None)
|
|
# TODO: we need to free this buffer..
|
|
# Keep track of Security Descritor state ?
|
|
return self
|
|
|
|
@classmethod
|
|
def _from_name_and_type(cls, objname, objtype, flags=DEFAULT_SECURITY_INFORMATION, query_sacl=False):
|
|
self = cls()
|
|
|
|
if query_sacl:
|
|
flags |= gdef.SACL_SECURITY_INFORMATION
|
|
|
|
winproxy.GetNamedSecurityInfoW(
|
|
objname,
|
|
objtype,
|
|
flags,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
self
|
|
)
|
|
return self
|
|
|
|
@classmethod
|
|
def _from_handle_and_type(cls, handle, objtype, flags=DEFAULT_SECURITY_INFORMATION, query_sacl=False):
|
|
self = cls()
|
|
|
|
if query_sacl:
|
|
flags |= gdef.SACL_SECURITY_INFORMATION
|
|
|
|
winproxy.GetSecurityInfo(
|
|
handle,
|
|
objtype,
|
|
flags,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
self
|
|
)
|
|
return self
|
|
|
|
|
|
def _apply_to_handle_and_type(self, handle, objtype=gdef.SE_KERNEL_OBJECT, flags=None):
|
|
# Make SecurityDescriptor method has_audit_ace ?
|
|
|
|
if flags is None:
|
|
flags = gdef.OWNER_SECURITY_INFORMATION | gdef.GROUP_SECURITY_INFORMATION | gdef.DACL_SECURITY_INFORMATION | gdef.LABEL_SECURITY_INFORMATION
|
|
if (self.sacl and
|
|
any(ace.Header.AceType != gdef.SYSTEM_MANDATORY_LABEL_ACE_TYPE for ace in self.sacl)):
|
|
flags |= gdef.SACL_SECURITY_INFORMATION
|
|
|
|
return winproxy.SetSecurityInfo(
|
|
handle,
|
|
objtype,
|
|
flags,
|
|
self.owner,
|
|
self.group,
|
|
self.dacl,
|
|
self.sacl
|
|
)
|
|
|
|
@classmethod
|
|
def from_filename(cls, filename, query_sacl=False, flags=DEFAULT_SECURITY_INFORMATION):
|
|
"""Retrieve the security descriptor for the file ``filename``"""
|
|
return cls._from_name_and_type(filename, gdef.SE_FILE_OBJECT, flags=flags, query_sacl=query_sacl)
|
|
|
|
|
|
def to_filename(self, filename, flags=0):
|
|
"Test method: WILL CHANGE"
|
|
if not flags:
|
|
if self.owner:
|
|
flags |= gdef.OWNER_SECURITY_INFORMATION
|
|
if self.group:
|
|
flags |= gdef.GROUP_SECURITY_INFORMATION
|
|
if self.dacl:
|
|
flags |= gdef.DACL_SECURITY_INFORMATION
|
|
if self.sacl:
|
|
flags |= gdef.SACL_SECURITY_INFORMATION
|
|
winproxy.SetNamedSecurityInfoW(filename, gdef.SE_FILE_OBJECT, flags, self.owner, self.group, self.dacl, self.sacl)
|
|
|
|
def to_handle(self, handle, flags=0):
|
|
if not flags:
|
|
if self.owner:
|
|
flags |= gdef.OWNER_SECURITY_INFORMATION
|
|
if self.group:
|
|
flags |= gdef.GROUP_SECURITY_INFORMATION
|
|
if self.dacl:
|
|
flags |= gdef.DACL_SECURITY_INFORMATION
|
|
if self.sacl:
|
|
flags |= gdef.SACL_SECURITY_INFORMATION
|
|
self._apply_to_handle_and_type(handle, gdef.SE_FILE_OBJECT, flags)
|
|
|
|
@classmethod
|
|
def from_service(cls, filename, query_sacl=False, flags=SERVICE_SECURITY_INFORMATION):
|
|
"""Retrieve the security descriptor for the service named ``service``"""
|
|
sd = cls._from_name_and_type(filename, gdef.SE_SERVICE, flags=flags, query_sacl=query_sacl)
|
|
sd.obj_type = "service"
|
|
return sd
|
|
|
|
@classmethod
|
|
def from_handle(cls, handle, query_sacl=False, flags=DEFAULT_SECURITY_INFORMATION, obj_type=None):
|
|
"""Retrieve the security descriptor for the kernel object described by``handle``"""
|
|
sd = cls._from_handle_and_type(handle, gdef.SE_KERNEL_OBJECT, flags=flags, query_sacl=query_sacl)
|
|
sd.obj_type = obj_type # Test
|
|
return sd
|
|
|
|
@classmethod
|
|
def from_binary(cls, data):
|
|
"""Retrieve the security descriptor described by the binary ``data``.
|
|
Binary security descriptor can be found in the registry for example
|
|
"""
|
|
if isinstance(data, basestring):
|
|
data = ctypes.c_buffer(data)
|
|
return ctypes.cast(data, cls)
|
|
|
|
def to_string(self, security_information=DEFAULT_SECURITY_INFORMATION):
|
|
"""Return the SDDL representation of the security descriptor
|
|
|
|
:type: :class:`str`
|
|
"""
|
|
result_cstr = gdef.LPSTR()
|
|
winproxy.ConvertSecurityDescriptorToStringSecurityDescriptorA(
|
|
self,
|
|
gdef.SDDL_REVISION_1,
|
|
security_information,
|
|
result_cstr,
|
|
None)
|
|
result = result_cstr.value # Retrieve a python-str copy
|
|
winproxy.LocalFree(result_cstr)
|
|
return result.decode()
|
|
|
|
__str__ = to_string
|
|
|
|
def isrelative(self):
|
|
"""[WIP] api may change"""
|
|
return bool(self.control & gdef.SE_SELF_RELATIVE)
|
|
|
|
def make_absolute(self):
|
|
"""[WIP] api may change"""
|
|
# Assert relative ?
|
|
abs_descriptor_size = gdef.DWORD(0)
|
|
dacl_size = gdef.DWORD(0)
|
|
sacl_size = gdef.DWORD(0)
|
|
owner_size = gdef.DWORD(0)
|
|
prim_groupe_size = gdef.DWORD(0)
|
|
|
|
try:
|
|
windows.winproxy.MakeAbsoluteSD(self, None, abs_descriptor_size,
|
|
None,
|
|
dacl_size,
|
|
None,
|
|
sacl_size,
|
|
None,
|
|
owner_size,
|
|
None,
|
|
prim_groupe_size)
|
|
except WindowsError as e:
|
|
if e.winerror != gdef.ERROR_INSUFFICIENT_BUFFER:
|
|
raise
|
|
|
|
abs_sd = windows.utils.BUFFER(gdef.BYTE, abs_descriptor_size.value)().cast(SecurityDescriptor)
|
|
# dacl = ctypes.create_string_buffer(dacl_size.value)
|
|
dacl = windows.utils.BUFFER(gdef.BYTE, dacl_size.value)()
|
|
sacl = windows.utils.BUFFER(gdef.BYTE, sacl_size.value)()
|
|
owner = ctypes.create_string_buffer(owner_size.value)
|
|
prim_groupe = ctypes.create_string_buffer(prim_groupe_size.value)
|
|
|
|
windows.winproxy.MakeAbsoluteSD(self, abs_sd, abs_descriptor_size,
|
|
dacl.cast(gdef.PACL),
|
|
dacl_size,
|
|
sacl.cast(gdef.PACL),
|
|
sacl_size,
|
|
owner,
|
|
owner_size,
|
|
prim_groupe,
|
|
prim_groupe_size)
|
|
|
|
abs_sd._save = [dacl, sacl, owner, prim_groupe]
|
|
return abs_sd
|
|
|
|
def make_relative(self):
|
|
"""[WIP] api may change"""
|
|
srsd_size = gdef.DWORD(0)
|
|
try:
|
|
windows.winproxy.MakeSelfRelativeSD(self, None, srsd_size)
|
|
except WindowsError as e:
|
|
if e.winerror != gdef.ERROR_INSUFFICIENT_BUFFER:
|
|
raise
|
|
|
|
srsd = windows.utils.BUFFER(gdef.BYTE, srsd_size.value)().cast(SecurityDescriptor)
|
|
windows.winproxy.MakeSelfRelativeSD(self, srsd, srsd_size)
|
|
return srsd
|
|
|
|
|
|
# If we want auto-free we need to handle relf-relative SD
|
|
# We need to keep-track of sub-object of the SD
|
|
# Just a ref to SD from SACL / DACL ?
|
|
|
|
# def __del__(self):
|
|
# if self._needs_free and sys.path is not None:
|
|
# print("FREE SELF")
|
|
# self._close_function(self)
|
|
|
|
def __repr__(self):
|
|
if (self.isrelative()):
|
|
return "<{0} (self relative format) at {1:#x}>".format(type(self).__name__, id(self))
|
|
return "<{0} (absolute format) at {1:#x}>".format(type(self).__name__, id(self))
|
|
|
|
def explain(self, type=None):
|
|
if type is None:
|
|
type = getattr(self, "obj_type", None)
|
|
return explain_sd(self, sdtype=type)
|
|
|
|
# Explain SD POC
|
|
# If ok -> new file
|
|
|
|
|
|
def explain_sid(sid):
|
|
if sid is None:
|
|
return "None"
|
|
try:
|
|
info = u"\\".join(windows.utils.lookup_sid(sid))
|
|
except WindowsError as e:
|
|
print(e)
|
|
info = u"<lookup failed>"
|
|
return u"{0} ({1})".format(info, sid)
|
|
|
|
def explain_mask(mask, sdtype):
|
|
mapper = windows.security.SPECIFIC_ACCESS_RIGTH_BY_TYPE[sdtype]
|
|
res = []
|
|
for i in range(32): # sizeof ACCESS_MASK * 8
|
|
v = mask & (1 << i)
|
|
if v:
|
|
res.append(mapper[ACE_MASKS[v]])
|
|
return res
|
|
|
|
def explain_simple_ace(ace, sdtype):
|
|
yield u"Type:"
|
|
yield u" " + str(ace.Header.AceType)
|
|
yield u"Flags: {0:#x}".format(ace.Header.AceFlags)
|
|
yield u" {0}".format(ace.Header.flags)
|
|
yield u"SID:"
|
|
yield u" " + explain_sid(ace.sid)
|
|
yield u"Mask: {0:#x}".format(ace.Mask)
|
|
mapper = windows.security.SPECIFIC_ACCESS_RIGTH_BY_TYPE[sdtype]
|
|
yield u" " + str(list(mapper[x] for x in ace.mask))
|
|
|
|
def explain_mandatory_label_ace(ace, sdtype):
|
|
assert ace.Header.AceType == gdef.SYSTEM_MANDATORY_LABEL_ACE_TYPE
|
|
yield u"Type:"
|
|
yield u" {0} (Integrity Level)".format(ace.Header.AceType)
|
|
yield u"Flags:"
|
|
yield u" {0:#x}".format(ace.Header.AceFlags)
|
|
yield u"SID:"
|
|
sid = ace.sid
|
|
count = windows.winproxy.GetSidSubAuthorityCount(sid)
|
|
integrity = windows.winproxy.GetSidSubAuthority(sid, count[0] - 1)[0]
|
|
yield u" {0} (Integrity Level: {1})".format(sid, KNOW_INTEGRITY_LEVEL[integrity])
|
|
yield u"Mask:"
|
|
mapper = MANDATORY_LABEL_ACE_MASK
|
|
yield u" " + str(list(mapper[x] for x in ace.mask))
|
|
|
|
def explain_object_related_ace(ace, sdtype):
|
|
yield u"Type:"
|
|
yield u" " + str(ace.Header.AceType)
|
|
yield u"HeaderFlags:"
|
|
yield u" {0:#x}".format(ace.Header.AceFlags)
|
|
yield u"SID:"
|
|
yield u" " + explain_sid(ace.sid)
|
|
yield u"Mask:"
|
|
mapper = windows.security.SPECIFIC_ACCESS_RIGTH_BY_TYPE[sdtype]
|
|
yield u" " + str(list(mapper[x] for x in ace.mask))
|
|
yield u"Flag:"
|
|
yield u" " + str(ace.flags)
|
|
object_type = ace.object_type
|
|
inherited_object_type = ace.inherited_object_type
|
|
if object_type:
|
|
yield u"ObjectType"
|
|
yield u" " + object_type
|
|
if inherited_object_type:
|
|
yield u"InheritedObjectType"
|
|
yield u" " + inherited_object_type
|
|
|
|
def explain_callback_ace(ace, sdtype):
|
|
yield u"Type:"
|
|
yield u" " + str(ace.Header.AceType)
|
|
yield u"HeaderFlags:"
|
|
yield u" {0:#x}".format(ace.Header.AceFlags)
|
|
yield u"SID:"
|
|
yield u" " + explain_sid(ace.sid)
|
|
yield u"Mask:"
|
|
mapper = windows.security.SPECIFIC_ACCESS_RIGTH_BY_TYPE[sdtype]
|
|
yield u" " + str(list(mapper[x] for x in ace.mask))
|
|
yield u"Application data:"
|
|
yield u" " + repr(ace.application_data.replace(b"\x00", b""))
|
|
try:
|
|
condition = ace.condition
|
|
yield u"Condition:"
|
|
yield u" " + condition
|
|
except ValueError:
|
|
pass
|
|
|
|
def explain_resource_attribute(ace, sdtype):
|
|
yield u"Type:"
|
|
yield u" " + str(ace.Header.AceType)
|
|
yield u"HeaderFlags:"
|
|
yield u" {0:#x}".format(ace.Header.AceFlags)
|
|
yield u"SID:"
|
|
yield u" " + explain_sid(ace.sid)
|
|
yield u"Mask:"
|
|
mapper = windows.security.SPECIFIC_ACCESS_RIGTH_BY_TYPE[sdtype]
|
|
yield u" " + str(list(mapper[x] for x in ace.mask))
|
|
|
|
attribute = ace.attribute
|
|
yield u"Attribute Name:"
|
|
yield u" " + attribute.name
|
|
yield u"Attribute Type:"
|
|
yield u" {0:#x}".format(attribute.ValueType)
|
|
yield u"Attribute Flags:"
|
|
yield u" {0:#x}".format(attribute.Flags)
|
|
yield u"Values:"
|
|
for value in attribute.values:
|
|
yield u" " + repr(value)
|
|
|
|
|
|
ACE_EXPLAINER = {
|
|
gdef.ACCESS_ALLOWED_ACE_TYPE: explain_simple_ace,
|
|
gdef.ACCESS_DENIED_ACE_TYPE: explain_simple_ace,
|
|
gdef.SYSTEM_MANDATORY_LABEL_ACE_TYPE: explain_mandatory_label_ace,
|
|
gdef.SYSTEM_AUDIT_ACE_TYPE: explain_simple_ace,
|
|
gdef.ACCESS_ALLOWED_OBJECT_ACE_TYPE: explain_object_related_ace,
|
|
gdef.ACCESS_DENIED_OBJECT_ACE_TYPE: explain_object_related_ace,
|
|
gdef.ACCESS_ALLOWED_CALLBACK_ACE_TYPE: explain_callback_ace,
|
|
gdef.SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE: explain_resource_attribute,
|
|
}
|
|
|
|
|
|
def explain_acl(acl, sdtype=None):
|
|
if acl is None:
|
|
yield u"None"
|
|
for ace in acl.aces:
|
|
yield str(ace)
|
|
for line in ACE_EXPLAINER[ace.Header.AceType](ace, sdtype):
|
|
yield u" " + line
|
|
|
|
def explain_sd(sd, sdtype=None):
|
|
print(u"Security Descriptor: {0}".format(sd))
|
|
print(u" Owner:")
|
|
print(u" " + explain_sid(sd.owner))
|
|
print(u" Group:")
|
|
print(u" " + explain_sid(sd.group))
|
|
print(u" Control:")
|
|
control = sd.control
|
|
print(" {0}".format([flag for flag in SD_CONTROL_FLAGS if control & flag]))
|
|
print(u" Dacl: {0}".format(sd.dacl))
|
|
if sd.dacl:
|
|
for line in explain_acl(sd.dacl, sdtype):
|
|
try:
|
|
print(u" " + line)
|
|
except UnicodeError:
|
|
print(" " + line.encode("cp1252"))
|
|
print(u" Sacl: {0}".format(sd.sacl))
|
|
if sd.sacl:
|
|
for line in explain_acl(sd.sacl, sdtype):
|
|
try:
|
|
print(u" " + line)
|
|
except UnicodeError:
|
|
print(" " + line.encode("cp1252"))
|