Windows 文件系统授权的继承和传播

\0x1 简单的权限与传播继承

  • 运用于 NTFS 的权限继承与传播是通过安全描述符来实现的,安全描述符的作用是控制对对象的访问。以文件夹为例,可以把文件夹看作一种容器,容器可以包含子容器和文件,只要父容器的安全描述符发生更改,并且安全描述符是可以传播的,那么子容器和子文件就可能发生更改。
  • 创建一个父文件夹 father 和一个子文件夹 child,之后查询 child 安全描述符的传播和 DACLACE 的继承状态。
int main(int argc, char *argv[])
{
    
    
	SECURITY_INFORMATION requestedInfo = DACL_SECURITY_INFORMATION;
	PSECURITY_DESCRIPTOR pSecDes = NULL; DWORD secSize = 0; 
	CHAR filePath[MAX_PATH] = "C:\\Users\\Cxy\\Desktop\\father\\child";
	if (!GetFileSecurityA(filePath, requestedInfo, pSecDes, secSize, &secSize))
	{
    
    
		pSecDes = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, secSize);
		if (!GetFileSecurityA(filePath, requestedInfo, pSecDes, secSize, &secSize))
			cout << GetLastError() << endl;
	}
	
	SECURITY_DESCRIPTOR_CONTROL pControl; DWORD revision;
	if (GetSecurityDescriptorControl(pSecDes, &pControl, &revision))
	{
    
    
		if ((pControl & SE_DACL_PROTECTED) != SE_DACL_PROTECTED)
			cout << "安全描述符是否可以传播: 可以传播" << endl;
	}

	BOOL daclPresent; BOOL daclDefaulted; PACL lpACL = NULL;
	if (!GetSecurityDescriptorDacl(pSecDes, &daclPresent, &lpACL, &daclDefaulted))
		cout << GetLastError() << endl;

	ACL_SIZE_INFORMATION aclInfo; DWORD aclInfoSize = sizeof(ACL_SIZE_INFORMATION);
	ACE_HEADER *aceHeader = NULL; ACCESS_ALLOWED_ACE *accessAllowed = NULL; LPVOID unknown = NULL;
	if (GetAclInformation(lpACL, &aclInfo, aclInfoSize, AclSizeInformation))
	{
    
    
		for (int i = 0; i < aclInfo.AceCount; i++)
		{
    
    
			if (GetAce(lpACL, i, &unknown))
			{
    
    
				aceHeader = (ACE_HEADER *)unknown;
				if ((aceHeader->AceType == ACCESS_ALLOWED_ACE_TYPE) || (aceHeader->AceType == ACCESS_DENIED_ACE_TYPE))
				{
    
    
					accessAllowed = (ACCESS_ALLOWED_ACE *)unknown;
					cout << " ACE[" << i << "]: " << endl;
					cout << "  > 类型: " << (INT)accessAllowed ->Header.AceFlags << endl;
					cout << "  > SID 账户名: " << SIDToName(&accessAllowed->SidStart) << endl;
				}
			}
		}
	}
	return 0;
}

PSTR WINAPI SIDToName(PSID lpSID)
{
    
    
	LPSTR userName = NULL; LPSTR domainName = NULL; DWORD nameSize = 0; DWORD domainSize = 0; SID_NAME_USE peUse;
	if (!LookupAccountSidA(NULL, lpSID, userName, &nameSize, domainName, &domainSize, &peUse))
	{
    
    
		userName = (LPSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nameSize);
		domainName = (LPSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, domainSize);
		if (!LookupAccountSidA(NULL, lpSID, userName, &nameSize, domainName, &domainSize, &peUse))
			cout << GetLastError() << endl;
	}
	return userName;
}
  • 上面代码的执行结果如下所示,使用 GetSecurityDescriptorControl 函数获取关于继承的标志位,只要继承的标志位不含 SE_DACL_PROTECTED 就说明该安全描述符的 DACL 是可以传播的。使用 GetAce 来查看 ACE Header 中的类型,可以看出三个 ACE 的类型都是 3 也就是包含 CONTAINER_INHERIT_ACE 标志位,说明该 ACE 相对于子容器来说是可以继承。

// https://docs.microsoft.com/en-us/windows/win32/api/accctrl/ns-accctrl-explicit_access_a
安全描述符是否可以传播: 可以传播
ACE[0]:
类型: 3
SID 账户名: SYSTEM
ACE[1]:
类型: 3
SID 账户名: Administrators
ACE[2]:
类型: 3
SID 账户名: Cxy

  • 接下来创建两个新账户 cxy2cxy3,并且分别向 fatherchild 文件夹添加这两个用户
LPVOID WINAPI EasyAlloc(INT size);

