当前位置:编程学习 > 网站相关 >>

编程实现遍历ACL访问控制列表检查进程访问权限

Author:Pnig0s[FreeBuf]

阅读本文的朋友需要对Windows访问控制模型有初步的了解,了解Token(访问令牌),ACL(访问控制列表),DACL(选择访问控制列表),ACE(访问控制列表项)等与访问控制模型相关的名词含义及之间的关系,当然我也会在文中简要科普一下ACM。

写这篇文章的目的主要是最近在写一个Win下本地提权的东西,涉及到了对ACL的操作,以前对ACL总是避而远之,Windows访问控制模型比较头疼,一个API会牵出一大把要用的API。毕竟涉及到用户访问的安全,肯定不能让编程人员随意更改这些机制,复杂一些也可以理解,可是能参考的资料很少,MSDN上关于一些访问控制相关API的使用和结构体的描述也含糊不清也没有什么代码实例。这篇文章也是在查阅国外了一些文献加上自己研究测试后完成的,发出来希望对涉及这方面编程的朋友有帮助。

—>>熟悉Windows访问控制机制的可以跳过本段:

因为是科普我这里简单介绍下Windows访问控制模型(ACM),别嫌我啰嗦,懂得直接Pass往下看。ACM中最重要的两部分是访问令牌(Access Token)和安全描述符表(Security Descriptor)。访问令牌存在于访问主体中,安全描述符表存在于访问客体中。比如我去米国,我就是访问主体,米国就是访问客体,我持有的签证就是访问令牌。系统中访问主体是进程客体是一切系统对象。访问令牌中有当前用户的唯一标识SID,组唯一标识SID以及一些权限标志(Privilege)。安全描述符表(SD)存在于Windows系统中的任何对象中(文件,注册表,互斥量,信号量等等)。SD中包含对象所有者的SID,组SID以及两个非常重要的数据结构选择访问控制列表(DACL)和系统访问控制列表(SACL),其中SACL涉及系统日志用的很少可以先无视。DACL中包含一个个ACE访问控制入口也是权限访问判断的核心,当一个进程访问某一对象的时候,对象会将进程的Token与自身的ACE依次比对,直到被允许或被拒绝,前面的ACE优于后面的ACE。整体的一个权限检查过程如下图:

—>>

\

上面简单介绍了本文要用到的也是Windows访问控制模型核心部分的一些知识,下面来介绍下如何编程实现遍历ACL来进行访问权限的检查。本文主要针对文件对象进行介绍,其他类型的对象大同小异。要用到的两个主要API为GetFileSecurity()和AccessCheck()。GetFileSecurity能够获取指定文件的安全描述符表,而AccessCheck可以指定要检查的权限,该函数能够将获得的安全描述符表与当前进程的Token进行检查来判断进程对该文件对象是否允许相应的权限。


不过这两个API并不那么容易用,因为其中要涉及到安全描述符表和访问令牌的获取,因此又牵扯出一大把API也涉及一些访问控制的知识。下面依次介绍要使用到的API然后给出整体的代码。GetFileSecurity的函数原型如下:
BOOL WINAPI GetFileSecurity(
 
__in LPCTSTR lpFileName,
 
__in SECURITY_INFORMATION RequestedInformation,
 
__out_opt PSECURITY_DESCRIPTOR pSecurityDescriptor,
 
__in DWORD nLength,
 
__out LPDWORD lpnLengthNeeded
 
);
lpFileName指定了要获取SD的文件。首先要定义一个PSECURITY_DESCRIPTOR的安全描述符表指针,因为描述符表大小未知,所以要调用两次GetFileSecurity()第一次将nLength置0,函数会返回实际大小,然后第二次用获取的大小去接收完整的SD,代码如下:文件开始部分定义的内存分配释放函数常量:
#define AllocMem(x) (HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,x))
#define FreeMem(x) (HeapFree(GetProcessHeap(),HEAP_ZERO_MEMORY,x))
.......
BOOL bRs = FALSE;
DWORD dwSizeNeeded = 0;
PSECURITY_DESCRIPTOR psd = NULL;
SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION|DACL_SECURITY_INFORMATION;
bRs = GetFileSecurity(lpFileName,si,psd,0,&dwSizeNeeded);
//第一次调用获得SD实际大小
if(!bRs)
{
if(GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
psd = (PSECURITY_DESCRIPTOR)AllocMem(dwSizeNeeded);
//根据获取到的大小对psd分配内存
}else
{
printf("\n[-]Get SD failed:%d",GetLastError());
return bRs;
}
}
if(!GetFileSecurity(lpFileName,si,psd,dwSizeNeeded,&dwSizeNeeded))
{
printf("\n[-]Get SD failed:%d",GetLastError());
return bRs;
}

 

至此针对指定文件对象的安全描述符表已经得到,下一步需要提取出访问进程的访问令牌(Token)。首先调用OpenProcessToken()获得本进程的Token,参数比较简单参考MSDN吧。然后有个比较重要的内容:我们需要模拟获得的令牌,因为OpenProcessToken获得的是进程的初始Token,不能直接用于访问权限的判断,我们要调用DuplicateToken()以当前用户的身份模拟一个同样的Token出来,具体使用待会儿看代码吧。下面到了会让人比较困惑的地方:就是GENERIC_MAPPING这个结构体,这个开始看MSDN一直一头雾水,没理解到底怎么使用,MSDN上也没有代码实例。鼓捣了一上午最后发现其实很简单。比如我们使用CreateFile()创建一个文件的时候可以指定一些权限访问的标志如GENERIC_WRITE,GENERIC_READ等等。但是这些权限标志都是通用的标志,还可以用这些标志来创建或打开其他类型的对象。在表示文件对象的时候,这些通用标志所包含的实际文件对象特有的权限标志列表如下:

\

CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,