MSI Error “The computer must be trusted for delegation” caused by KB2918614

烈酒焚心 提交于 2019-12-02 22:25:09

From what I understand,

With KB2918614, MS have apparently tried to fix something in the Windows Installer Service.

New stuff:

  • They are creating a file by name "SourceHash{PRODUCT-GUID}" under %windir%\Windows\Installer. This is done for every product installed on the machine(with KB2918614 already installed).
  • SECREPAIR- They are computing 'Stored Hash Value' and 'Current Hash' for a given MSI.

Error:

  1. And, in this comparison, for some reason, these mismatch! (Found these in the MSI verbose logs).

  2. Once this fails, it looks for Machine policy value 'AlwaysInstallElevated' User policy value 'AlwaysInstallElevated'

  3. Now, if you are running a silent install "qn", this error is thrown: MSI_LUA: Elevation prompt disabled for silent installs.

  4. Removing silent install cmdline option for msiexec- ex., "qr" or "qb", will throw a UAC prompt. (which most likely will not be the expected behavior).

Additional Info:

My MSI is ivkoded through a bootstrapper exe. But, it doesn't really matter. Even a manual call to msiexec through cmd line behaves the same way.

Any inputs/solutions, anyone?

This is the word from the MS Enterprise Support folks.

Apparently they are not aware of any fix to this. At-least as of now. All they are saying that this KB is to fix a security loophole. I don't understand what kind of a security fix this is- one that allows a Fresh Install without an UAC prompt, but throws an UAC prompt only for the Upgrade.

Workaround 1: Distributing hash.

Capture the Hash file* in one machine and distribute them to other machines. Hash files are created under “%windir%\installer” directory. The naming convention is as follows: “SourceHash * This file is created only when a Product is installed with KB2918614 installed on the machine.This directory is hidden. Open cmd prompt using 'run as administrator'. Traverse to this path and open the folder using "explorer ." command. [I couldn't solve the issue using this approach- may be because accessing this directory requires administrator privileges which the Windows Installer itself might not have]

Workaround 2: Whitelisting.

Only if you trust the application that it is always digitally signed and doesn't contain anything malicious(even in the future).

Step 1: Enable Whitelisting

Under Key “HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer”, create a DWORD: “SecureRepairPolicy” and set its Value to 2.

Step 2: Add the application to the whitelist

Create a new key “SecureRepairWhitelist” under "HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer” and create StringValues with the product codes(Including flower brackets {}) of the product.

... Sadly though, both these workarounds need admin privileges!

Here is my automatic way of utilizing the registry whitelist work around mentioned on Microsoft's site.

Now, before I run my install command against a remote machine, I look at the MSI and extract the product code with Get-ProductCodeFromMSI, and then use Add-GuidToWhitelist to add each GUID to the list on that computer. Here's an example:

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

Before doing that, each machine can be tested and repaired for the workaround using Test-SecureRepairPolicy and Repair-SecureRepairPolicy, respectively.

Get-ProductCodeFromMSI will require the DLL referenced to be placed somewhere and unblocked - this DLL can be retrieved from the Wix toolset.

Code for the functions I reference is here:

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'")
}

I also encounter the issue. I got powershell script to install MSI on remote machines (using Invoke-Command cmdlet and provide the credential along with the script), but suddenly it failed to install MSI which I suppose also from this security patch.
After run the MSI installation file manually on target machine using the domain account (from remote desktop), suddenly the powershell script can run the MSI installation using domain account, but still failed to install if I used target machine local admin account.
I prefer to add this as a comment, but I don't have enough rep to do that. If the other has any solution or explanation for this I'd love to know that too. Thanks.

This is to be to do with the SourceHash{product-code} files under \windows\installer directory. This file can be opened with Orca you can read the contents. It contains a file name, hash algorithm specifier, and hash. On Windows 2k3 this hash is a base64ed sha256 hash with the last byte changed.

If you rename the SourceHash file for your product out of the way, I found the upgrade worked and after that a new SourceHash file is generated. You can then diff the two source hash files. In the case I am investigating, when you diff the two files only the hash listed for the original msi is different. After a successful upgrade the hash of the new msi listed in the source hash file will match that of installation source. The broken sourcehash file was obviously generated from a modified/different source MSI, although I have as yet been unable to identify which.

I have the same problem. MSIs failed to be installed with Invoke-Command PoSH. I've found that if I install any MSI on the server locally under the same account which is used for Invoke-Command the issue is fixed and Invoke-Command starts to work as usual.

Response from Microsoft: This security update resolves a privately disclosed vulnerability in Microsoft Windows. The vulnerability could allow elevation of privilege if an attacker runs a specially crafted application that attempts to repair a previously-installed application. An attacker must have valid logon credentials and be able to log on locally to exploit this vulnerability.

Workaround if you have problems with repairing application:

  1. Uninstall the application and reinstall it with the security update installed. (sourcehash file generated with security update)

  2. Manually copy the sourcehash file to c:\windows\installer folder. As the sourcehash file is generated based on the application files, the sourcehash file generated on computer A can be used on computer B.

  3. http://happysccm.com/kb2918614-uac-gate/ - commands to uninstall it.

I had this as well on different servers. After a few hours of digging I discovered that they could not reach the domain controllers. Check your DNS settings and ensure they can reach the AD. After fixing this this error disappeared.

If you are executing through psexec, just adding the -s argument also resolves the error. Then it is ran as the remote system user and UAC is not required.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!