-
Notifications
You must be signed in to change notification settings - Fork 17
FinalPolicy Bypass
WinVerifyTrust does not decide trust at the first step. It walks through a short chain, then reaches one last decision gate named FinalPolicy. TrustMeBro swaps that last gate with a cleanup function that reports success, so the whole check ends in "valid."
The default Authenticode action uses seven stages:
- Initialization
- Message
- Signature
- Certificate
- CertCheck
- FinalPolicy
- Cleanup
SoftpubAuthenticode makes the real trust decision. SoftpubCleanup is the cleanup handler and returns S_OK.
The important registry value is $Function under the Authenticode action GUID:
- Key:
HKLM\SOFTWARE\Microsoft\Cryptography\Providers\Trust\FinalPolicy\{00AAC56B-CD44-11d0-8CC2-00C04FC295EE} - Before:
$Function = SoftpubAuthenticode - After:
$Function = SoftpubCleanup
Result:
- system-wide effect
- survives reboot
- every WinVerifyTrust check that reaches this action returns success
If you pair this with a stolen signature, UAC shows the donor certificate's Subject CN in the publisher field.
Testing all seven WinVerifyTrust stages with the same cleanup redirection showed one useful slot.
| Stage | Redirect result |
|---|---|
| Initialization | failed |
| Message |
0x800B0100, null signer |
| Signature | failed |
| Certificate | failed |
| CertCheck | failed |
| FinalPolicy | bypass succeeded |
| Cleanup | failed |
Only FinalPolicy redirected to SoftpubCleanup yields a trust bypass.
On Win11 24H2, wintrust.dll places the real Authenticode decision logic in SoftpubAuthenticode at 0x18001E0A0. That path checks hash match, EKU, revocation, and chain trust. SoftpubCleanup at 0x180022670 frees state and returns S_OK. One function decides trust. The other tears down state.
FinalPolicy redirection works across all six registered Authenticode action GUIDs, not only {00AAC56B-CD44-11d0-8CC2-00C04FC295EE}. The custom-provider feature relies on that wider coverage.
WintrustCertificateTrust at 0x18002D850 checks EKU 1.3.6.1.4.1.311.10.3.13, Lifetime Signing, and treats it as a separate allow path. This logic sits apart from FinalPolicy.
| EKU set | Result |
|---|---|
| codeSigning | pass |
| no EKU | pass |
| anyExtendedKeyUsage | pass |
| serverAuth | fail 0x800B0110
|