暂时冒充并启用权限?

时间:2010-03-17 16:48:26

标签: c windows security

我们维护一个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;
}

首先它尝试作为调用者。如果在访问被拒绝的情况下失败,它会暂时启用调用者令牌中的权限并再次尝试。如果失败并且拒绝访问且调用者冒充,则会暂时无人值守并再次尝试。如果失败并拒绝访问,则会临时启用进程令牌中的权限并再次尝试。我认为这应该处理几乎任何情况,但我想知道是否有更好的方法来实现这一目标?我们可能希望使用此方法进行大量操作(即几乎任何访问安全对象的操作)。

3 个答案:

答案 0 :(得分:1)

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

如果你真的需要能够访问所有内容,我会说启用这些权限并执行应该成功的扫描操作,无论呼叫者的安全性是否未被模仿。

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

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

答案 1 :(得分:0)

对于DLL而言,这一切看起来都非常时髦!这听起来像是服务而不是DLL的工作,或者,如果您要允许用户帐户删除特权帐户放置的数据,那么为什么不简单地设置ACL允许删除操作的对象?

话虽如此,你究竟想做什么?用户帐户通常不能删除管理员帐户放在那里的数据!

答案 2 :(得分:0)

您可以执行此操作的唯一方法是作为服务运行,并在服务中模拟用户。服务必须被授予模仿权限,默认情况下授予所有服务帐户。当你模仿调用者时,你可能不得不模仿委托,这样你就可以轻松地离开机器了。