由KB2918614引起的MSI错误“计算机必须受信任以进行委派”

11
我们有一个基于MSI的安装程序,最近在Windows 2008 R2环境中停止工作。该安装程序使用\\servername\c$\管理UNC共享将文件复制到目标计算机,然后通过WMI Win32_Process class上的create方法进行远程执行。现在,远程执行会在事件查看器中显示以下错误消息:
引用:

源自MsiInstaller的事件ID 10837的说明找不到。引发此事件的组件未安装在您的本地计算机上,或者安装已损坏。您可以在本地计算机上安装或修复组件。

如果事件源自另一台计算机,则必须保存事件的显示信息。

事件附带了以下信息:

Product:OUR PRODUCT NAME - 请求的操作无法完成。计算机必须为委派信任,并且当前用户帐户必须配置为允许委派。

经过搜索,似乎这是由于最近发布的Windows Installer安全补丁引起的。当我卸载KB2918614时,安装程序就开始工作了,而如果我重新安装KB2918614,MSI又会停止工作。
错误消息指示为解决此问题,我们需要域管理员使用Active Directory Users and Computers编辑目标计算机以允许委派,但是该MSI未使用任何远程资源,因此我不明白为什么需要这样做。同样的MSI和远程执行过程在Windows Server 2012上运行良好,因此我想知道这是否与2008 R2的补丁有关。
是否有其他方法可以解决这个错误消息?

更新:这似乎不是WMI远程执行的问题,因为当我们尝试使用Powershell、WinRM和Invoke-Command -ComputerName TargetComputer ...命令远程安装MSI时,也会出现此问题。在安装了KB2918614之后,Windows Installer在2008 R2上的工作方式发生了变化,导致自定义操作无法完成任务。


我建议尝试允许委派,至少作为一个实验。我怀疑问题不在于MSI访问远程资源,而是与安装的各个部分中的模拟(或非模拟)有关。这只是一个猜测,但是在安装过程中,安装程序会在用户帐户和系统帐户之间移动,也许在域环境中会发生某些需要将委派传递到或从域控制器传递的情况,类似于这样的情况。 - PhilDW
你实际上正在使用远程资源,文件来自另一台机器。Windows 知道这一点,并将该信息存储在备用的 NTFS 数据流中。请在 superuser.com 上提出有关此问题的问题。 - Hans Passant
我们设计了MSI,并希望找到一种方法来重写它,以解决我们客户面临的问题。我检查了文件是否被阻止,但在文件属性中并没有出现“此文件来自另一台计算机”的安全提示或解除阻止选项。 - Greg Bray
3
我们在应用此Microsoft更新后,远程执行MSI时遇到了问题。今天我与一位微软代表交谈,他表示这是一个已知的问题,正在进行修复。他告诉我他们希望在10月中旬到11月初之间推出一个额外的补丁程序来修复这个问题。 - ksun
有任何进展吗?我想知道我们是否在2012 R2服务器上遇到了类似的问题。 - Robin
1
我今天跟 Microsoft 进行了跟进,他们告诉我在这里找到的修复方法应该可以解决问题:http://support2.microsoft.com/kb/3000988。然而,我看到下面有一个回答被踩了。有人应用过这个补丁并且没有成功吗? - ksun
9个回答

5
据我所知,通过KB2918614更新,微软显然试图修复Windows Installer Service中的某些问题。
新内容:
- 他们正在%windir%\Windows\Installer下创建名为“SourceHash{PRODUCT-GUID}”的文件。对于机器上安装的每个产品(已安装了KB2918614),都会执行此操作。 - SECREPAIR-他们正在计算给定MSI的“存储哈希值”和“当前哈希”。
错误:
- 在此比较中,由于某种原因,这些哈希值不匹配!(在MSI详细日志中发现这些)。 - 一旦出现错误,它会查找 机器策略值“AlwaysInstallElevated” 用户策略值“AlwaysInstallElevated” - 如果您正在运行静默安装“qn”,则会抛出此错误:MSI_LUA:禁用静默安装的提升提示。 - 对于msiexec的静默安装cmdline选项进行删除,例如“qr”或“qb”,将引发UAC提示。(这很可能不是预期的行为)。
附加信息:
我的MSI是通过启动程序exe进行编码的。但是,这并不重要。即使通过cmd线手动调用msiexec也会以相同的方式运行。
有任何意见/解决方案吗?

