Uncovering and Disclosing a Signature Spoofing Vulnerability in Windows Installer: CVE-2021-26413
Executive Summary
Okta Security has discovered and disclosed a new bypass in Windows Installer (MSI) Authenticode signature validation that could allow an attacker to disguise an altered package as legitimate software.
Background
Since at least 2018, malware authors have abused a quirk of Windows Installer signature validation to disguise malware as legitimate software. The quirk is that content appended to an MSI file doesn't invalidate its signature.
Historically this was exploited by appending a JAR (Java file) to an MSI file. Since the Java runtime reads JARs back to front, the malicious code would be executed while keeping the signature intact. Malware authors have exploited this bug to disguise malware like Ratty and Adwind as Microsoft binaries with valid signatures1.
Bernardo Quintero of VirusTotal disclosed this condition to Microsoft in 2018. Microsoft initially decided it did not require servicing2. By August 2020, Microsoft’s position had changed and the issue was patched as CVE-2020-14643. Microsoft acknowledged that the technique was under active exploitation by malware authors.
Matt Graeber found a similar bug in PE Authenticode signatures that was exploited through the lax parsing of HTA files by mshta.exe. This issue was patched as CVE-2020-15994 in November 2020.
We disclosed a new variant to Microsoft on December 4th, 2020, and the fix (which can be found here) was released on April 13th, 2021.
Issue
To validate the Authenticode signature of a Windows Installer file, the operating system or user invokes the WinVerifyTrust or WinVerifyTrustEx functions. These functions first identify the type of file and then load its subject interface package (SIP). In the case of MSI, the SIP is MSISIP.dll. The registry is then queried for the name of the DLL exports that retrieve and verify the signature data. For MSI files, these are MsiSIPGetSignedDataMsg and MsiSIPVerifyIndirectData, respectively. To learn more, consult Matt Graeber’s whitepaper, Subverting Trust in Windows [pdf]5.
MSI files conform to the Compound File Binary format, as described in [MS-CFB]6. Succinctly, files of this format represent filesystems with storage objects serving as directories and stream objects serving as files. MsiSIPGetSignedDataMsg will retrieve the signature data from the “\x05DigitalSignature'' stream, and MsiSIPVerifyIndirectData will walk the root directory storage object to compute and verify that each digest matches what is contained within.
The exploitable condition we have uncovered arises when MSISIP.dll fails to take into account unallocated bytes, explained in section 4.3 of [MS-CFB]7 as:
Usually, a compound file includes ranges of bytes that are not allocated for either CFB structures or user-defined data. For instance, each stream whose length is not an exact multiple of the sector size requires a trailing portion of the last sector in the stream's sector chain to be unused. Implementations that fail to initialize these byte ranges to zero (as recommended in section 2.7) might unintentionally leak user data.
These unallocated bytes are not factored in when computing and verifying the signature. This allows an attacker to insert malicious data into these ranges and keep the signature intact.
Proof of Concept
The primary restriction to exploitation is that these ranges are rather limited in size. However, Matt Graeber’s trick with HTA files and their lax parsing works to deliver a small payload:
Source: Okta
Impact
Malware authors have used similar bugs to disguise malicious software as legitimate software, hoping to assuage fears and entice a user into executing it. During an evaluation of Authenticode for Windows Installer, we discovered this issue and found that it had a further consequence: enabling an attacker to bypass a security check and escalate privileges.
To elaborate: our privileged Windows service would verify that a provided Windows Installer file had a valid Okta signature through a call to WinVerifyTrust. If it did, the file was executed. This vulnerability allowed a low privilege attacker to provide an MSI to the Windows service, bypass the signature checks and execute their malicious code as SYSTEM.
Defense in depth
In the case of applications that rely solely on WinVerifyTrust, as ours did, we recommend adding additional checks such as a full file checksum, file size, and file extension, for starters. Now that Microsoft is actively servicing this type of vulnerability, we anticipate more variants to come to light. This is due to the complexity of Authenticode signatures and the increased attention towards this bug class from both a defensive analysis perspective and from an offensive perspective by malware authors.
Patch Analysis
The fix was implemented in MSISIP!IsSupportedFileType:
This function is called from MsiSIPIsMyTypeOfFile which is called to determine if the MSI SIP supports the provided file. If the file is supported, you will see the “Digital Signatures” tab in the file properties and the aforementioned exports of the SIP will be invoked to extract and verify the signature.
The patched function, IsSupportedFileType, is provided the absolute path to the file8 as its sole argument. It will extract the file extension from the path and compare it to “.jar” (part of the fix for CVE-2020-1464) as well as compare it to “.hta”, which is the fix for this bug (CVE-2021-26413). If the extension matches, MsiSIPIsMyTypeOfFile ultimately returns FALSE and the file is not handled by this SIP. In other words, for the purposes of Authenticode, it is not recognized as a Windows Installer file:
Note the missing “Digital Signatures” tab.
Final thoughts
Recent supply chain attacks have raised awareness about the importance of code signing for the purposes of verifying integrity and authenticity. However, as we’ve demonstrated, code signing is not a panacea. Issues in signing technologies like Authenticode allow attackers to abuse the trust placed in code signing. To protect your users and applications against future issues, we recommend a layered approach that includes additional verification mechanisms.
References
[2] https://blog.virustotal.com/2019/01/distribution-of-malicious-jar-appended.html
[3] https://medium.com/@TalBeerySec/glueball-the-story-of-cve-2020-1464-50091a1f98bd
[4] https://twitter.com/mattifestation/status/1326228491302563846?lang=en
[5] https://specterops.io/assets/resources/SpecterOps_Subverting_Trust_in_Windows.pdf
[7] 4.3 Unallocated Ranges, https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cfb/965e60b4-0a45-4bc9-8a3d-56495a0187ca
[8] https://docs.microsoft.com/en-us/windows/win32/api/mssip/nc-mssip-pfnisfilesupportedname