【问题标题】:Temporarily impersonate and enable privileges?临时模拟并启用权限?
【发布时间】:2010-03-24 15:08:15
【问题描述】:

我们维护一个 DLL,它可以做很多与系统相关的事情;遍历文件系统、注册表等。此 DLL 的调用者可能使用模拟,也可能不使用模拟。为了更好地支持所有可能的场景,我正在尝试将其修改为更智能。我将使用删除文件的示例。目前我们只调用 DeleteFile(),如果失败,那就结束了。我想出了以下几点:

BOOL TryReallyHardToDeleteFile(LPCTSTR lpFileName)
{
    // 1. caller without privilege
    BOOL bSuccess = DeleteFile(lpFileName);
    DWORD dwError = GetLastError();
    if(!bSuccess && dwError == ERROR_ACCESS_DENIED)
    {
        // failed with access denied; try with privilege
        DWORD dwOldRestorePrivilege = 0;
        BOOL bHasRestorePrivilege = SetPrivilege(SE_RESTORE_NAME, SE_PRIVILEGE_ENABLED, &dwOldRestorePrivilege);
        if(bHasRestorePrivilege)
        {
            // 2. caller with privilege
            bSuccess = DeleteFile(lpFileName);
            dwError = GetLastError();
            SetPrivilege(SE_RESTORE_NAME, dwOldRestorePrivilege, NULL);
        }
        if(!bSuccess && dwError == ERROR_ACCESS_DENIED)
        {
            // failed with access denied; if caller is impersonating then try as process
            HANDLE hToken = NULL;
            if(OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken))
            {
                if(RevertToSelf())
                {
                    // 3. process without privilege
                    bSuccess = DeleteFile(lpFileName);
                    dwError = GetLastError();
                    if(!bSuccess && dwError == ERROR_ACCESS_DENIED)
                    {
                        // failed with access denied; try with privilege
                        bHasRestorePrivilege = SetPrivilege(SE_RESTORE_NAME, SE_PRIVILEGE_ENABLED, &dwOldRestorePrivilege);
                        if(bHasRestorePrivilege)
                        {
                            // 4. process with privilege
                            bSuccess = DeleteFile(lpFileName);
                            dwError = GetLastError();
                            SetPrivilege(SE_RESTORE_NAME, dwOldRestorePrivilege, NULL);
                        }
                    }
                    SetThreadToken(NULL, hToken);
                }
                CloseHandle(hToken);
                hToken = NULL;
            }
        }
    }
    if(!bSuccess)
    {
        SetLastError(dwError);
    }
    return bSuccess;
}

所以首先它尝试作为调用者。如果访问被拒绝而失败,它会暂时启用调用者令牌中的权限并重试。如果访问被拒绝而失败并且调用者正在模拟,它会暂时取消模拟并重试。如果访问被拒绝而失败,它会暂时启用进程令牌中的权限并重试。我认为这应该可以处理几乎任何情况,但我想知道是否有更好的方法来实现这一点?有很多我们可能想要使用此方法的操作(即几乎所有访问安全对象的操作)。

【问题讨论】:

  • 为什么不直接跳到最后一个选项呢?没有异常处理,如果我正确阅读了您的解释,保证可以工作。
  • 这不处理进程作为 LocalSystem(即服务)运行并且我们需要读取网络位置上的文件(即只有模拟的用户帐户可以访问文件)的情况.
  • @Chris:这也违背了最小特权的心态。您应该只需要该操作所需的权限,仅此而已。这是做事更难的方式,但更“理想”的方式。
  • @Ioan - 我没有想到这一点。我想这取决于早期尝试失败的可能性有多大。
  • 你错过了只读属性标志:)

标签: c windows security


【解决方案1】:

备份和恢复权限一起提供对所有文件的完全访问权限,句号。这些对 LocalSystem 可用。您必须使用 FILE_FLAG_BACKUP_SEMANTICS 打开文件才能使用它。一些 Win32 API 不是为此而设计的,不会将标志传递给内核,尽管在某些情况下,您可以使用 CreateFile 打开目录。 (对于内核来说,目录只是另一种文件)。

如果您确实需要能够访问所有内容,我会说启用这些权限并执行应该成功的扫描操作,而不管调用者的安全性如何。

一个突出的问题是文件可能被锁定或打开但不能共享访问。没有办法从用户模式解决这个问题(不杀死拥有资源的进程,可能是矫枉过正)。这就是为什么我所知道的主流扫描仪使用内核模式文件系统过滤驱动程序来实现此功能的原因。

另外,考虑审计:您希望审计条目显示给 LocalSystem 还是与调用进程关联的用户?

【讨论】:

  • 问题是我需要访问网络位置; LocalSystem 没有该访问权限。模拟还将授予我访问用户的加密文件的权限,LocalSystem 也无法访问(至少不能访问解密的内容)。
【解决方案2】:

您可以做到这一点的唯一方法是作为服务运行,并在服务内模拟用户。服务必须被授予模拟权限,默认情况下所有服务帐户都授予该权限。当您冒充呼叫者时,您可能必须冒充委托,以便您可以轻松离开机器。

【讨论】:

  • 是的,这就是我们服务的结构。但是,我无法控制第 3 方将如何使用我们的 DLL,因此我尝试以通用方式处理此问题。到目前为止,在我的测试中,它似乎运行良好,尽管如果模拟用户是标准用户,那么他们的令牌在其中没有任何有用的权限,这是一个浪费的步骤。实际上,我们需要模拟的唯一原因是访问网络路径;也许我可以在尝试访问本地路径时取消模拟,并在尝试访问网络路径时重新模拟。
【解决方案3】:

这一切对于 DLL 来说似乎真的很时髦!这听起来像是 service 而不是 DLL 的工作,或者,如果您要允许用户帐户删除特权帐户放在那里的数据,那么为什么不简单地设置 ACL允许删除操作的对象?

话虽如此,您实际上想做什么?用户帐户通常不能删除管理员帐户放置的数据!

【讨论】:

  • 我同意你所说的大部分内容,但这超出了我的控制范围。此 DLL 是一种在概念上类似于防病毒软件的扫描组件。在我们的产品中,它在 LocalSystem 上下文中作为服务运行。但是,用户可以指定要扫描的位置;这可能包括需要模拟访问的网络路径。所以一开始我们模拟用户然后运行扫描。如果用户还想扫描系统路径(可能他无权访问),我们需要临时取消模拟以访问它们。 DeleteFile() 只是一个例子;我可以改用 FindFirstFile()。
猜你喜欢
  • 2015-10-23
  • 2019-12-25
  • 2017-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多