int main(int argc, char *argv[])
{
    
    
	// 获取文件安全描述符
	SECURITY_INFORMATION requestInfor = DACL_SECURITY_INFORMATION; DWORD secSize = 0;
	CHAR filePath[MAX_PATH] = "C:\\Users\\Cxy\\Desktop\\father\\child"; PSECURITY_DESCRIPTOR pSecDes = NULL;
	if (!GetFileSecurityA(filePath, requestInfor, pSecDes, secSize, &secSize))
	{
    
    
		pSecDes = Alloc(secSize);
		if (!GetFileSecurityA(filePath, requestInfor, pSecDes, secSize, &secSize))
			cout << GetLastError() << endl;
	}

	// 转换安全描述符的格式
	SECURITY_DESCRIPTOR *pSecDesSelf = NULL; DWORD size = 0;
	PACL lpDACL = NULL; PACL lpSACL = NULL; PSID lpOwner = NULL; PSID lpPrimaryGroup = NULL;
	DWORD daclSize = 0; DWORD saclSize = 0; DWORD ownerSize = 0; DWORD primaryGroupSize = 0;
	if (!MakeAbsoluteSD(pSecDes, pSecDesSelf, &size, lpDACL, &daclSize, lpSACL, &saclSize, lpOwner, &ownerSize, lpPrimaryGroup, &primaryGroupSize))
	{
    
    
		pSecDesSelf = (SECURITY_DESCRIPTOR *)Alloc(size);
		lpDACL = (PACL)Alloc(daclSize); lpSACL = (PACL)Alloc(saclSize);
		lpOwner = (PSID)Alloc(ownerSize); lpPrimaryGroup = (PSID)Alloc(primaryGroupSize);
		if (!MakeAbsoluteSD(pSecDes, pSecDesSelf, &size, lpDACL, &daclSize, lpSACL, &saclSize, lpOwner, &ownerSize, lpPrimaryGroup, &primaryGroupSize))
			cout << GetLastError() << endl;
	}

	// 获取 cxy* 帐户名的 SID
	PSID sid = NULL; DWORD cbSid = 0; LPSTR domainName = NULL; DWORD domainNameSize = 0;
	CHAR account[MAX_PATH] = "cxy3"; SID_NAME_USE peUse;
	if (!LookupAccountNameA("", account, sid, &cbSid, domainName, &domainNameSize, &peUse))
	{
    
    
		sid = (PSID)Alloc(cbSid); domainName = (LPSTR)Alloc(domainNameSize);
		if (!LookupAccountNameA("", account, sid, &cbSid, domainName, &domainNameSize, &peUse))
			cout << GetLastError() << endl;
	}

	// 构建新的 DACL
	TRUSTEE_A trustee; EXPLICIT_ACCESS_A userAccess; PACL newDacl;
	BuildTrusteeWithSidA(&trustee, sid);
	userAccess.grfAccessPermissions = MY_READ_ATTRIBUTES; userAccess.grfAccessMode = SET_ACCESS;
	userAccess.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; userAccess.Trustee = trustee;
	if (ERROR_SUCCESS == SetEntriesInAclA(1, &userAccess, pSecDesSelf->Dacl, &newDacl))
	{
    
    
		pSecDesSelf->Dacl = newDacl;
	}
	else
		cout << GetLastError() << endl;
	
	// 重新设置文件对象的可传播的安全描述符, SetSecurityInfo 也可以将安全描述符设置为可传播
	if (ERROR_SUCCESS != SetNamedSecurityInfo(filePath, SE_FILE_OBJECT, requestInfor, pSecDesSelf->Owner, pSecDesSelf->Group, pSecDesSelf->Dacl, pSecDesSelf->Sacl))
		cout << GetLastError() << endl;
	return 0;
}

LPVOID WINAPI EasyAlloc(INT size)
{
    
    
	return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
}

在这里插入图片描述

这里附上高级权限的掩码
#define MY_CONTROL_FULLY (0x1F01FF) // 00000000 00001111 00000001 11111111 完全控制
#define MY_READ_DATA (0x1) // 00000000 00000000 00000000 00000001 列出文件夹/读取数据
#define MY_WRITE_DATA (0x2) // 00000000 00000000 00000000 00000010 创建文件/写入数据
#define MY_ADDITIONAL_DATA (0x4) // 00000000 00000000 00000000 00000100 创建文件夹/附加数据
#define MY_READ_EXTENDED_DATA (0x8) // 00000000 00000000 00000000 00001000 读取扩展数据
#define MY_WRITE_EXTENDED_DATA (0x10) // 00000000 00000000 00000000 00010000 写入扩展属性
#define MY_EXECUTABLE_FILE (0x20) // 00000000 00000000 00000000 00100000 遍历文件夹/执行文件
#define MY_READ_ATTRIBUTES (0x80) // 00000000 00000000 00000000 10000000 读取属性
#define MY_WRITE_ATTRIBUTES (0x100) // 00000000 00000000 00000001 00000000 写入数据
#define MY_DELETE (0x10000) // 00000000 00000001 00000000 00000000 删除
#define MY_READ_PERMISSION (0x20000) // 00000000 00000010 00000000 00000000 读取权限
#define MY_CHANGE_PERMISSION (0x40000) // 00000000 00000100 00000000 00000000 更改权限
#define MY_TAKE_OWNERSHIP (0x80000) // 00000000 00001000 00000000 00000000 取得所有权

  • 最后在 child 文件夹下创建 test.txt 文件,可以看出它继承了 fatherchild 的安全描述符,这就是 NTFS 下安全描述符的基本继承和传播规则。
    在这里插入图片描述

