Installer-Based Execution: PKG Internals, Scripts, and Receipts on Golden Gate
Module 1.2 directly ran a shell script, a .command file, and a binary. It
built the other formats but did not launch or install them. A pkg has a
different execution path: the installer places files, can run lifecycle
scripts, and can register a receipt. Its effective authority depends on the
installation domain and authorization granted, not on the .pkg extension.
This module opens the pkg you built in 1.2, walks its install lifecycle,
examines the postinstall script and the auth declaration. The user-home
install was run successfully on the macOS 27 validation VM while tester01
owned the active console. A login-window host may fail this target; check
the session rather than claiming the command works everywhere.
Build the Installer Package
This module reuses the Phase 01 pack. The Makefile's pkg target now bundles
a postinstall proof script alongside the payload:
Continue in the Phase 01 lab workspace prepared in module 1.1.
pkgbuild: Inferring bundle components from contents of build/pkgroot pkgbuild: Adding top-level postinstall script pkgbuild: Wrote package to build/msl-payload.pkg
The pkg is now sitting in
build/msl-payload.pkg, freshly built with a scripts directory inside.
PKG Internals: What Is Inside the File
A pkg is a xar archive. Module 1.2 showed file calling it exactly that.
Open it with the platform's own tool and see the structure:
/tmp/msl-14-expand/Bom /tmp/msl-14-expand/PackageInfo /tmp/msl-14-expand/Payload /tmp/msl-14-expand/Scripts/postinstall
Four files, each with a job. Payload holds the files to be installed.
Bom is the bill of materials describing payload files. It can support
receipt queries, but it is not an automatic uninstaller. PackageInfo
declares package identity, location, and scripts. Scripts/postinstall
is the script the installer would run after placing the payload.
Read the manifest, because it declares authority:
<?xml version="1.0" encoding="utf-8"?>
<pkg-info overwrite-permissions="true" relocatable="false" identifier="com.macseclabs.msl-payload" postinstall-action="none" version="1.0" format-version="2" generator-version="InstallCmds-883 (26A434)" install-location="/macseclabs-pkg-lab" auth="root">
<payload numberOfFiles="2" installKBytes="1"/>
<bundle-version/>
<upgrade-bundle/>
<update-bundle/>
<atomic-update-bundle/>
<strict-identifier/>
<relocate/>
<scripts>
<postinstall file="./postinstall" timeout="600"/>
</scripts>
</pkg-info>The auth="root" attribute declares an authorization level for this
component package. It does not prove that this package was installed, nor
that the postinstall ran as root. Installation domain and authorization
decide the actual context. Apple's distribution-package reference describes
current-user-home installs as running as the current user and unable to
write outside that home. The later capture must be read with its target and
UID attached; do not infer privilege from this XML alone.
Also read your own script, carried inside the pkg, verbatim:
#!/bin/sh
# postinstall proof script for the Phase 01 lab pkg.
# Records the execution context the installer gave it, as a whoami foothold.
E="$HOME/macseclabs/evidence/1.4-installer-scripts"
mkdir -p "$E"
printf '{"ts":"%s","event":"postinstall-executed","uid":%s,"user":"%s","euid":%s}
' \
"$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$(id -ru)" "$(id -un)" "$(id -u)" \
>> "$E/postinstall-proof.jsonl"
{
echo "trigger: pkg-postinstall"
echo "whoami: $(whoami)"
echo "id: $(id)"
echo "date: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
} >> "$E/postinstall-foothold.txt"
exit 0One job, in two parts: append a line recording who the installer let it be,
and write a whoami/id foothold the report can later cite. Keep the
expansion as evidence:
The Installation Lifecycle
The installer's flow is placement, then scripts, then receipt. Run the
install against a user-home target; this VM did not prompt for a password.
CurrentUserHomeDirectory depends on the logged-in user's per-session
installer service, so run this from Terminal in the active graphical desktop.
The validation VM had tester01 at the console and completed this command.
An SSH-only host at the login window may not provide the same per-user
installer service. Confirm your own session before running the install:
installer: Package name is msl-payload installer: Installing at base path /Users/tester01 installer: The install was successful.
Three lines, and each is a lifecycle stage. The installer read the package name from the manifest, resolved the base path from the target, placed the payload, ran the postinstall, and recorded success. Now read what the postinstall proof captured:
The payload is now at ~/macseclabs-pkg-lab/msl-payload.sh. That explicit
lab-only prefix makes the installation easy to inspect and safe to remove at
the end.
{"ts":"2026-10-02T01:06:29Z","event":"postinstall-executed","uid":501,"user":"tester01","euid":501}Read the foothold the postinstall wrote:
trigger: pkg-postinstall whoami: tester01 id: uid=501(tester01) gid=20(staff) groups=20(staff),12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),701(com.apple.sharepoint.group.1),33(_appstore),98(_lpadmin),100(_lpoperator),204(_developer),250(_analyticsusers),395(com.apple.access_ftp),398(com.apple.access_screensharing),399(com.apple.access_ssh),400(com.apple.access_remote_ae) date: 2026-10-02T01:06:29Z
This VM run reports UID 501 for the user-home target. It does not validate a
system-domain installation. Cross-reference the auth declaration, target,
and observed UID before asserting the script's authority. Phase 06 revisits
the privilege boundary with a separate scenario.
The Receipt: The Forensic Gift That Outlives the Payload
This user-home install produced a receipt in the user domain. Inspect it before cleanup:
{
"InstallDate" => 2026-10-02 01:06:29 +0000
"InstallPrefixPath" => "macseclabs-pkg-lab"
"InstallProcessName" => "installer"
"InstallToken" => "A00AD2F4-A8A8-486B-8713-1EA610A91BF5"
"PackageFileName" => "msl-payload.pkg"
"PackageIdentifier" => "com.macseclabs.msl-payload"
"PackageVersion" => "1.0"
}The captured fields identify this package, install prefix, process, and recorded time. A receipt can outlive payload deletion, but retention depends on cleanup, system state, and management policy. A system-domain install may create different receipt evidence; this user-domain capture does not prove that scenario.
Keep the receipt as evidence, then clean the install off the box:
Forgot package 'com.macseclabs.msl-payload' on '/Users/tester01'.
The receipt copy in your evidence preserves the package metadata you read.
The user-home receipt also has a BOM. Removing only its plist leaves
registration behind and makes a repeat run an upgrade. pkgutil --forget
removes the receipt registration for this exact lab package; the bounded
directory removal above handles its payload files.
Do not call it the only surviving record: installer logs, unified logging,
filesystem metadata, backups, and management telemetry may also retain evidence.
On an engagement, enumerate those sources before making a retention claim.
The PKG Proof Pattern: Authority in Exchange for a Receipt
The lab pkg illustrates a common installer-script pattern. The payload places files, and the postinstall runs code with the authority the target granted, inside the installer's process context, after the user performed the most legitimate-looking interaction in software: running an installer. The chain differences from module 1.2's formats are structural:
| Property | App bundle launch | PKG install |
|---|---|---|
| User interaction | double-click | authenticate or open installer |
| Execution context | user session if opened as an app | installer script context depends on target and authorization |
| Lasting record | process and policy evidence depend on launch | receipt if installation succeeds and records it |
| Script timing | at each observed app launch, if the app executes code | during the observed install; upgrades may run it again |
The receipt row is an operator tradeoff. A pkg can offer an installation workflow with elevated authority when properly authorized, while creating package and script evidence. The design question from module 1.1 returns: does the engagement need that installation context, and what evidence does the actual target record?
An install may leave a receipt, installer logs, file activity, and a postinstall process. Which of these a defender can see depends on the host's collection and retention. Correlate package identifier, script path, UID, and time before describing one installation as a complete chain.
The user-home and system-domain paths have different authority and possible records. An unfamiliar package identifier can be a triage lead, not an automatic malicious verdict. If management telemetry exists, compare it with the local receipt and installer logs before claiming deployment provenance.
The postinstall proof file reports what the script wrote about its own context. An independent process sensor would be needed to establish the parent chain, and this module did not measure any commercial EDR.
Closing the Workspace
== MacSec Labs workspace check: 1.4-installer-scripts == PASS evidence directory exists: /Users/tester01/macseclabs/evidence/1.4-installer-scripts INFO non-empty evidence files: 4 evidence contents: /Users/tester01/macseclabs/evidence/1.4-installer-scripts/PackageInfo.xml /Users/tester01/macseclabs/evidence/1.4-installer-scripts/postinstall-foothold.txt /Users/tester01/macseclabs/evidence/1.4-installer-scripts/postinstall-proof.jsonl /Users/tester01/macseclabs/evidence/1.4-installer-scripts/receipt.plist result: workspace check passed
The four artifacts came from the current VM's successful user-home install. The exact lab install directory and receipt were removed after the evidence copies were made; no system-domain install was attempted.
Checkpoint
Before module 1.5, confirm:
- What four files sit inside the expanded pkg, and what is each one's job?
- What does
auth="root"in PackageInfo declare, and what decided the actual uid of your postinstall? - What three lifecycle stages did the installer output show?
- Which receipt fields survive deletion of the payload, and why do they matter to an auditor?
- Why does the pkg trade stealth for authority compared to a bundle launch?
- Where did your lab install's receipt live, and where would a system install's receipt live?
Module 1.5 changes the interaction entirely: no download, no installer, just a link the user clicks and a handler registered by an app. URL schemes and document types are another initial-access surface to test against the current host's registration and user-choice behavior.
This is one free module of many.
Get every module across all ten phases, with working code and the detection engineering to catch each technique.
Get full access