3

以下是我使用注册表白名单绕过微软网站上提到的自动化方法。

现在,在对远程计算机运行我的安装命令之前,我查看MSI并使用Get-ProductCodeFromMSI提取产品代码,然后使用Add-GuidToWhitelist将每个GUID添加到该计算机的列表中。 以下是一个例子:

$guids = Get-ChildItem -Path D:\somefolder -filter "*.msi" -recurse | % {Get-ProductCodefromMSI $_.FullName}
Add-GUIDtoWhiteList -computername "SomeServer" -GUIDs $guids

在执行此操作之前,可以使用Test-SecureRepairPolicy和Repair-SecureRepairPolicy分别测试和修复每台机器的解决方法。
Get-ProductCodeFromMSI需要引用的DLL必须放置在某个地方并解除阻止 - 可以从Wix工具集中检索此DLL。
我引用的函数代码在这里:
Function Test-SecureRepairPolicy{
    param (
        [Parameter(mandatory=$true,ValueFromPipelineByPropertyName=$true)][string[]]
        # Specifies the computer name to connect to
        $ComputerName
    )

    Process {
        foreach ($Computer in $ComputerName)
        {
            #Open Remote Base
            $reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$Computer)
            #Get Windows key
            $subkey = $reg.OpenSubKey("SOFTWARE\Policies\Microsoft\Windows")
            $subkeynames = $subkey.GetSubKeyNames()
            if (($subkeynames | Measure-Object).Count -lt 1){
                return New-Object -type PSObject -Property @{
                    Success = $False
                    Note = "Can not open base key"
                    ComputerName = $Computer
                }
            }
            if ($subkeynames -notcontains "Installer"){
                return New-Object -type PSObject -Property @{
                    Success = $False
                    Note = "Can not locate installer subkey"
                }
            }
            $subkey.Close();$subkey = $null
            $subkey = $reg.OpenSubKey("SOFTWARE\Policies\Microsoft\Windows\Installer")
            $subkeynames = $subkey.GetSubKeyNames()
            if ($subkeynames -notcontains "SecureRepairWhitelist"){
                return New-Object -type PSObject -Property @{
                    Success = $False
                    Note = "Can not locate repairlist subkey"
                    ComputerName = $Computer
                }
            }
            $repairvalue = $subkey.GetValue("SecureRepairPolicy")
            if ($repairvalue -ne 2){
                return New-Object -type PSObject -Property @{
                    Success = $False
                    Note = "SecureRepairPolicy is incorrect"
                    ComputerName = $Computer
                }
            }
            $subkey.Close();$subkey = $null;$reg.Close();$reg = $null
            return New-Object -type PSObject -Property @{
                Success = $True
                Note = "SecureRepairPolicy structure is in place"
                ComputerName = $Computer
            } 
        }
    }
}