\0x2 继承与传播

  • 通过 SetSecurityDescriptorControl 设置安全描述符拥有 SE_DACL_PROTECTED 位是阻止安全描述符进行传播的方法之一,但是并不影响子对象的继承,当子对象刚创建时会继承父对象的安全描述符,但是之后怎么更改父对象的安全描述符都与子对象无关,因为父对象禁用了传播。

\0x3 显示权限与继承权限

  • 显示权限一般是非继承权限,child 文件夹既拥有 father 继承下来的 ACE cxy2,也有自己显式创建的 ACE cxy3。那么显示权限是自己创建的当然可以更改,但是继承的权限可以更改吗?应该不能把,不然要继承干嘛,而且更改的时候也是灰色的,貌似不可以更改。那通过调用 WIN32 API 呢?先试一试能不能删除继承的 ACE cxy2
LPVOID WINAPI EasyAlloc(INT size);

int main(int argc, char *argv[])
{
    
    
	// 获取文件安全描述符
	SECURITY_INFORMATION requestInfor = DACL_SECURITY_INFORMATION; DWORD secSize = 0;
	CHAR filePath[MAX_PATH] = "C:\\Users\\Cxy\\Desktop\\father\\child"; PSECURITY_DESCRIPTOR pSecDes = NULL;
	if (!GetFileSecurityA(filePath, requestInfor, pSecDes, secSize, &secSize))
	{
    
    
		pSecDes = Alloc(secSize);
		if (!GetFileSecurityA(filePath, requestInfor, pSecDes, secSize, &secSize))
			cout << GetLastError() << endl;
	}

	// 转换安全描述符的格式
	SECURITY_DESCRIPTOR *pSecDesSelf = NULL; DWORD size = 0;
	PACL lpDACL = NULL; PACL lpSACL = NULL; PSID lpOwner = NULL; PSID lpPrimaryGroup = NULL;
	DWORD daclSize = 0; DWORD saclSize = 0; DWORD ownerSize = 0; DWORD primaryGroupSize = 0;
	if (!MakeAbsoluteSD(pSecDes, pSecDesSelf, &size, lpDACL, &daclSize, lpSACL, &saclSize, lpOwner, &ownerSize, lpPrimaryGroup, &primaryGroupSize))
	{
    
    
		pSecDesSelf = (SECURITY_DESCRIPTOR *)Alloc(size);
		lpDACL = (PACL)Alloc(daclSize); lpSACL = (PACL)Alloc(saclSize);
		lpOwner = (PSID)Alloc(ownerSize); lpPrimaryGroup = (PSID)Alloc(primaryGroupSize);
		if (!MakeAbsoluteSD(pSecDes, pSecDesSelf, &size, lpDACL, &daclSize, lpSACL, &saclSize, lpOwner, &ownerSize, lpPrimaryGroup, &primaryGroupSize))
			cout << GetLastError() << endl;
	}
	
	if (!DeleteAce(pSecDesSelf->Dacl, 2))
		cout << GetLastError() << endl;
	
	// 重新设置文件对象的可传播的安全描述符
	if (ERROR_SUCCESS != SetNamedSecurityInfo(filePath, SE_FILE_OBJECT, requestInfor, pSecDesSelf->Owner, pSecDesSelf->Group, pSecDesSelf->Dacl, pSecDesSelf->Sacl))
		cout << GetLastError() << endl;
	return 0;
}

LPVOID WINAPI EasyAlloc(INT size)
{
    
    
	return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size);
}
  • 好像不行诶,不能删除继承的权限。除非使用 GetSecurityDescriptorControl() -> SE_DACL_PROTECTED 禁用 father 的继承才行,那么修改其中的权限呢?当前也是不行的。当新的安全描述符中的 DACL 喂给系统时首先忽略继承的 ACE,之后才可能会对显示 ACE 进行更改。如果非要更改继承 ACE,可以沿着继承树往上找到想更改的显示 ACE,就像 fatherACE cxy2,再由系统沿继承树传播下来,这样 child 继承的 ACE cxy2 也会得到更改。
    在这里插入图片描述

\0x4 ACL 中 ACE 的顺序

  • ACL 中显示 ACE 优先于继承 ACE,在显示和继承 ACL 中,拒绝的 ACE 优先于允许 ACE
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_38924942/article/details/112716360