From 986664d01fee7aadf525072d92ae348d107c6756 Mon Sep 17 00:00:00 2001 From: hakril Date: Thu, 20 Dec 2018 22:48:16 +0100 Subject: [PATCH] update Doc + test + sample for token.py --- .../debug_debugger_on_setup.txt | 10 + .../samples_output/token_token_demo.txt | 21 + docs/source/token.rst | 61 ++ tests/test_token.py | 122 +++- windows/winobject/token.py | 596 ++++++++++++------ 5 files changed, 600 insertions(+), 210 deletions(-) create mode 100644 docs/source/samples_output/debug_debugger_on_setup.txt create mode 100644 docs/source/samples_output/token_token_demo.txt create mode 100644 docs/source/token.rst diff --git a/docs/source/samples_output/debug_debugger_on_setup.txt b/docs/source/samples_output/debug_debugger_on_setup.txt new file mode 100644 index 0000000..7a1fe71 --- /dev/null +++ b/docs/source/samples_output/debug_debugger_on_setup.txt @@ -0,0 +1,10 @@ +(cmd) python debug\debugger_on_setup.py +== With on_setup == +Setup called: + +Process exit: + +== Without on_setup == +Exception: EXCEPTION_BREAKPOINT(0x80000003L) + +Process exit: diff --git a/docs/source/samples_output/token_token_demo.txt b/docs/source/samples_output/token_token_demo.txt new file mode 100644 index 0000000..d2f54bd --- /dev/null +++ b/docs/source/samples_output/token_token_demo.txt @@ -0,0 +1,21 @@ +(cmd) python token\token_demo.py +Our process token is +Retrieving some infos +Username: +User: + - lookup : ('WILLIE', 'hakril') +Primary group: + - lookup : ('WILLIE', 'Aucun') + +Token Groups is +First group SID is +Some sid and attributes: + - S-1-5-21-184905214-2723199098-2761450773-513: 7 + - S-1-1-0: 7 + - S-1-5-114: 16 + +Duplicate token is +Enabling +Current thread token is +Setting impersonation token ! +Current thread token is diff --git a/docs/source/token.rst b/docs/source/token.rst new file mode 100644 index 0000000..0586e94 --- /dev/null +++ b/docs/source/token.rst @@ -0,0 +1,61 @@ +Token +""""" + +.. module:: windows.winobject.token + +This module expose the :class:`Token` object that can be primarily retrieved through: + + * :data:`windows.winobject.process.WinProcess.token` + * :data:`windows.winobject.process.WinThread.token` + * :data:`windows.current_process.token ` + * :data:`windows.current_thread.token ` + +.. note:: + + See sample :ref:`token_sample` + + +Token +''''' + +.. autoclass:: Token + :members: + :inherited-members: + + +TokenGroups +''''''''''' + +.. autoclass:: TokenGroups + :show-inheritance: + :members: + :inherited-members: + + + +TokenPrivileges +''''''''''''''' + +.. autoclass:: TokenPrivileges + :show-inheritance: + :special-members: __getitem__, __setitem__ + :members: + :inherited-members: + + +TokenSecurityAttributesInformation +'''''''''''''''''''''''''''''''''' + +.. autoclass:: TokenSecurityAttributesInformation + :show-inheritance: + :members: + :inherited-members: + + +TokenSecurityAttributeV1 +'''''''''''''''''''''''' + +.. autoclass:: TokenSecurityAttributeV1 + :show-inheritance: + :members: + :inherited-members: \ No newline at end of file diff --git a/tests/test_token.py b/tests/test_token.py index 4aac8c2..cba6150 100644 --- a/tests/test_token.py +++ b/tests/test_token.py @@ -1,28 +1,108 @@ +import pytest +import os + import windows +import windows.security import windows.generated_def as gdef +@pytest.fixture +def curtok(): + return windows.current_process.token + +@pytest.fixture +def newtok(): + return windows.current_process.token.duplicate() + +def test_token_info(curtok): + assert isinstance(curtok.computername, basestring) + assert isinstance(curtok.username, basestring) + assert isinstance(curtok.integrity, (int, long)) + assert isinstance(curtok.is_elevated, (bool)) + +def test_lower_integrity(newtok): + assert newtok.integrity != 123 + # Change token integrity + newtok.integrity = 123 + # newtok.integrity retrieve the integrity at each call so this in enough + assert newtok.integrity == 123 + +def test_token_user(curtok): + user_sid = curtok.user + assert user_sid + computername, username = windows.security.lookup_sid(user_sid) + assert computername == windows.system.computer_name + assert username == os.environ["USERNAME"] + +def test_token_id(curtok): + ntok = curtok.duplicate() + assert ntok.id != curtok.id + mid = ntok.modified_id + aid = ntok.authentication_id + ntok.enable_privilege("SeShutDownPrivilege") + mid2 = ntok.modified_id + aid2 = ntok.authentication_id + ntok.integrity -= 1 + assert ntok.modified_id != mid2 != mid + assert ntok.authentication_id == aid2 == aid -def test_token_info(): - token = windows.current_process.token - assert isinstance(token.computername, basestring) - assert isinstance(token.username, basestring) - assert isinstance(token.integrity, (int, long)) - assert isinstance(token.is_elevated, (bool)) - -def test_lower_integrity(proc32): - # Lowering the integrity in remote process - # Because we don't want to mess with the token of our testing process - - proc32.execute_python("import windows") - # We stock the handle becase lowering the integrity - # will mess with token retrieval - proc32.execute_python("token = windows.current_process.token") - proc32.execute_python("token.integrity = 123") - # execute_python will raise this in our own process :) - proc32.execute_python("assert token.integrity == 123") +def test_enable_privilege(newtok): + PRIVILEGE_NAME = "SeShutdownPrivilege" + assert not newtok.privileges[PRIVILEGE_NAME] & gdef.SE_PRIVILEGE_ENABLED + newtok.enable_privilege(PRIVILEGE_NAME) + assert newtok.privileges[PRIVILEGE_NAME] & gdef.SE_PRIVILEGE_ENABLED -def test_token_elevation(): - tok = windows.current_process.token - assert tok.TokenElevation \ No newline at end of file +def test_adjust_privilege(newtok): + PRIVILEGE_NAME = "SeShutdownPrivilege" + PRIVILEGE2_NAME = "SeTimeZonePrivilege" + tok_dup = newtok.duplicate() + assert not tok_dup.privileges[PRIVILEGE_NAME] & gdef.SE_PRIVILEGE_ENABLED + assert not tok_dup.privileges[PRIVILEGE2_NAME] & gdef.SE_PRIVILEGE_ENABLED + # Enable privilege in another token. + privs = newtok.privileges + assert not privs[PRIVILEGE_NAME] & gdef.SE_PRIVILEGE_ENABLED + assert not privs[PRIVILEGE2_NAME] & gdef.SE_PRIVILEGE_ENABLED + + privs[PRIVILEGE_NAME] = gdef.SE_PRIVILEGE_ENABLED + privs[PRIVILEGE2_NAME] = gdef.SE_PRIVILEGE_ENABLED + tok_dup.adjust_privileges(privs) + + assert tok_dup.privileges[PRIVILEGE_NAME] & gdef.SE_PRIVILEGE_ENABLED + assert tok_dup.privileges[PRIVILEGE2_NAME] & gdef.SE_PRIVILEGE_ENABLED + + assert not newtok.privileges[PRIVILEGE_NAME] & gdef.SE_PRIVILEGE_ENABLED + newtok.enable_privilege(PRIVILEGE_NAME) + assert newtok.privileges[PRIVILEGE_NAME] & gdef.SE_PRIVILEGE_ENABLED + + +def test_token_groups(curtok): + groups = curtok.groups + groups_size = groups.GroupCount + assert groups_size > 0 + assert len(groups.sids) == groups_size + assert len(groups.sids_and_attributes) == groups_size + +def test_token_duplicate(newtok): + x = newtok.duplicate() + assert x.type == newtok.type + + primtok = newtok.duplicate(type=gdef.TokenPrimary) + assert primtok.type == gdef.TokenPrimary + with pytest.raises(WindowsError): + assert x.impersonation_level + + with pytest.raises(ValueError): + # duplicate TokenPrimary -> TokenImpersonation require explicit impersonation_level + primtok.duplicate(type=gdef.TokenImpersonation) + + for i in range(gdef.SecurityAnonymous, gdef.SecurityDelegation + 1): + x = newtok.duplicate(type=gdef.TokenImpersonation, impersonation_level=i) + assert x.type == gdef.TokenImpersonation + assert x.impersonation_level == i + + + + + +# def test_token_groups diff --git a/windows/winobject/token.py b/windows/winobject/token.py index d34e16e..6739b7c 100644 --- a/windows/winobject/token.py +++ b/windows/winobject/token.py @@ -1,38 +1,23 @@ import ctypes +import functools +from __builtin__ import type as bltn_type import windows from windows import utils from windows import winproxy import windows.generated_def as gdef +import windows.security -import functools - -# Move to windows.security ? -def lookup_privilege(luid, system_name=None): - pass - -# stolen from windows.utils :D (refactor !) -def lookup_privilege_name(privilege_value): - if isinstance(privilege_value, tuple): - luid = LUID(privilege_value[1], privilege_value[0]) - privilege_value = luid - size = DWORD(0x100) - buff = ctypes.c_buffer(size.value) - winproxy.LookupPrivilegeNameA(None, privilege_value, buff, size) - return buff[:size.value] - -KNOW_INTEGRITY_LEVEL = [ -gdef.SECURITY_MANDATORY_UNTRUSTED_RID, -gdef.SECURITY_MANDATORY_LOW_RID, -gdef.SECURITY_MANDATORY_MEDIUM_RID, -gdef.SECURITY_MANDATORY_MEDIUM_PLUS_RID, -gdef.SECURITY_MANDATORY_HIGH_RID, -gdef.SECURITY_MANDATORY_SYSTEM_RID, -gdef.SECURITY_MANDATORY_PROTECTED_PROCESS_RID -] - -know_integrity_level_mapper = gdef.FlagMapper(*KNOW_INTEGRITY_LEVEL) +KNOW_INTEGRITY_LEVEL = gdef.FlagMapper( + gdef.SECURITY_MANDATORY_UNTRUSTED_RID, + gdef.SECURITY_MANDATORY_LOW_RID, + gdef.SECURITY_MANDATORY_MEDIUM_RID, + gdef.SECURITY_MANDATORY_MEDIUM_PLUS_RID, + gdef.SECURITY_MANDATORY_HIGH_RID, + gdef.SECURITY_MANDATORY_SYSTEM_RID, + gdef.SECURITY_MANDATORY_PROTECTED_PROCESS_RID +) # Voodoo to fix lookup-strangeness in class declaration def meta_craft(x): @@ -48,10 +33,18 @@ class TokenGroups(gdef.TOKEN_GROUPS): @property def sids_and_attributes(self): + """The sids and attributes of each group + + :type: [:class:`~windows.generated_def.winstructs.SID_AND_ATTRIBUTES`] - A list of :class:`~windows.generated_def.winstructs.SID_AND_ATTRIBUTES` + """ return self._groups # Something else ? @property def sids(self): + """The sids of each group + + :type: [:class:`~windows.generated_def.winstructs.PSID`] - A list of :class:`~windows.generated_def.winstructs.PSID` + """ return [g.Sid for g in self._groups] def __repr__(self): @@ -60,19 +53,87 @@ class TokenGroups(gdef.TOKEN_GROUPS): TokenGroupsType = TokenGroups # Prevent confusion with token.TokenGroups class TokenPrivileges(gdef.TOKEN_PRIVILEGES): + """Improved ``TOKEN_PRIVILEGES`` usable like a mapping""" @property def _privileges(self): return windows.utils.resized_array(self.Privileges, self.PrivilegeCount) - @property def all(self): + """The list of all privileges + + :returns: [:class:`~windows.generated_def.winstructs.LUID_AND_ATTRIBUTES`] - A list of :class:`~windows.generated_def.winstructs.LUID_AND_ATTRIBUTES` + """ return list(self._privileges) + def keys(self): + """The name of all privileges in the TokenPrivileges + + :returns: [:class:`str`] - A list of name + """ + return [self._lookup_name(p.Luid) for p in self._privileges] + + __iter__ = keys + + def items(self): + """The (name, Attribute) of all privileges in the TokenPrivileges + + :returns: [(:class:`str`, :class:`int`)] - A list of (name, Attribute) tuple + """ + return [(self._lookup_name(p.Luid), p.Attributes) for p in self._privileges] + + def _get_priv_by_name(self, name): + luid = self._lookup_value(name) + x = [p for p in self._privileges if p.Luid == luid] + if not x: + return None + assert len(x) == 1 + return x[0] + + def __getitem__(self, name): + """Retrieve the attribute value for privilege ``name`` + + :raises: KeyError if privilege ``name`` not in the TokenPrivileges + :returns: :class:`int` + """ + priv = self._get_priv_by_name(name) + if not priv: + raise KeyError(name) + return priv.Attributes + + def __setitem__(self, name, value): + """Set the attribute value for privilege ``name`` + + :raises: KeyError if privilege ``name`` not in the TokenPrivileges + """ + priv = self._get_priv_by_name(name) + if not priv: + raise KeyError(name) + priv.Attributes = value + + # __delitem__ that set SE_PRIVILEGE_REMOVED ? + + def _lookup_name(self, luid): + size = gdef.DWORD(0x100) + buff = ctypes.c_buffer(size.value) + winproxy.LookupPrivilegeNameA(None, luid, buff, size) + return buff[:size.value] + + def _lookup_value(self, name): + luid = gdef.LUID() + winproxy.LookupPrivilegeValueA(None, name, ctypes.byref(luid)) + return luid + + + TokenPrivilegesType = TokenPrivileges class TokenSecurityAttributesInformation(gdef.TOKEN_SECURITY_ATTRIBUTES_INFORMATION): @property def attributes(self): + """Return all the attributes as :class:`TokenSecurityAttributeV1` + + :type: [:class:`TokenSecurityAttributeV1`] - A list of token security attributes + """ tptr = ctypes.cast(self.Attribute.pAttributeV1, ctypes.POINTER(TokenSecurityAttributeV1)) # Well look like this cast does NOT keep a ref to self. # Setup the base object ref ourself @@ -93,10 +154,12 @@ class TokenSecurityAttributeV1(gdef.TOKEN_SECURITY_ATTRIBUTE_V1): @property def name(self): + """The name of the security attribute""" return self.Name.str @property def values(self): + """The values of the security attribute""" array_name = self.VALUE_ARRAY_PTR_BY_TYPE[self.ValueType] return getattr(self.Values, array_name)[:self.ValueCount] @@ -104,9 +167,16 @@ class TokenSecurityAttributeV1(gdef.TOKEN_SECURITY_ATTRIBUTE_V1): return """<{0} name="{1}">""".format(type(self).__name__, self.name) + # https://docs.microsoft.com/en-us/windows/desktop/SecAuthZ/access-tokens class Token(utils.AutoHandle): - """The token of a process""" + """Represent a Windows Token. + The attributes only documented by a type are from the :class:`~windows.generated_def.winstructs.TOKEN_INFORMATION_CLASS`, such return values may be improved version of the structure. + + .. note:: + + see `[MSDN] TOKEN_INFORMATION_CLASS `_ + """ def __init__(self, handle): self._handle = handle @@ -119,7 +189,7 @@ class Token(utils.AutoHandle): raise return cbsize.value - def _get_token_infomations(self, infos_class, rtype): + def get_token_infomations(self, infos_class, rtype): required_size = self._get_required_token_information_size(infos_class) requested_size = max(required_size, ctypes.sizeof(rtype)) buffer = utils.BUFFER(rtype, 1)(size=requested_size) @@ -128,136 +198,285 @@ class Token(utils.AutoHandle): return buffer[0] - craft = meta_craft(_get_token_infomations) + def set_informations(self, info_type, infos): + return winproxy.SetTokenInformation(self.handle, info_type, ctypes.byref(infos), ctypes.sizeof(infos)) + + + craft = meta_craft(get_token_infomations) # https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ne-winnt-_token_information_class - TokenUser = craft(gdef.TokenUser, gdef.TOKEN_USER) - TokenGroups = craft(gdef.TokenGroups , TokenGroupsType) - TokenPrivileges = craft(gdef.TokenPrivileges , TokenPrivilegesType) - TokenOwner = craft(gdef.TokenOwner, gdef.TOKEN_OWNER ) - TokenPrimaryGroup = craft(gdef.TokenPrimaryGroup, gdef.TOKEN_PRIMARY_GROUP) - TokenDefaultDacl = craft(gdef.TokenDefaultDacl, gdef.TOKEN_DEFAULT_DACL ) - TokenSource = craft(gdef.TokenSource, gdef.TOKEN_SOURCE) - TokenType = craft(gdef.TokenType, gdef.TOKEN_TYPE) - TokenImpersonationLevel = craft(gdef.TokenImpersonationLevel, gdef.SECURITY_IMPERSONATION_LEVEL) - TokenStatistics = craft(gdef.TokenStatistics, gdef.TOKEN_STATISTICS) - TokenRestrictedSids = craft(gdef.TokenRestrictedSids, TokenGroupsType) - TokenSessionId = craft(gdef.TokenSessionId, gdef.DWORD) - TokenGroupsAndPrivileges = craft(gdef.TokenGroupsAndPrivileges, gdef.TOKEN_GROUPS_AND_PRIVILEGES) + TokenUser = craft(gdef.TokenUser, gdef.TOKEN_USER) #: :class:`~windows.generated_def.winstructs.TOKEN_USER` + TokenGroups = craft(gdef.TokenGroups , TokenGroupsType) #: :class:`TokenGroups` + TokenPrivileges = craft(gdef.TokenPrivileges , TokenPrivilegesType) #: :class:`TokenPrivileges` + TokenOwner = craft(gdef.TokenOwner, gdef.TOKEN_OWNER) #: :class:`~windows.generated_def.winstructs.TOKEN_OWNER` + TokenPrimaryGroup = craft(gdef.TokenPrimaryGroup, gdef.TOKEN_PRIMARY_GROUP) #: :class:`~windows.generated_def.winstructs.TOKEN_PRIMARY_GROUP` + TokenDefaultDacl = craft(gdef.TokenDefaultDacl, gdef.TOKEN_DEFAULT_DACL) #: :class:`~windows.generated_def.winstructs.TOKEN_DEFAULT_DACL` + TokenSource = craft(gdef.TokenSource, gdef.TOKEN_SOURCE) #: :class:`~windows.generated_def.winstructs.TOKEN_SOURCE` + TokenType = craft(gdef.TokenType, gdef.TOKEN_TYPE) #: :class:`~windows.generated_def.winstructs.TOKEN_TYPE` + TokenImpersonationLevel = craft(gdef.TokenImpersonationLevel, gdef.SECURITY_IMPERSONATION_LEVEL) #: :class:`~windows.generated_def.winstructs.SECURITY_IMPERSONATION_LEVEL` + TokenStatistics = craft(gdef.TokenStatistics, gdef.TOKEN_STATISTICS) #: :class:`~windows.generated_def.winstructs.TOKEN_STATISTICS` + TokenRestrictedSids = craft(gdef.TokenRestrictedSids, TokenGroupsType) #: :class:`~windows.generated_def.winstructs.TokenGroups` + TokenSessionId = craft(gdef.TokenSessionId, gdef.DWORD) #: :class:`~windows.generated_def.winstructs.DWORD` + TokenGroupsAndPrivileges = craft(gdef.TokenGroupsAndPrivileges, gdef.TOKEN_GROUPS_AND_PRIVILEGES) #: :class:`~windows.generated_def.winstructs.TOKEN_GROUPS_AND_PRIVILEGES` # TokenSessionReference = craft(gdef.TokenSessionReference, ???) # Reserved. - TokenSandBoxInert = craft(gdef.TokenSandBoxInert, gdef.DWORD) + TokenSandBoxInert = craft(gdef.TokenSandBoxInert, gdef.DWORD) #: :class:`~windows.generated_def.winstructs.DWORD` # TokenAuditPolicy = craft(gdef.TokenAuditPolicy, ???) # Reserved. - TokenOrigin = craft(gdef.TokenOrigin, gdef.TOKEN_ORIGIN) - TokenElevationType = craft(gdef.TokenElevationType, gdef.TOKEN_ELEVATION_TYPE) - TokenLinkedToken = craft(gdef.TokenLinkedToken, gdef.TOKEN_LINKED_TOKEN) - TokenElevation = craft(gdef.TokenElevation, gdef.TOKEN_ELEVATION) - TokenHasRestrictions = craft(gdef.TokenHasRestrictions, gdef.DWORD) - TokenAccessInformation = craft(gdef.TokenAccessInformation, gdef.TOKEN_ACCESS_INFORMATION ) - TokenVirtualizationAllowed = craft(gdef.TokenVirtualizationAllowed, gdef.DWORD) - TokenVirtualizationEnabled = craft(gdef.TokenVirtualizationEnabled, gdef.DWORD) - TokenIntegrityLevel = craft(gdef.TokenIntegrityLevel, gdef.TOKEN_MANDATORY_LABEL ) - TokenUIAccess = craft(gdef.TokenUIAccess, gdef.DWORD) - TokenMandatoryPolicy = craft(gdef.TokenMandatoryPolicy, gdef.TOKEN_MANDATORY_POLICY) - TokenLogonSid = craft(gdef.TokenLogonSid, TokenGroupsType) - TokenIsAppContainer = craft(gdef.TokenIsAppContainer, gdef.DWORD) - TokenCapabilities = craft(gdef.TokenCapabilities, TokenGroupsType) - TokenAppContainerSid = craft(gdef.TokenAppContainerSid, gdef.TOKEN_APPCONTAINER_INFORMATION) - TokenAppContainerNumber = craft(gdef.TokenAppContainerNumber, gdef.DWORD) - TokenUserClaimAttributes = craft(gdef.TokenUserClaimAttributes, gdef.CLAIM_SECURITY_ATTRIBUTES_INFORMATION) - TokenDeviceClaimAttributes = craft(gdef.TokenDeviceClaimAttributes, gdef.CLAIM_SECURITY_ATTRIBUTES_INFORMATION) + TokenOrigin = craft(gdef.TokenOrigin, gdef.TOKEN_ORIGIN) #: :class:`~windows.generated_def.winstructs.TOKEN_ORIGIN` + TokenElevationType = craft(gdef.TokenElevationType, gdef.TOKEN_ELEVATION_TYPE) #: :class:`~windows.generated_def.winstructs.TOKEN_ELEVATION_TYPE` + TokenLinkedToken = craft(gdef.TokenLinkedToken, gdef.TOKEN_LINKED_TOKEN) #: :class:`~windows.generated_def.winstructs.TOKEN_LINKED_TOKEN` + TokenElevation = craft(gdef.TokenElevation, gdef.TOKEN_ELEVATION) #: :class:`~windows.generated_def.winstructs.TOKEN_ELEVATION` + TokenHasRestrictions = craft(gdef.TokenHasRestrictions, gdef.DWORD) #: :class:`~windows.generated_def.winstructs.DWORD` + TokenAccessInformation = craft(gdef.TokenAccessInformation, gdef.TOKEN_ACCESS_INFORMATION) #: :class:`~windows.generated_def.winstructs.TOKEN_ACCESS_INFORMATION` + TokenVirtualizationAllowed = craft(gdef.TokenVirtualizationAllowed, gdef.DWORD) #: :class:`~windows.generated_def.winstructs.DWORD` + TokenVirtualizationEnabled = craft(gdef.TokenVirtualizationEnabled, gdef.DWORD) #: :class:`~windows.generated_def.winstructs.DWORD` + TokenIntegrityLevel = craft(gdef.TokenIntegrityLevel, gdef.TOKEN_MANDATORY_LABEL) #: :class:`~windows.generated_def.winstructs.TOKEN_MANDATORY_LABEL` + TokenUIAccess = craft(gdef.TokenUIAccess, gdef.DWORD) #: :class:`~windows.generated_def.winstructs.DWORD` + TokenMandatoryPolicy = craft(gdef.TokenMandatoryPolicy, gdef.TOKEN_MANDATORY_POLICY) #: :class:`~windows.generated_def.winstructs.TOKEN_MANDATORY_POLICY` + TokenLogonSid = craft(gdef.TokenLogonSid, TokenGroupsType) #: :class:`TokenGroups` + TokenIsAppContainer = craft(gdef.TokenIsAppContainer, gdef.DWORD) #: :class:`~windows.generated_def.winstructs.DWORD` + TokenCapabilities = craft(gdef.TokenCapabilities, TokenGroupsType) #: :class:`TokenGroups` + TokenAppContainerSid = craft(gdef.TokenAppContainerSid, gdef.TOKEN_APPCONTAINER_INFORMATION) #: :class:`~windows.generated_def.winstructs.TOKEN_APPCONTAINER_INFORMATION` + TokenAppContainerNumber = craft(gdef.TokenAppContainerNumber, gdef.DWORD) #: :class:`~windows.generated_def.winstructs.DWORD` + TokenUserClaimAttributes = craft(gdef.TokenUserClaimAttributes, gdef.CLAIM_SECURITY_ATTRIBUTES_INFORMATION) #: :class:`~windows.generated_def.winstructs.CLAIM_SECURITY_ATTRIBUTES_INFORMATION` + TokenDeviceClaimAttributes = craft(gdef.TokenDeviceClaimAttributes, gdef.CLAIM_SECURITY_ATTRIBUTES_INFORMATION) #: :class:`~windows.generated_def.winstructs.CLAIM_SECURITY_ATTRIBUTES_INFORMATION` # TokenRestrictedUserClaimAttributes = craft(gdef.TokenRestrictedUserClaimAttributes, ???) # Reserved. # TokenRestrictedDeviceClaimAttributes = craft(gdef.TokenRestrictedDeviceClaimAttributes, ???) # Reserved. - TokenDeviceGroups = craft(gdef.TokenDeviceGroups, TokenGroups) - TokenRestrictedDeviceGroups = craft(gdef.TokenRestrictedDeviceGroups, gdef.TOKEN_GROUPS) + TokenDeviceGroups = craft(gdef.TokenDeviceGroups, TokenGroupsType) #: :class:`TokenGroups` + TokenRestrictedDeviceGroups = craft(gdef.TokenRestrictedDeviceGroups, gdef.TOKEN_GROUPS) #: :class:`~windows.generated_def.winstructs.TOKEN_GROUPS` # Reserved. # Structure found in ntseapi.h (thx internet) - TokenSecurityAttributes = craft(gdef.TokenSecurityAttributes, TokenSecurityAttributesInformation) + TokenSecurityAttributes = craft(gdef.TokenSecurityAttributes, TokenSecurityAttributesInformation) #: :class:`TokenSecurityAttributesInformation` + # Help would be appreciated for the structures of the following query type + # TokenIsRestricted = craft(gdef.TokenIsRestricted, ???) # Reserved. - # TokenProcessTrustLevel = craft(gdef.TokenProcessTrustLevel, ???) # Reserved. - # TokenPrivateNameSpace = craft(gdef.TokenPrivateNameSpace, ???) # Reserved. + TokenProcessTrustLevel = craft(gdef.TokenProcessTrustLevel, gdef.PSID) #: :class:`~windows.generated_def.winstructs.PSID` + # TokenPrivateNameSpace = craft(gdef.TokenPrivateNameSpace, gdef.ULONG) # Reserved. # TokenSingletonAttributes = craft(gdef.TokenSingletonAttributes, ???) # Reserved. # TokenBnoIsolation = craft(gdef.TokenBnoIsolation, ???) # Reserved. # TokenChildProcessFlags = craft(gdef.TokenChildProcessFlags, ???) # Reserved. # TokenIsLessPrivilegedAppContainer = craft(gdef.TokenIsLessPrivilegedAppContainer, ???) # Reserved. - # property arround raw 'GetTokenInformation' + # High level properties @property def user(self): + """The user sid of the token + + :type: :class:`~windows.generated_def.winstructs.PSID` + """ return self.TokenUser.User.Sid - groups = TokenGroups # The property not the ctypes Struct + @property + def username(self): + """The username of the token + + :type: :class:`str` + """ + return self._user_and_computer_name()[1] + + @property + def computername(self): + """The computername of the token + + :type: :class:`str` + """ + return self._user_and_computer_name()[0] + + def _user_and_computer_name(self): + return windows.security.lookup_sid(self.user) + + + groups = TokenGroups #: Alias for TokenGroups (type may change in the future for improved struct) @property def owner(self): + """The owner sid of the token + + :type: :class:`~windows.generated_def.winstructs.PSID` + """ return self.TokenOwner.Owner @property def primary_group(self): + """The sid of the primary group of the token + + :type: :class:`~windows.generated_def.winstructs.PSID` + """ return self.TokenPrimaryGroup.PrimaryGroup @property def default_dacl(self): - return self._get_token_infomations(gdef.TokenDefaultDacl, windows.security.PAcl)[0] + """The defaul DACL of the token + + :type: :class:`windows.security.Acl` + """ + return self.get_token_infomations(gdef.TokenDefaultDacl, windows.security.PAcl)[0] # def source(self): (tok.TokenSource) ?? @property def type(self): + """The type (Primary / Impersonation) of the token + + + """ return self.TokenType.value @property def impersonation_level(self): - return self.TokenImpersonationLevel.value + """The impersonation level of a ``TokenImpersonation`` token. - statistics = TokenStatistics - restricted_sids = TokenRestrictedSids - session_id = TokenSessionId + :raises: :class:`WindowsError` if token is not a ``TokenImpersonation`` + :type: :class:`int` -- Enum value from :class:`~windows.generated_def.winstructs.SECURITY_IMPERSONATION_LEVEL` + """ + try: + return self.TokenImpersonationLevel.value + except WindowsError as e: + if (e.winerror == gdef.ERROR_INVALID_PARAMETER and + self.type != gdef.TokenImpersonation): + # raise ValueError ? + e.strerror += " This Token is not an Impersonation token" + raise + + statistics = TokenStatistics #: Alias for TokenStatistics (type may change in the future for improved struct) + + @property + def id(self): + """The TokenId Specifies an unique identifier that identifies this instance of the token object. + + :type: :class:`int` + """ + return int(self.TokenStatistics.TokenId) + + @property + def authentication_id(self): + """The AuthenticationId Specifies an unique identifier assigned to the session this token represents. + There can be many tokens representing a single logon session. + + :type: :class:`int` + """ + return int(self.TokenStatistics.AuthenticationId) + + @property + def modified_id(self): + """The ModifiedId Specifies an unique identifier that changes each time the token is modified. + + :type: :class:`int` + """ + return int(self.TokenStatistics.ModifiedId) + + restricted_sids = TokenRestrictedSids #: Alias for TokenRestrictedSids (type may change in the future for improved struct) + session_id = TokenSessionId #: Alias for TokenSessionId (type may change in the future for improved struct) @property def groups_and_privileges(self): + """Alias for TokenGroupsAndPrivileges (type may change in the future for improved struct)""" # Return enhanced 'TOKEN_GROUPS_AND_PRIVILEGES' ? - raise NotImplementedError("Token.groups_and_privileges") + return self.TokenGroupsAndPrivileges - sandbox_inert = TokenSandBoxInert + @property + def privileges(self): + """Alias for ``TokenPrivileges`` - # audit_policy + :type: :class:`TokenPrivileges` + """ + return self.TokenPrivileges + + sandbox_inert = TokenSandBoxInert #: Alias for TokenSandBoxInert (type may change in the future for improved struct) + + # def audit_policy(self): + # raise NotImplementedError("Need to find the type of TokenAuditPolicy") @property def origin(self): - # Make a enhanced LUID ? + """The originating logon session of the token. + + :type: :class:`int` + """ origin_logon_session = self.TokenOrigin.OriginatingLogonSession - return gdef.ULONG64.from_buffer(origin_logon_session).value + return int(origin_logon_session) # improved LUID implem __int__ :) @property def elevation_type(self): + """The elevation type of the token. + + :type: :class:`int` -- Enum value from :class:`~windows.generated_def.winstructs.TOKEN_ELEVATION_TYPE` + """ return self.TokenElevationType.value @property def linked_token(self): + """The token linked to our token if present (may raise else) + + :type: :class:`Token` + """ + # TODO: return None if not present ? return Token(self.TokenLinkedToken.LinkedToken) @property - def elevation(self): + def elevated(self): + """``True`` if token is an elevated token""" return bool(self.TokenElevation.TokenIsElevated) - is_elevated = elevation # Keep this old name ? - has_restriction = TokenHasRestrictions + is_elevated = elevated #: Alias for ``elevated`` deprecated and may disapear + has_restriction = TokenHasRestrictions #: Alias for TokenHasRestrictions (type may change in the future for improved struct) @property def access_information(self): + """Alias for TokenAccessInformation (type may change in the future for improved struct)""" # Return enhanced subclass ? - raise NotImplementedError("Token.access_information") - # return self.TokenAccessInformation + return self.TokenAccessInformation - virtualization_allowed = TokenVirtualizationAllowed - virtualization_enabled = TokenVirtualizationEnabled + @property + def trust_level(self): + """The trust level of the process if present else ``None``. + + :type: :class:`~windows.generated_def.winstructs.PSID` + """ + tl = self.TokenProcessTrustLevel + if not tl: # NULL: + return None + return tl + + virtualization_allowed = TokenVirtualizationAllowed #: Alias for TokenVirtualizationAllowed (type may change in the future for improved struct) + virtualization_enabled = TokenVirtualizationEnabled #: Alias for TokenVirtualizationEnabled (type may change in the future for improved struct) @property def integrity_level(self): - # Return SID_AND_ATTRIBUTES ? Only SID ? + """The integrity level and attributes of the token + + :type: :class:`windows.generated_def.winstructs.SID_AND_ATTRIBUTES` + """ return self.TokenIntegrityLevel.Label # SID_AND_ATTRIBUTES - ui_access = TokenUIAccess + def get_integrity(self): + """Return the integrity level of the token + + :type: :class:`int` + """ + sid = self.integrity_level.Sid + count = winproxy.GetSidSubAuthorityCount(sid) + integrity = winproxy.GetSidSubAuthority(sid, count[0] - 1)[0] + return KNOW_INTEGRITY_LEVEL[integrity] + + def set_integrity(self, integrity): + """Set the integrity level of a token + + :param type: :class:`int` + """ + mandatory_label = gdef.TOKEN_MANDATORY_LABEL() + mandatory_label.Label.Attributes = 0x60 + mandatory_label.Label.Sid = gdef.PSID.from_string("S-1-16-{0}".format(integrity)) + return self.set_informations(gdef.TokenIntegrityLevel, mandatory_label) + + _INTEGRITY_PROPERTY_DOC = """The integrity of the token as an int (extracted from integrity PSID) + + :getter: :func:`get_integrity` + :setter: :func:`set_integrity` + """ + + integrity = property(get_integrity, set_integrity, doc=_INTEGRITY_PROPERTY_DOC) + + ui_access = TokenUIAccess #: Alias for TokenUIAccess (type may change in the future for improved struct) VALID_TOKEN_POLICIES = gdef.FlagMapper( gdef.TOKEN_MANDATORY_POLICY_OFF, @@ -268,129 +487,128 @@ class Token(utils.AutoHandle): @property def mandatory_policy(self): + """mandatory integrity access policy for the associated token + + :type: :class:`int` -- see `[MSDN] mandatory policy `_ + """ return self.VALID_TOKEN_POLICIES[self.TokenMandatoryPolicy.Policy] @property def logon_sid(self): + """The logon sid of the token. (Case of multiple logon sid not handled and will raise AssertionError) + + :type: :class:`windows.generated_def.winstructs.SID_AND_ATTRIBUTES` + """ rgroups = self.TokenLogonSid assert rgroups.GroupCount == 1, "More than 1 TokenLogonSid" return rgroups.Groups[0] - is_appcontainer = TokenIsAppContainer - capabilities = TokenCapabilities + is_appcontainer = TokenIsAppContainer #: Alias for TokenIsAppContainer (type may change in the future for improved struct) + capabilities = TokenCapabilities #: Alias for TokenCapabilities (type may change in the future for improved struct) @property def appcontainer_sid(self): - return self.TokenAppContainerSid.TokenAppContainer + """The sid of the TokenAppContainerSid if present else ``None`` - appcontainer_number = TokenAppContainerNumber + :type: :class:`~windows.generated_def.winstructs.PSID` + """ + sid = self.TokenAppContainerSid.TokenAppContainer + if not sid: # NULL + return None + return sid - # def security_attribute: - # see PTOKEN_SECURITY_ATTRIBUTES_INFORMATION - # https://github.com/wj32/Backup/blob/59aa77379f9f7ca57f27265e796dd2fe4dae9fab/include/phnt/ntseapi.h - # blablabla :) + appcontainer_number = TokenAppContainerNumber #: Alias for TokenAppContainerNumber (type may change in the future for improved struct) + + @property + def security_attributes(self): + """The security attributes of the token + + :type: [:class:`TokenSecurityAttributeV1`] - A list of token security attributes + """ + return self.TokenSecurityAttributes.attributes - def duplicate(self, access_rigth=0, attributes=None, impersonation_level=gdef.SecurityImpersonation, toktype=gdef.TokenPrimary): + ## Token Methods + def duplicate(self, access_rigth=gdef.MAXIMUM_ALLOWED, attributes=None, type=None, impersonation_level=None): + """Duplicate the token into a new :class:`Token`. + + :param type: The type of token: ``TokenPrimary(0x1L)`` or ``TokenImpersonation(0x2L)`` + :param impersonation_level: The :class:`~windows.generated_def.winstructs.SECURITY_IMPERSONATION_LEVEL` for a ``TokenImpersonation(0x2L)``: + + - If ``type`` is ``TokenPrimary(0x1L)`` this parameter is ignored if ``None`` or used as-is. + - If ``type`` is ``TokenImpersonation(0x2L)`` and this parameter is None, ``self.impersonation_level`` is used. + - If ``type`` is ``TokenImpersonation(0x2L)`` and our Token is a ``TokenPrimary(0x1L)`` this parameter MUST be provided + + :returns: :class:`Token` - The duplicate token + + Example: + + >>> tok + + >>> tok.duplicate() + + >>> tok.duplicate(type=gdef.TokenImpersonation) + ... + ValueError: Duplicating a PrimaryToken as a TokenImpersonation require explicit parameter + >>> tok.duplicate(type=gdef.TokenImpersonation, impersonation_level=gdef.SecurityImpersonation) + + """ newtoken = gdef.HANDLE() - winproxy.DuplicateTokenEx(self.handle, access_rigth, attributes, impersonation_level, toktype, newtoken) - return type(self)(newtoken.value) + if type is None: + type = self.type + if impersonation_level is None: + if self.type == gdef.TokenImpersonation: + impersonation_level = self.impersonation_level + elif type != gdef.TokenImpersonation: + impersonation_level = 0 #: ignored + else: + raise ValueError("Duplicating a PrimaryToken as a TokenImpersonation require explicit parameter") + winproxy.DuplicateTokenEx(self.handle, access_rigth, attributes, impersonation_level, type, newtoken) + return bltn_type(self)(newtoken.value) + def adjust_privileges(self, privileges): + """Adjust the token privileges according to ``privileges``. + This API is the `complex one` to adjust multiple privileges at once. + To simply enable one privilege see :func:`enable_privilege`. - ### OLD CODE + :param privileges: :class:`~windows.generated_def.winstructs.TOKEN_PRIVILEGES` (or subclass as :class:`TokenPrivileges`). To easily update your token privileges use the result of :data:`privileges`. - def get_integrity(self): - """Return the integrity level of a token + Example: - :type: :class:`int` - """ - buffer_size = self.get_required_information_size(gdef.TokenIntegrityLevel) - buffer = ctypes.c_buffer(buffer_size) - self.get_informations(gdef.TokenIntegrityLevel, buffer) - sid = ctypes.cast(buffer, ctypes.POINTER(gdef.TOKEN_MANDATORY_LABEL))[0].Label.Sid - count = winproxy.GetSidSubAuthorityCount(sid) - integrity = winproxy.GetSidSubAuthority(sid, count[0] - 1)[0] - return know_integrity_level_mapper[integrity] + >>> tok = windows.current_process.token + >>> privs = tok.privileges + >>> privs["SeShutdownPrivilege"] = gdef.SE_PRIVILEGE_ENABLED + >>> privs["SeUndockPrivilege"] = gdef.SE_PRIVILEGE_ENABLED + >>> tok.adjust_privileges(privs) - def set_integrity(self, integrity): - """Set the integrity level of a token + """ + buffsize = None + if isinstance(privileges, TokenPrivilegesType): + # The TokenPrivilegesType should come from a PTR via Improved buffer + try: + buffsize = privileges._b_base_.real_size + except AttributeError as e: + pass + if buffsize is None: + buffsize = ctypes.sizeof(privileges) + winproxy.AdjustTokenPrivileges(self.handle, False, privileges, buffsize, None, None) + if winproxy.GetLastError() == gdef.ERROR_NOT_ALL_ASSIGNED: + raise ValueError("Failed to adjust all privileges") - :param type: :class:`int` - """ - mandatory_label = gdef.TOKEN_MANDATORY_LABEL() - mandatory_label.Label.Attributes = 0x60 - mandatory_label.Label.Sid = gdef.PSID.from_string("S-1-16-{0}".format(integrity)) - self.set_informations(gdef.TokenIntegrityLevel, mandatory_label) - - integrity = property(get_integrity, set_integrity) - - @property - def is_elevated(self): - """``True`` if process is Admin""" - elevation = gdef.TOKEN_ELEVATION() - self.get_informations(gdef.TokenElevation, elevation) - return bool(elevation.TokenIsElevated) - - @property - def token_user(self): - buffer_size = self.get_required_information_size(gdef.TokenUser) - buffer = ctypes.c_buffer(buffer_size) - self.get_informations(gdef.TokenUser, buffer) - return ctypes.cast(buffer, ctypes.POINTER(gdef.TOKEN_USER))[0] - - @property - def computername(self): - """The computername of the token""" - return self._user_and_computer_name()[1] - - @property - def username(self): - """The username of the token""" - return self._user_and_computer_name()[0] - - - - def _user_and_computer_name(self): - tok_usr = self.token_user - sid = tok_usr.User.Sid - usernamesize = gdef.DWORD(0x1000) - computernamesize = gdef.DWORD(0x1000) - username = ctypes.c_buffer(usernamesize.value) - computername = ctypes.c_buffer(computernamesize.value) - peUse = gdef.SID_NAME_USE() - winproxy.LookupAccountSidA(None, sid, username, usernamesize, computername, computernamesize, peUse) - return username[:usernamesize.value], computername[:computernamesize.value] - - def get_informations(self, info_type, data): - cbsize = gdef.DWORD() - winproxy.GetTokenInformation(self.handle, info_type, ctypes.byref(data), ctypes.sizeof(data), ctypes.byref(cbsize)) - return cbsize.value - - def get_required_information_size(self, info_type): - cbsize = gdef.DWORD() + def enable_privilege(self, name): + """Enable privilege ``name`` in the token""" + privs = self.privileges try: - winproxy.GetTokenInformation(self.handle, info_type, None, 0, ctypes.byref(cbsize)) - except WindowsError as e: - if not e.winerror == gdef.ERROR_INSUFFICIENT_BUFFER: - raise - return cbsize.value - - #TODO: TEST + DOC - def set_informations(self, info_type, infos): - return winproxy.SetTokenInformation(self.handle, info_type, ctypes.byref(infos), ctypes.sizeof(infos)) - - # TEST - def dacl(self): - """TEST CODE""" - buffer_size = self.get_required_information_size(TokenDefaultDacl) - buffer = ctypes.c_buffer(buffer_size) - self.get_informations(gdef.TokenDefaultDacl, buffer) - # TODO: use windows.security.Acl - return ctypes.cast(buffer, POINTER(TOKEN_DEFAULT_DACL))[0] - - + privs[name] = gdef.SE_PRIVILEGE_ENABLED + except KeyError as e: + raise ValueError("{0} as no privilege <{1}>".format(self, name)) + return self.adjust_privileges(privs) def __repr__(self): - tid_int = gdef.ULONG64.from_buffer(self.TokenStatistics.TokenId).value - return "<{0} TokenId={1:#x}>".format(type(self).__name__, tid_int) + flag_repr = gdef.Flag.__repr__ + tid_int = int(self.TokenStatistics.TokenId) + toktype = self.type + if toktype == gdef.TokenPrimary: + return "<{0} TokenId={1:#x} Type={2}>".format(type(self).__name__, tid_int, flag_repr(toktype)) + return "<{0} TokenId={1:#x} Type={2} ImpersonationLevel={3}>".format(type(self).__name__, tid_int, flag_repr(toktype), flag_repr(self.impersonation_level))