Repository navigation
Darling install tree: verify shipped binaries after the lock, and narrow the service account's Modify to pg-runtime (#4038 review residuals) #4043
Description
Activity
Disposition (backtrack of #3898-#4043, 2026-09-23): work order 4 of 6, tier 1 residual (the #4038 review's Medium residual plus a Low); needs one design call. Recommendation: (1) after the lock, verify the Authenticode signature of our own signed binaries on release builds, and only warn on an unsigned dev or nightly build; third-party and pg-runtime binaries stay covered by extracting fresh (README). (2) Narrow the service account's Modify to pg-runtime only after auditing every write the service makes in the tree, with a real-service install test, since a wrong narrowing stops collection. #4038 already locks before anything runs, so both are hardening, not an open hole.
- added a commit that references this issue
on Sep 23, 2026 Revised recommendation for part 1. The earlier one (Authenticode checks after the lock) is withdrawn: it doesn't close the window it targets.
- A local user who can replace the service exe or a DLL between extraction and step 1b2 can just as easily replace
install-darling.ps1, and the admin then runs it elevated. - A signature check run BY that script only catches an attacker who leaves the script alone. The same goes for a hash manifest the script verifies.
- The trust root is where the admin extracts the zip, not anything inside the tree.
Recommended instead:
- Check the tree's ACL first, before the lock. At the very start, before step 1b2, the script inspects the tree's ACL. It looks for any Allow ACE granting write, append, delete, change-permissions or take-ownership to a principal beyond SYSTEM, Administrators, TrustedInstaller and the installing admin. The typical case is
Authenticated Users: Modifyinherited fromC:\. If it finds one:- it names the principals;
- it explains that files may have been replaced before the lock;
- it refuses unless
-AcceptWritableExtractionis passed. That switch exists for dev loops and says in its help that it accepts the risk. - It points to
C:\Program Files\<product>, or a folder only the admin can write, as the place to extract. - This fires on every risky install, attack or not, so it fixes the habit that creates the window. It doesn't depend on an attacker having left the script untouched.
- Docs: the README and install doc recommend extracting under
C:\Program Files, which non-admins can't write, and say why. - Upgrade path: unchanged. It already verifies a
-Sourcezip's SHA-256 and locks before the overlay. - Part 2 is unchanged: narrow the service account's Modify to pg-runtime only after auditing every write the service makes in the tree. It's verified with a real-service install on a test box, never on a monitored or dev-in-use host.
- A local user who can replace the service exe or a DLL between extraction and step 1b2 can just as easily replace
- addedin-progressActively being worked by a local session or its agents (PR open or in flight)Actively being worked by a local session or its agents (PR open or in flight)
on Sep 23, 2026 - added a commit that references this issue
on Sep 23, 2026 - added 7 commits that reference this issue
on Sep 23, 2026 Fixed by #4050, merged to dev at 19:01Z. It ships with the next release.
- removedin-progressActively being worked by a local session or its agents (PR open or in flight)Actively being worked by a local session or its agents (PR open or in flight)
on Sep 23, 2026 - added a commit that references this issue
on Sep 23, 2026
Residuals from the round-1 security review of #4038 (#4034, locking the install folder). #4038 closes WRITE access, and now locks before anything runs from the tree, both on install and before an upgrade's overlay. It also normalizes the logon account and reports junctions. Two things a lock can't do remain, and each needs a design call.
1. A file swapped before the lock survives it (Medium residual)
Between extracting the zip (the folder inherits
Authenticated Users: ModifyfromC:\) andinstall-darling.ps1step 1b2, a local user can replace the service exe, a DLL, or a pg-runtime binary. The lock stops FURTHER writes; it never checks CONTENTS, so a planted binary loads as the service account on first start. #4038 narrows the window to extract-then-run and documents extracting into a fresh folder and running the script straight away. The window itself remains.Options:
-Sourcezip's SHA-256.2. The service account holds Modify on the whole tree (Low, defence in depth)
Only
pg-runtimeandpg-runtime-prevextraction needs the service to write into the tree; the store, logs and log-hash key live under ProgramData. A compromised service or postgres process could rewrite the service exe for persistence.Fix: grant the service read and execute on the root, and Modify only on pre-created
pg-runtimeandpg-runtime-prevfolders.Risk: if the service writes anywhere else in the tree, it breaks at start, which is tier-1 breakage. Every write site needs auditing first, then a test on a real service install.