Function Repair-SecureRepairPolicy{
    param (
        [Parameter(mandatory=$true,ValueFromPipelineByPropertyName=$true)][string[]]
        # Specifies the computer name to connect to
        $ComputerName

    )
    Begin{
        Function Add-RemoteRegistryKey($Computer,$Parent,$Name){
            $reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$Computer)
            $subkey = $reg.OpenSubKey($Parent, $true)
            $subkey.CreateSubKey($Name)
            $subkey.Close();$subkey = $null;$reg.Close();$reg = $null
        }
        Function Add-InstallerKey($Computer){
           Add-RemoteRegistryKey $Computer "SOFTWARE\Policies\Microsoft\Windows" "Installer" 
        }
        Function Add-RepairPolicy($Computer){
            $reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$Computer)
            $subkey = $reg.OpenSubKey("SOFTWARE\Policies\Microsoft\Windows\Installer", $true)
            $subkey.SetValue("SecureRepairPolicy",2, "DWORD")
            $subkey.Close();$subkey = $null;$reg.Close();$reg = $null
        }
        Function Add-WhitelistKey($Computer){
            Add-RemoteRegistryKey $Computer "SOFTWARE\Policies\Microsoft\Windows\Installer" "SecureRepairWhitelist"
        }

    }
    Process {
        foreach ($Computer in $ComputerName)
        {
            $audit = Test-SecureRepairPolicy $computer
            if ($audit.Success){
                Write-Output "Repair whitelist keys setup.  No repair being performed."
            }
            else{
                Write-Output "Repair whitelist keys not setup.  Attempting to resolve"
                 if ($audit.Note -match "Can not open base key"){
                    Write-Error "Unable to open computer's registry key"
                    return
                 }
                 if ($audit.Note -match "Can not locate installer subkey"){
                    Add-Installerkey $Computer
                    Add-RepairPolicy $Computer
                    Add-WhitelistKey $Computer
                 }
                 if ($audit.Note -match "Can not locate repairlist subkey"){
                    Add-RepairPolicy $Computer
                    Add-WhitelistKey $Computer
                 }
                 if ($audit.Note -match "Can not locate repairlist subkey"){
                    Add-RepairPolicy $Computer
                 }
                 Write-Output "Showing new audit"
                 Test-SecureRepairPolicy $computer
            }
        }
    }
}

Function Add-GUIDtoWhiteList{
    param (
        [Parameter(mandatory=$true,ValueFromPipelineByPropertyName=$true)][string[]]
        # Specifies the computer name to connect to
        $ComputerName,
        [Parameter(mandatory=$true)][string[]]
        # Specifies the GUID(s) to add.
        $GUIDs
    )

    Process {
        foreach ($Computer in $ComputerName)
        {
            #Open Remote Base
            $reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$Computer)
            $subkey = $reg.OpenSubKey("SOFTWARE\Policies\Microsoft\Windows\Installer\SecureRepairWhitelist", $true)
            foreach($GUID in $GUIDs){
                $subkey.SetValue($GUID,"", "String")
            }
            $subkey.Close();$subkey = $null;$reg.Close();$reg = $null
        }
    }
}

Function Get-ProductCodefromMSI ($msi){
    [Reflection.Assembly]::LoadFrom("D:\scripts\lib\Microsoft.Deployment.WindowsInstaller.dll") | out-null
    (New-Object -TypeName Microsoft.Deployment.WindowsInstaller.Database -ArgumentList $msi).ExecuteQuery("SELECT Value FROM Property WHERE Property = 'ProductCode'")
}

3
这是来自MS Enterprise Support人员的消息。
显然,他们不知道如何解决这个问题。至少目前还没有。 他们所说的是,这个KB是为了修复一个安全漏洞。 我不明白这是什么样的安全修复 - 一种允许进行新安装而无需UAC提示,但仅在升级时引发UAC提示的安全修复。
解决方法1:分发哈希值。
在一台机器上捕获哈希文件*,并将其分发到其他机器上。 哈希文件创建在“%windir%\installer”目录下。命名约定如下:“SourceHash *只有在安装了KB2918614的机器上安装产品时才会创建此文件。此目录被隐藏。使用“以管理员身份运行”的方式打开cmd提示符。遍历到此路径并使用“explorer .”命令打开文件夹。 [我无法通过此方法解决问题-可能是因为访问此目录需要管理员权限,而Windows Installer本身可能没有这些权限]
解决方法2:白名单。
只有您信任该应用程序始终经过数字签名且不包含任何恶意内容(即使在未来)时才可以使用。
步骤1:启用白名单
在键“HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer”下创建DWORD:“SecureRepairPolicy”,并将其值设置为2。
步骤2:将应用程序添加到白名单中
在“HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer”下创建一个新键“SecureRepairWhitelist”,并使用产品代码(包括花括号{})创建字符串值。
...不幸的是,这两个解决方法都需要管理员权限!

