Windows Access Token

  • security descriptor

 

A structure and associated data that contains the security information for a securable object. A security descriptor identifies the object's owner and primary group. It can also contain a DACL that controls access to the object, and a SACL that controls the logging of attempts to access the object.

 

  • securable object

A securable object is an object that can have a security descriptor. All named Windows objects are securable. Some unnamed objects, such as process and thread objects, can have security descriptors too.For most securable objects, you can specify an object's security descriptor in the function call that creates the object. For example, you can specify a security descriptor in the CreateFile and CreateProcess functions.

Each type of securable object defines its own set of specific access rights and its own mapping of generic access rights.

The following table shows the functions to use to manipulate the security information for some common securable objects.

参考:https://msdn.microsoft.com/en-us/library/windows/desktop/aa379557%28v=vs.85%29.aspx

  • security identifier

A security identifier (SID) is a unique value of variable length used to identify a trustee(A trustee is the user account, group account, or logon session to which an access control entry (ACE) applies). Each account has a unique SID issued by an authority, such as a Windows domain controller, and stored in a security database. Each time a user logs on, the system retrieves the SID for that user from the database and places it in the access token for that user. The system uses the SID in the access token to identify the user in all subsequent interactions with Windows security. When a SID has been used as the unique identifier for a user or group, it cannot ever be used again to identify another user or group.

Windows security uses SIDs in the following security elements:

  In security descriptors to identify the owner of an object and primary group

  In access control entries, to identify the trustee for whom access is allowed, denied, or audited

  In access tokens, to identify the user and the groups to which the user belongs

参考:https://msdn.microsoft.com/en-us/library/windows/desktop/aa379571%28v=vs.85%29.aspx

  • privileges

A privilege is the right of an account, such as a user or group account, to perform various system-related operations on the local computer, such as shutting down the system, loading device drivers, or changing the system time. Privileges differ from access rights in two ways:

  Privileges control access to system resources and system-related tasks, whereas access rights control access to securable objects.

  A system administrator assigns privileges to user and group accounts, whereas the system grants or denies access to a securable object based on the access rights granted in the ACEs in the object's DACL.

Each system has an account database that stores the privileges held by user and group accounts. When a user logs on, the system produces an access token that contains a list of the user's privileges, including those granted to the user or to groups to which the user belongs. Note that the privileges apply only to the local computer; a domain account can have different privileges on different computers.

When the user tries to perform a privileged operation, the system checks the user's access token to determine whether the user holds the necessary privileges, and if so, it checks whether the privileges are enabled. If the user fails these tests, the system does not perform the operation.

To determine the privileges held in an access token, call the GetTokenInformation function, which also indicates which privileges are enabled. Most privileges are disabled by default.

The Windows API defines a set of string constants, such as SE_ASSIGNPRIMARYTOKEN_NAME, to identify the various privileges. These constants are the same on all systems and are defined in Winnt.h. For a table of the privileges defined by Windows, see Privilege Constants. However, the functions that get and adjust the privileges in an access token use the LUID type to identify privileges. The LUID values for a privilege can differ from one computer to another, and from one boot to another on the same computer. To get the current LUID that corresponds to one of the string constants, use the LookupPrivilegeValue function. Use the LookupPrivilegeName function to convert a LUID to its corresponding string constant.

参考:https://msdn.microsoft.com/en-us/library/windows/desktop/aa379306%28v=vs.85%29.aspx

  • Access Token

An access token is an object that describes the security context of a process or thread. The information in a token includes the identity and privileges of the user account associated with the process or thread. When a user logs on, the system verifies the user's password by comparing it with information stored in a security database. If the password is authenticated, the system produces an access token. Every process executed on behalf of this user has a copy of this access token.

The system uses an access token to identify the user when a thread interacts with a securable object or tries to perform a system task that requires privileges. Access tokens contain the following information:

  The security identifier (SID) for the user's account

  SIDs for the groups of which the user is a member

  A logon SID that identifies the current logon session

  A list of the privileges held by either the user or the user's groups

  An owner SID

  The SID for the primary group

  The default DACL that the system uses when the user creates a securable object without specifying a security descriptor

  The source of the access token

  Whether the token is a primary or impersonation token

  An optional list of restricting SIDs

  Current impersonation levels

  Other statistics

