Skip to content

Verify installer Authenticode signatures before running elevated#5

Merged
rodchristiansen merged 2 commits into
mainfrom
feat/package-signature-verification
Jun 16, 2026
Merged

Verify installer Authenticode signatures before running elevated#5
rodchristiansen merged 2 commits into
mainfrom
feat/package-signature-verification

Conversation

@rodchristiansen

Copy link
Copy Markdown
Contributor

Problem

InstallPackage handed any downloaded MSI/EXE straight to msiexec.exe / direct execution — elevated, with no provenance check. The download only proves where the bytes came from (the configured URL), not who produced them. If the manifest or its host/CDN is compromised, an attacker-supplied installer runs as an elevated/SYSTEM process on every freshly provisioned PC. This is the Windows counterpart of the highest-severity audit finding.

Fix

Add SignatureVerifier (in BootstrapMate.Core) and gate InstallPackage on it for msi/exe items:

  • WinVerifyTrust confirms the embedded Authenticode signature is present, intact, and chains to a trusted root.
  • The signer certificate's common name/subject is matched against an optional expected publisher.
  • Unsigned/untrusted installers are refused unless AllowUnsigned is set (then permitted with a warning).
  • A publisher mismatch is never bypassed, even with AllowUnsigned.

Only msi/exe are gated (PE/MSI files Authenticode can validate); nupkg/pkg/ps1 keep their existing handling. Defaults are secure: verification on, no unsigned allowed.

Configuration

Intune CSP / Group Policy via the bundled ADMX (new Security category): VerifyPackageSignatures, ExpectedPublisher, AllowUnsigned. Also readable from HKLM\SOFTWARE\BootstrapMate\Settings (machine) / HKCU (GUI), and overridable per manifest item (expectedPublisher, allowUnsigned).

Mirrors the macOS SignatureVerifier (pkgutil --check-signature) for cross-platform parity.

Testing

⚠️ dotnet is not available on the author's machine, so this was not built locally — relying on the repo CI (windows-latest, dotnet 10) to compile-verify. Please confirm the CI build is green before merging. The WinVerifyTrust P/Invoke and signer-cert extraction follow the standard Win32 pattern; logic mirrors the already-tested macOS implementation.

Notes

  • No CLI flags added on Windows (unlike macOS): the Windows arg-parsing path is policy/registry-centric for Autopilot/ESP, and config flows through Intune/GPO/registry/manifest. Can add CLI flags as a follow-up if wanted.
  • Part of the cross-platform signature-verification work; the macOS half is bootstrapmate-macintosh#3.

DownloadAndInstallPackage handed any downloaded MSI/EXE straight to
msiexec/the executable, running it elevated with no provenance check. The
download only proves where the bytes came from, not who produced them: if
the manifest or its host is compromised, an attacker-supplied installer
runs as an elevated/SYSTEM process.

Add SignatureVerifier (WinVerifyTrust + signer-certificate extraction) and
gate InstallPackage on it for msi/exe items: the file must carry a
signature that chains to a trusted root and, when configured, match an
expected publisher. Unsigned/untrusted installers are refused unless
AllowUnsigned is set; a publisher mismatch is never bypassed.

Configurable via Intune CSP / Group Policy (new ADMX Security category:
VerifyPackageSignatures, ExpectedPublisher, AllowUnsigned), the
machine/user registry, and per-item manifest overrides (expectedPublisher,
allowUnsigned). Defaults are secure. Mirrors the macOS SignatureVerifier.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an Authenticode provenance gate for Windows binary installers so MSI/EXE downloads are signature-verified (and optionally publisher-matched) before being executed elevated, reducing the risk of running attacker-supplied installers if a manifest/CDN is compromised.

Changes:

  • Introduces SignatureVerifier using WinVerifyTrust + signer certificate inspection and a policy decision helper (AllowUnsigned, publisher mismatch always denied).
  • Wires signature verification into the Windows install path for msi/exe, with per-item manifest overrides (expectedPublisher, allowUnsigned).
  • Extends config/policy/ADMX(+ADML) + documentation to expose VerifyPackageSignatures, ExpectedPublisher, and AllowUnsigned.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/BootstrapMate.Core/SignatureVerifier.cs New WinVerifyTrust-based Authenticode verifier and policy decision logic.
src/BootstrapMate.Core/PolicyDetector.cs Adds policy aliases for the new signature-verification settings.
src/BootstrapMate.Core/ConfigManager.cs Loads/saves the new security settings from policy/registry and CLI override plumbing.
src/BootstrapMate.Core/BootstrapMateConfig.cs Adds config fields + secure defaults for signature verification.
resources/en-US/BootstrapMate.adml Adds localized strings/presentation for Security policies.
resources/BootstrapMate.admx Adds new Security category and policies for signature verification configuration.
README.md Documents signature verification behavior and relevant policy/registry keys.
Program.cs Gates MSI/EXE installs on signature verification and supports per-item overrides.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

new("00AAC56B-CD44-11d0-8CC2-00C04FC295EE");

private const uint WTD_UI_NONE = 2;
private const uint WTD_REVOKE_NONE = 0;
pPolicyCallbackData = IntPtr.Zero,
pSIPClientData = IntPtr.Zero,
dwUIChoice = WTD_UI_NONE,
fdwRevocationChecks = WTD_REVOKE_NONE,
Comment thread Program.cs Outdated
… choice

- Use ToLowerInvariant() for the package-type switch to avoid
  locale-dependent comparison.
- Add a comment explaining why WinVerifyTrust revocation checking is left
  off (OOBE/ESP often runs without reliable networking); the trusted-root
  chain is still verified, and the knob to enable WHOLECHAIN is noted.
@rodchristiansen rodchristiansen merged commit 81e567e into main Jun 16, 2026
2 checks passed
@rodchristiansen rodchristiansen deleted the feat/package-signature-verification branch June 16, 2026 01:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants