A proof-of-concept, caveat emptor workflow for securely executing a repository-hosted script

Background
While EDR tools can excel at running one-off code on a limited number of endpoints, device management solutions are often best suited for executing predefined policies at scale.
EDR Script Runner strives to strike a balance between the immediate, dynamic needs of threat hunting teams and the reliability of a MDM server, by securely executing a repository-hosted script, only when necessary.
Workflow

- An authorized administrator edits
edrScript.zsh, generates its hash (asedrScriptHash.txt), then commits and pushes both to a private, secure repository - The script — and its hash — are ready to be downloaded client-side to the target Mac
- The MDM server instructs the Mac to execute
edrScriptRunner.zsh, which first validates the checksum, provided by either:edrScriptHash.txt- Jamf Pro Policy Parameter
- After the checksum has been validated,
edrScript.zshis downloaded client-side and the script’sscriptModificationTimestampvariable is compared to the last client-side execution- If the modification dates differ, the script is executed as
root(caveat emptor)
- If the modification dates differ, the script is executed as
Configuration
Complete the following steps to securely (?) execute a repository-hosted script with Jamf Pro.
A. Customize the scripts for your environment
edrScript.zsh
- Review and adjust the Global Variables as required for your environment
scriptLog(i.e., the location of your client-side logs)
#################################################################################################### # # Global Variables # #################################################################################################### export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin/ # Script Version scriptVersion="0.0.3" # Client-side Log scriptLog="/var/log/org.test.edr.log" # Script Modification Timestamp (i.e., $( date '+%Y-%m-%d-%H%M%S' | tr -d '\n' | pbcopy ) ) scriptModificationTimestamp="2024-09-18-045533" # Initialize SECONDS SECONDS="0"
- Set your preferred Organization Variables
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Organization Variables # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Script Human-readabale Name humanReadableScriptName="EDR Script" # Organization's Script Name organizationScriptName="EDR"
- Edit the script’s actual
commandas desired
####################################################################################################
#
# Program
#
####################################################################################################
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Proof-of-concept Command Substitution: `jamf about`
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
notice "Proof-of-concept Command Substitution"
logComment "jamf about"
command=$( /usr/local/bin/jamf about )
if [[ "$?" == "0" ]]; then
logComment "Successful execution"
logComment "jamf about: ${command}"
else
errorOut "Execution error"
fi
edrScriptRunner.zsh
- Review and adjust the Global Variables as required for your environment
scriptLog(i.e., the location of your client-side logs)
####################################################################################################
#
# Global Variables
#
####################################################################################################
export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin/
# Script Version
scriptVersion="0.0.5"
# Client-side Log
scriptLog="/var/log/org.test.log"
# Initialize SECONDS
SECONDS="0"
# Temporary Working Directory
workDirectory=$( basename "${0%%.*}" )
tempDirectory=$( mktemp -d "/private/tmp/$workDirectory.XXXXXX" )
- Set your preferred Organization Variables
orgRepo(i.e., the location of your organization’s private, secure code repository)orgFile(i.e., the actual file name of the script which will be executed)orgPlist(i.e., your organization’s client-side.plist)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Organization Variables # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Script Human-readabale Name humanReadableScriptName="EDR Script Runner" # Organization's Script Name organizationScriptName="EDRr" # Organization's Repository orgRepo="https://raw.githubusercontent.com/dan-snelson/Jamf-Pro-Scripts/development/EDR/" # Organization's File orgFile="edrScript.zsh" # Organization's .plist orgPlist="/Library/Preferences/org.test.plist"
- Set your preferred script defaults (which will be trumped in a policy’s script parameters)
- Parameter 4:
checksumSource(i.e., script (default) or policy) - Parameter 5:
expectedScriptChecksum(i.e., the actual checksum oforgFile)openssl dgst -sha256 "edrScript.zsh" | awk -F'= ' '{print $2}' > edrScriptHash.txt
- Parameter 4:
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Jamf Pro Script Parameters
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Parameter 4: Checksum Source [ script (default) | policy ]
checksumSource="${4:-"script"}"
# Parameter 5: Expected Script Checksum
case ${checksumSource} in
script ) expectedScriptChecksum=$( curl --location --silent --fail "${orgRepo}/${orgFile%%.*}Hash.txt" ) ;;
policy | * ) expectedScriptChecksum="${5:-"55b9765ed20cd1563382e79d5af7f5505fb6be55488c9b1c7effbfe2b64c8c3b"}" ;;
esac
B. Add the edrScriptRunner.zsh script to your Jamf Pro server
- Add the
edrScriptRunner.zshscript to your Jamf Pro server - Specify the following for Options > Parameter Labels
- Parameter 4:
Checksum Source [ script (default) | policy ] - Parameter 5:
Expected Script Checksum (used when "Checksum Source" is set to "policy")
- Parameter 4:
- Click Save

C. Create a Jamf Pro Policy to execute edrScriptRunner.zsh
- Create a new Jamf Pro Policy, using the following as a guide for Options > General:
- Set Display Name to
EDR Script Runner (0.0.5)
- Enable Trigger > Recurring Check-in
- Set Execution Frequency to
Once every month
- Set Display Name to

- Select the Scripts payload and add the
edrScriptRunner.zshscript, specifying the following Parameter Values- Checksum Source:
script🔥 allowsrootexecution with no interaction from the Device Management teampolicyrequires coordination between the SecOps and Device Management teams to update the checksum and flush policy logs
- Expected Script Checksum: actual checksum of
orgFile
- Checksum Source:

- Scope the policy as desired
- Click Save
D. Testing
Checksum Generation
Each time the orgFile script is updated, execute the following command to update its checksum:
openssl dgst -sha256 "edrScript.zsh" | awk -F'= ' '{print $2}' > edrScriptHash.txt
Repeated Executions
To test repeated executions, SecOps and Device Management teams will need to actively coordinate to:
- Flush policy logs
- Delete client-side
.plistkeys:defaults delete "${orgPlist}" "EDR Script Runner"
- Manually execute the Jamf Pro policy