Every process has a primary token that describes the security context of the user account associated with the process. By default, the system uses the primary token when a thread of the process interacts with a securable object. Moreover, a thread can impersonate a client account. Impersonation allows the thread to interact with securable objects using the client's security context. A thread that is impersonating a client has both a primary token and an impersonation token.

Every process has a primary token that describes the security context of the user account associated with the process. By default, the system uses the primary token when a thread of the process interacts with a securable object. Moreover, a thread can impersonate a client account. Impersonation allows the thread to interact with securable objects using the client's security context. A thread that is impersonating a client has both a primary token and an impersonation token.

Use the OpenProcessToken function to retrieve a handle to the primary token of a process. Use the OpenThreadToken function to retrieve a handle to the impersonation token of a thread.

参考:https://msdn.microsoft.com/en-us/library/windows/desktop/aa374909%28v=vs.85%29.aspx

有一篇微软的文章《What's in a token》:http://blogs.technet.com/b/askds/archive/2007/11/02/what-s-in-a-token.aspx

再来看看国内的几篇讲解:

  • 用户、认证和对象安全:

Windows系统具有很完善的安全和认证机制,称作访问控制机制。程序的执行主体(线程)在访问对象(文件、事件等)时,系统会根据线程的“权限”和线 程需要访问的对象所具有的访问控制列表(ACL)中的“安全描述符”是否匹配来进行认证,决定一个线程是否可以操作一个对象

1. 关于权限、访问控制列表、安全描述符等在安全认证中所依赖的数据结构,并重点讲解安全认证的过程

A需要访问(Access)B,A就是访问的主体,B就是访问的客体。A的“访问令牌”和B的安全描述符共同决定了A是否可以访问B.

访问的主体是进程。在系统中,线程才是程序执行的流程,因此只有线程才能操作对象。每个线程都是属于一个进程的,线程并没有属于自己的权限,而是来源于线 程所属于的进程。一个进程中的所有线程都具有同样的权限,可以把进程看作访问的主体。一个线程能访问哪些对象,能进行哪此操作,是由线程权限决定的。访问 的客体是安全对象,所有被访问的对象都具有安全描述符,包括了文件、注册表、事件(Event)、互斥(Mutex)、管道等。

1.1 访问令牌、权限和用户标识

     进程的权限继承自创建进程用户和用户所属的用户组。用户有专用数据结构来表示权限—访问令牌(Access Token)。访问令牌包括两个部分:一个是令牌所表示的用户,包括用户标识符(SID),用户所属的用户组等;另一部分是“权限” (Privilege)

  在进程访问安全对象时,会用到SID。每个安全对象都有访问控制列表(ACL),ACL说明了哪些用户( SID)能访问本对象,哪些不能,以及能进行哪种访问等。而“权限”在访问某个具体的安全对象时并没有作用,“权限”是表示进程是否能够进行特定的系统操 作,如关闭系统、修改系统时间、加载设备驱动等

  创建进程的API函数是CreateProcess,CreateProcess函数所创建的进程使用的访问令牌是当前登录用户的访问令牌。 此外还可以指定进程的用户。使用CreateProcessAsUser和CreateProcessWithTokenW等API函数,在创建前需要先 得到用户的令牌,可以使用LogonUser登录用户(是否可以同时登录多个用户受操作系统版本限制),LogonUser函数用返回用户的令牌。 如果需要得到进程和线程的访问令牌,可以使用OpenProcessToken、 OpenThreadToken等函数。获取令牌中的信息可以使用API函数GetTokenInformation。如果需要修改权限,可以使用 AdjustTokenPrivileges等函数。

1.2 进程的系统操作权限

    进程的权限特指进程是否能够进行各种系统操作,例如是否可以关闭系统,是否能够修改系统时间,是否能够加载设备驱动等。权限是一个列表,每种权限是列表中的一项。权限列表存在于进程的访问令牌中。权限有很多种,每一种表示了一个特定的操作是否能够进行,如果进程的访问令牌中的权限列表中有这个权限,则表示进程可以进行这种操作,比如SE_LOAD_DRIVER_ NAME表示进程可以加载驱动。

1.3 安全对象

Windows系统几乎所有的对象都有安全属性,包括文件、文件夹、注册表、线程同步对象、进程间通信对象、网络 共享等,进程和线程也可以是其他进程的操作对象,所以进程和线程也是安全对象。在创建对象时都可以指定对象的安全属性,比如CreateFile、 CreatePipe、CreateProcess、RegCreateKeyEx和RegSaveKeyEx 等,SECURITY_ATTRIBUTES结构用于指定对象的安全属性。GetNamedSecurityInfo、GetSecurityInfo、 SetSecurityInfo、 SetKernelObjectSecurity、SetNamedSecurityInfo等API函数可以获取和设置对象的安全属性。对象的安全属性 是以安全描述符(Security Descriptor)的形式存在的,安全描述符中包括了访问控制列表。

安全描述符结构:

一个安全描述符包含以下安全信息:

  • 两个安全标识符(Security identifiers),简称为SID,分别是OwnerSid和GroupSid. 所谓SID就是每次当我们创建一个用户或一个组的时候,系统会分配给改用户或组一个唯一SID,当你重新安装系统后,也会得到一个唯一的SID。SID是唯一的,不随用户的删除而分配到另外的用户使用。
    请记住,SID永远都是唯一的SIF是由计算机名、当前时间、当前用户态线程的CPU耗费时间的总和三个参数决定以保证它的唯一性。
      例:   S-1-5-21-1763234323-3212657521-1234321321-500
  • 一个DACL(Discretionary Access Control List),其指出了允许和拒绝某用户或用户组的存取控制列表。 当一个进程需要访问安全对象,系统就会检查DACL来决定进程的访问权。如果一个对象没有DACL,那么就是说这个对象是任何人都可以拥有完全的访问权限。
  • 一个SACL(System Access Control List),其指出了在该对象上的一组存取方式(如,读、写、运行等)的存取控制权限细节的列表。
  • 还有其自身的一些控制位。SECURITY_DESCRIPTOR_CONTROL

    DACL和SACL构成了整个存取控制列表Access Control List,简称ACL,ACL中的每一项,我们叫做ACE(Access Control Entry),ACL中的每一个ACE。

1.4 访问控制列表(ACL)

每个安全对象都有访问控制列表。其结构如下:

访问控制列表有两种,一种是选择访问控制列表(discretionary access control list,DACL),另一种是系统访问控制列表(system access controllist,SACL)。DACL决定了用户或用户组是否能访问这个对象,SACL控制了尝试访问安全对象的检测信息的继承关系。DACL是访问控制的关键,DACL中包括一个访问控制入口(AccessControl Entries,ACE)列表。ACE表明了用户(通过用户SID或用户组SID)是否能进行操作以及能进行哪种操作。在进行访问控制检测时,会依次检测 DACL中的ACE,直到被允许或被拒绝

 

2  安全机制程序示例

本节通过实例说明访问令牌和安全描述符访问控制列表等内容和程序设计的方法

列举进程访问令牌内容和权限;

修改进程的权限;

显示安全描述符的内容,列举DACL;

修改对象的安全描述符。

2.1 列举进程访问令牌内容和权限

#include <windows.h>
#include <iostream>
#include <WinBase.h>
#include <Sddl.h>

int GetUserSid()
{
    HANDLE hProcess = GetCurrentProcess();
    if (!hProcess) {
        return 0;
    }

    HANDLE hToken;
    if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken) || !hToken){
        CloseHandle(hProcess);
        return 0;
    }

    //User
    DWORD dwTemp = 0;
    char tagTokenInfoBuf[0x500] = { 0 };
    PTOKEN_USER tagTokenInfo = (PTOKEN_USER)tagTokenInfoBuf;
    if (!GetTokenInformation(hToken, TokenUser, tagTokenInfoBuf, sizeof(tagTokenInfoBuf), &dwTemp)) 
    {
        CloseHandle(hToken);
        CloseHandle(hProcess);
        return 0;
    }

    LPTSTR MySid;
    ConvertSidToStringSidA(tagTokenInfo->User.Sid, &MySid);//想要得到的值是LPTSTR类型,所以传递他的指针,即&MySid
    printf("User SID:%s\n", MySid);
    LocalFree((HLOCAL)MySid);

    //Owner
    memset(tagTokenInfoBuf, 0, 0x500);//
    PTOKEN_OWNER pOwner = (PTOKEN_OWNER)tagTokenInfoBuf;
    if (!GetTokenInformation(hToken, TokenOwner, tagTokenInfoBuf, sizeof(tagTokenInfoBuf), &dwTemp))
    {
        CloseHandle(hToken);
        CloseHandle(hProcess);
        return 0;
    }
    LPTSTR OwnerSid;
    ConvertSidToStringSidA(pOwner->Owner, &OwnerSid);
    printf("Owner SID:%s\n", OwnerSid);

    //PTOKEN_PRIVILEGES
    //Group  TokenGroups
    memset(tagTokenInfoBuf, 0, 0x500);
    PTOKEN_GROUPS pGroup = (PTOKEN_GROUPS)tagTokenInfoBuf;
    if (!GetTokenInformation(hToken, TokenGroups, tagTokenInfoBuf, sizeof(tagTokenInfoBuf), &dwTemp))
    {
        CloseHandle(hToken);
        CloseHandle(hProcess);
        return 0;
    }
    for (int i = 0; i < pGroup->GroupCount; i++)
    {
        LPTSTR GroupSid;
        ConvertSidToStringSidA(pGroup->Groups[i].Sid, &GroupSid);
        printf("Group-SID%d:%s\n", i, GroupSid);
    }

    CloseHandle(hToken);
    CloseHandle(hProcess);

    return 0;
}