谢谢您,DebugBreak先生!这真的让我有些兴奋。显然补丁正在进行修订,所以我们(希望)不必再去修改这些内容了。 - cjones26
欢迎。对于开发人员来说情况相当糟糕。终端用户可以想办法解决问题。 一个建议:如果可行的话,当遇到升级场景时,请卸载该产品并进行全新安装。 - DebugBreak
我尝试分发哈希,但是MSI正在删除它并尝试重新创建它,失败发生在重新创建期间。我也无法使白名单工作。安装程序密钥丢失,然后我尝试使用psexec -s,那样就可以了。白名单被列为msdn上的解决方法:https://support.microsoft.com/en-us/kb/2918614 - curlyhairedgenius

1

我有同样的问题。使用Invoke-Command PoSH安装MSIs失败。我发现,如果在服务器上使用与Invoke-Command相同的帐户本地安装任何MSI,则可以解决此问题,并且Invoke-Command将像往常一样工作。


1
我也遇到了这个问题。我使用PowerShell脚本在远程机器上安装MSI(使用Invoke-Command命令和提供脚本的凭据),但突然安装MSI失败,我认为也是由于此安全补丁引起的。
在使用域帐户(从远程桌面)手动运行目标机器上的MSI安装文件后,突然间,PowerShell脚本可以使用域帐户运行MSI安装,但如果我使用目标机器本地管理员帐户,则仍然无法安装。
我更喜欢将此添加为评论,但我没有足够的声望来做到这一点。如果其他人有任何解决方案或对此有解释,我也很想知道。谢谢。

1

这涉及到 \windows\installer 目录下的 SourceHash{product-code} 文件。此文件可使用 Orca 打开并查看其中的内容。它包含文件名、哈希算法说明符和哈希值。在 Windows 2k3 上,该哈希是一个 base64 编码的 sha256 哈希,最后一个字节被更改。

如果您将产品的 SourceHash 文件重命名,则可以解决升级问题。然后会生成一个新的 SourceHash 文件。您可以比较这两个源哈希文件。在我调查的情况下,当您比较这两个文件时,只有原始 msi 的哈希不同。成功升级后,源哈希文件中新 msi 的哈希将与安装源的哈希匹配。破损的 sourcehash 文件显然是从修改/不同的源 MSI 生成的,尽管我目前还无法确定是哪个。


0

微软的回应:

此安全更新解决了 Microsoft Windows 中的一个私下披露的漏洞。如果攻击者运行一个试图修复先前安装的应用程序的特制应用程序,则该漏洞可能会导致权限提升。攻击者必须拥有有效的登录凭据并能够本地登录才能利用此漏洞。

如果您在修复应用程序时遇到问题,请尝试以下解决方法:

  1. 使用已安装安全更新的版本卸载应用程序,然后重新安装它(使用安全更新生成的 sourcehash 文件)。

  2. 手动将 sourcehash 文件复制到 c:\windows\installer 文件夹中。由于 sourcehash 文件是基于应用程序文件生成的,因此在计算机 A 上生成的 sourcehash 文件可以在计算机 B 上使用。

  3. http://happysccm.com/kb2918614-uac-gate/ - 卸载命令。


1
请在答案中包含所需的命令。允许链接到其他信息,但不披露直接链接给其他方是不受欢迎的。但请确保每个答案本身都是完全自包含的。 - EWit

0

如果您正在通过psexec执行,只需添加-s参数也可以解决错误。然后它将作为远程系统用户运行,不需要UAC。


0

我在不同的服务器上也遇到了这个问题。经过几个小时的挖掘,我发现它们无法访问域控制器。检查您的DNS设置并确保它们可以访问AD。修复此问题后,该错误消失了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接