int main()
{
    GetUserSid();
    system("pause");
    return 0;
}

运行就会输出结果,也可利用process explorer查看:

2.2修改进程的权限

在获得进程的权限之后,还可以对进程的权限进行修改,以使进程可以进行特定的系统操作。

LookupPrivilegeValue:The LookupPrivilegeValue function retrieves the locally unique identifier (LUID) used on a specified system to locally represent the specified privilege name.

AdjustTokenPrivileges:The AdjustTokenPrivileges function enables or disables privileges in the specified access token. Enabling or disabling privileges in an access token requires TOKEN_ADJUST_PRIVILEGES access.

#include <windows.h>
#include <stdio.h>
#include <winbase.h>

BOOL SetPrivilege(
    HANDLE hToken,          // access token handle
    LPCTSTR lpszPrivilege,  // name of privilege to enable/disable
    BOOL bEnablePrivilege   // to enable or disable privilege
    )
{
    TOKEN_PRIVILEGES tp;
    LUID luid;

    if (!LookupPrivilegeValue(//查找privilege对应的LUID
        NULL,            // lookup privilege on local system
        lpszPrivilege,   // privilege to lookup 
        &luid))        // receives LUID of privilege
    {
        //printf("LookupPrivilegeValue error: %u\n", GetLastError());
        return FALSE;
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if (bEnablePrivilege)
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.

    if (!AdjustTokenPrivileges(
        hToken,
        FALSE,
        &tp,
        sizeof(TOKEN_PRIVILEGES),
        (PTOKEN_PRIVILEGES)NULL,
        (PDWORD)NULL))
    {
        printf("AdjustTokenPrivileges error: %u\n", GetLastError());
        return FALSE;
    }

    if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
    {
        printf("The token does not have the specified privilege. \n");
        return FALSE;
    }

    return TRUE;
}

int main()
{
    HANDLE hToken;
    BOOL bEnablePrivilege = TRUE;
    OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
    SetPrivilege(hToken, "SeSecurityPrivilege", bEnablePrivilege);
    system("pause");
    return 0;
}

参考:https://msdn.microsoft.com/en-us/library/aa446619%28v=vs.85%29.aspx

2.3获取安全对象的安全描述符

GetFileSecurity:The GetFileSecurity function obtains specified information about the security of a file or directory. The information obtained is constrained by the caller's access rights and privileges.

SECURITY_DESCRIPTOR结构:The SECURITY_DESCRIPTOR structure contains the security information associated with an object. Applications use this structure to set and query an object's security status.

A security descriptor includes information that specifies the following components of an object's security:

相关的操作有很多:

可以利用函数GetSecurityDescriptorSacl获取访问控制列表。

参考:https://msdn.microsoft.com/en-us/library/aa379561%28v=vs.85%29.aspx

AccessCheck:The AccessCheck function determines whether a security descriptor grants a specified set of access rights to the client identified by an access token.

GENERIC_MAPPING structure:The GENERIC_MAPPING structure defines the mapping of generic access rights to specific and standard access rights for an object. When a client application requests generic access to an object, that request is mapped to the access rights defined in this structure.

#include <windows.h>
#include <iostream>
#include <Sddl.h>

BOOL AccessSecurityDescriptor()
{
    HANDLE hToken;
    DWORD dwSizeNeeded = 0x100;
    LPCTSTR lpPath = "D:\\Coding\\test\\HW\\HW\\Debug\\HW.exe";
    //SECURITY_DESCRIPTOR
    
    PISECURITY_DESCRIPTOR psd = NULL;
    psd = (PISECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSizeNeeded * 2);
    SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION
        | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
    //利用GetFileSecurity获取指定文件的安全描述符表
    if (GetFileSecurity(lpPath, si, psd, 0x100, &dwSizeNeeded))//获取Security Descriptor大小
    {
        
    }
    else
    {
        return FALSE;
    }
    if (GetFileSecurity(lpPath, si, psd, dwSizeNeeded, &dwSizeNeeded))
    {

    }
    else
    {
        return FALSE;
    }

    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken))
    {
        return FALSE;
    }

    HANDLE hImpersonatedToken = NULL;
    if (DuplicateToken(hToken,
        SecurityImpersonation, &hImpersonatedToken))
    {
        DWORD dwGenericAccessMask = GENERIC_READ | GENERIC_WRITE;
        GENERIC_MAPPING genMap;
        PRIVILEGE_SET privileges = { 0 };
        DWORD grantAccess = 0;
        DWORD privLength = sizeof(privileges);
        BOOL bGrantAccess = FALSE;
        //将通用权限控制标志和特定类型对象权限控制标志挂钩 
        genMap.GenericRead = FILE_GENERIC_READ;
        genMap.GenericWrite = FILE_GENERIC_WRITE;
        genMap.GenericExecute = FILE_GENERIC_EXECUTE;
        genMap.GenericAll = FILE_ALL_ACCESS;
        // Use the GENERIC_MAPPING structure to convert any 
        // generic access rights to object-specific access rights.
        //映射通用权限控制标志
        //即检查当前进程对某文件是否有读权限的时候,我们必须要调用MapGenericMask()把GENERIC_READ,GENERIC_WRITE,GENERIC_EXECUTE等等
        //这类通用权限控制标志映射成该类型的对象特有的权限控制标志,对于文件就是FILE_GENERIC_READ,
        //FILE_GENERIC_WRITE等等。
        MapGenericMask(&dwGenericAccessMask, &genMap);
         
        if (AccessCheck(psd, hImpersonatedToken,
            dwGenericAccessMask, &genMap, &privileges, &privLength, &grantAccess, &bGrantAccess))
        {
            //PISECURITY_DESCRIPTOR pSecurityDescriptor=NULL;
            SECURITY_DESCRIPTOR_CONTROL Control;
            DWORD dwRevision;
            if (GetSecurityDescriptorControl(psd, &Control, &dwRevision))
            {

            }
            else
            {

            }
            PSID pOwner=NULL;
            BOOL bOwnerDefaulted;
            if (GetSecurityDescriptorOwner(psd, &pOwner, &bOwnerDefaulted))
            {
                /*printf("Owner:%s\n", pOwner);*/
                LPTSTR OwnerSid;
                ConvertSidToStringSidA(pOwner, &OwnerSid);
                printf("Owner SID:%s\n", OwnerSid);
                
            }
            else
            {

            }
            return TRUE;
        }
        else
        {
            return FALSE;
        }
    }
}

int main()
{
    if (AccessSecurityDescriptor())
    {
        std::cout << "Access allow!" << std::endl;
    }
    else
    {
        std::cout << "Access denied!" << std::endl;
    }

    system("pause");
    return 0;
}

参考:《How AccessCheck Works》https://msdn.microsoft.com/en-us/library/windows/desktop/aa446683%28v=vs.85%29.aspx

http://pnig0s1992.blog.51cto.com/393390/908495/

2.4修改安全描述符

对不同对象的安全描述符的修改使用的API函数不同。如果是文件则使用SetFileSecurity。

其他参考:http://www.cnblogs.com/xiaojinma/archive/2012/12/07/2806584.html

http://www.tuicool.com/articles/V7reEb

http://www.cppblog.com/weiym/archive/2013/08/25/202751.html?opt=admin

 

posted @ 2016-01-29 18:16  _No.47  阅读(1190)  评论(0编辑  收藏  举报