Designed as a possible last step before a MDM Lock Computer command, this CrowdStrike Falcon / Jamf Pro combination approach may aid in keeping a Mac computer online for investigation, while discouraging end-user tampering

Background
When a macOS computer is lost, stolen or involved in a security breach, the Mobile Device Management (MDM) Lock Computer command can be used as an “atomic” option to quickly bring some peace of mind to what are typically stressful situations, while the MDM Wipe Computer command can be used as the “nuclear” option.
For occasions where first forensically securing a macOS computer are preferred, the following approach may aid in keeping a device online for investigation, while discouraging end-user tampering.
This approach should not be used in production without thorough Red Team testing; caveat emptor.
Flowcharts


Configuration
Complete the following steps to forensically secure a macOS computer with CrowdStrike Falcon and Jamf Pro, which leverages the macOS 15 Sequoia (and earlier) built-in option to lock or unlock a user’s screen, which is based on a previous post: Actionable messages with Jamf Helper.
A. Customize the Jamf Pro Forensically Sound Workstation Lockout.bash
script for your environment
- 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="1.0.0" # Client-side Log scriptLog="/var/log/org.churchofjesuschrist.log" # jamfHelper Location JH="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper"
- Set your preferred Organization Variables
organizationLocalAdmin
(i.e., your organization’s local admin, which will be granted exclusive SSH access)
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Organization Variables # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Script Human-readabale Name humanReadableScriptName="Forensically Sound Workstation Lockout" # Organization's Script Name organizationScriptName="FSWL" # Organization's Local Admin (for exclusive SSH) organizationLocalAdmin="organizations_local_admin_account"
- Set your preferred script defaults (which will be trumped in a policy’s script parameters)
- Parameter 4:
heading
(i.e., the heading to be displayed to the end-user) - Parameter 5:
icon
(i.e., the absolute path to a client-side icon) - Parameter 6:
description
(i.e., the message to be displayed to the end-user; See: Bonus: Line-breaking Voodoo) - Parameter 7:
launchDaemonVariation
(i.e., determine if the LaunchDaemon should execute a local, client-side script, or, if it should execute a remote, Jamf Pro policy) - Parameter 8:
policyTrigger
(i.e., when Parameter 7,launchDaemonVariation
, is set to Remote Policy, specify the name of the policy’s Custom Trigger)
- Parameter 4:
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Jamf Pro Script Parameters # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Sets the heading of the window to the specified string heading="${4:-"Heading [Parameter 4]"}" # Absolute path to client-side icon icon="${5:-"/System/Library/CoreServices/Finder.app/Contents/Resources/Finder.icns"}" # Sets the main contents of the window to the specified string description="${6:-"Description [Parameter 6]"}" # LaunchDaemon Variation [ Local Script | Remote Policy ] launchDaemonVariation="${7:-"Local Script"}" # Policy Trigger policyTrigger="${8:-"Policy Trigger [Parameter 8]"}"

Jamf Pro Forensically Sound Workstation Lockout.bash
Latest version available on GitHub.
#!/bin/bash # shellcheck disable=SC2034,SC2317,SC2086,SC2143,SC2181 #################################################################################################### # # Title: Jamf Pro Forensically Sound Workstation Lockout # # Purpose: Leverages the built-in macOS LockScreen binary and a LaunchDaemon # to prevent end-user interaction # # Documentation: https://snelson.us/fswl # #################################################################################################### # # HISTORY # # Version 1.0.0, 15-Mar-2025, Dan K. Snelson (@dan-snelson) # - First "official" release # #################################################################################################### # # Global Variables # #################################################################################################### export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin/ # Script Version scriptVersion="1.0.0" # Client-side Log scriptLog="/var/log/org.churchofjesuschrist.log" # jamfHelper Location JH="/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # LaunchDaemon Variables # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Hard-coded domain name plistDomain="org.churchofjesuschrist" # Unique label for this plist plistLabel="fswl" # Prepend domain to label plistDomainAndLabel="$plistDomain.$plistLabel" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Organization Variables # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Script Human-readabale Name humanReadableScriptName="Forensically Sound Workstation Lockout" # Organization's Script Name organizationScriptName="FSWL" # Organization's Local Admin (for exclusive SSH) organizationLocalAdmin="organizations_local_admin_account" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Jamf Pro Script Parameters # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Sets the heading of the window to the specified string heading="${4:-"Heading [Parameter 4]"}" # Absolute path to client-side icon icon="${5:-"/System/Library/CoreServices/Finder.app/Contents/Resources/Finder.icns"}" # Sets the main contents of the window to the specified string description="${6:-"Description [Parameter 6]"}" # LaunchDaemon Variation [ Local Script | Remote Policy ] launchDaemonVariation="${7:-"Local Script"}" # Policy Trigger policyTrigger="${8:-"Policy Trigger [Parameter 8]"}" #################################################################################################### # # Functions # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Client-side Logging # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function updateScriptLog() { echo "${organizationScriptName} ($scriptVersion): $( date +%Y-%m-%d\ %H:%M:%S ) - ${1}" | tee -a "${scriptLog}" } function preFlight() { updateScriptLog "[PRE-FLIGHT] ${1}" } function logComment() { updateScriptLog " ${1}" } function notice() { updateScriptLog "[NOTICE] ${1}" } function info() { updateScriptLog "[INFO] ${1}" } function errorOut(){ updateScriptLog "[ERROR] ${1}" } function error() { updateScriptLog "[ERROR] ${1}" (( errorCount++ )) || true } function warning() { updateScriptLog "[WARNING] ${1}" (( errorCount++ )) || true } function fatal() { updateScriptLog "[FATAL ERROR] ${1}" exit 1 } function quitOut(){ updateScriptLog "[QUIT] ${1}" } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Create LaunchDaemon for Remote Policy # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function launchDaemonRemotePolicy() { logComment "Call remote policy" ( cat <<endOfLaunchDaemonRemote <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Enabled</key> <true/> <key>EnableTransactions</key> <true/> <key>Label</key> <string>${plistDomainAndLabel}</string> <key>UserName</key> <string>root</string> <key>ProgramArguments</key> <array> <string>/usr/local/jamf/bin/jamf</string> <string>policy</string> <string>-event</string> <string>${policyTrigger}</string> <string>-verbose</string> </array> <key>RunAtLoad</key> <true/> <key>StandardErrorPath</key> <string>${scriptLog}</string> <key>StandardOutPath</key> <string>${scriptLog}</string> </dict> </plist> endOfLaunchDaemonRemote ) > /Library/LaunchDaemons/$plistDomainAndLabel.plist } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Create LaunchDaemon for Local Script # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function launchDaemonLocalScript() { notice "Create LaunchDaemon for Local Script" logComment "Create local script directory" mkdir -pv "/usr/local/${plistDomain}/scripts/" logComment "Create local script" ( cat <<endOfLaunchDaemonScript <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Enabled</key> <true/> <key>EnableTransactions</key> <true/> <key>Label</key> <string>${plistDomainAndLabel}</string> <key>UserName</key> <string>root</string> <key>ProgramArguments</key> <array> <string>/bin/bash</string> <string>/usr/local/${plistDomain}/scripts/${plistLabel}.bash</string> </array> <key>RunAtLoad</key> <true/> <key>StandardErrorPath</key> <string>${scriptLog}</string> <key>StandardOutPath</key> <string>${scriptLog}</string> </dict> </plist> endOfLaunchDaemonScript ) > /Library/LaunchDaemons/$plistDomainAndLabel.plist # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Create Local Script # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # notice "Create local script ..." logComment "/usr/local/${plistDomain}/scripts/${plistLabel}.bash" ( cat <<endOfScript #!/bin/bash #################################################################################################### # # ABOUT # # Forensically Sound Workstation Lockout # #################################################################################################### # # HISTORY # # Version 0.0.1, 04-Feb-2025, Dan K. Snelson (@dan-snelson) # - Original version # # Version 0.0.2, 28-Feb-2025, Dan K. Snelson (@dan-snelson) # - Added hard-coded sleep of 11 seconds to launchDaemonLocalScript (to try and keep launchd happy) # #################################################################################################### # # Global Variables # #################################################################################################### export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin/ # Script Version scriptVersion="0.0.2" #################################################################################################### # # Program # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Scripts must run at least 10 seconds (or launchd may get suspicious) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # sleep 11 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Display Message # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # echo "The following message will be displayed to the end-user:" echo "${heading} ${description}" echo "Lock screen with specified message" osascript -e "set Volume 10" afplay /System/Library/Sounds/Blow.aiff /System/Library/CoreServices/RemoteManagement/AppleVNCServer.bundle/Contents/Support/LockScreen.app/Contents/MacOS/LockScreen & sleep 1.5 /Library/Application\ Support/JAMF/bin/jamfHelper.app/Contents/MacOS/jamfHelper -windowType fs -icon '${icon}' -heading '${heading}' -description '${description}' exit 0 endOfScript ) > "/usr/local/${plistDomain}/scripts/${plistLabel}.bash" } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Validate / Create Groups # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function validateCreateGroup(){ groupName="${1}" groupNumber="${2}" if [[ $( dscl . list /Groups | grep "${groupName}" ) ]]; then logComment "${groupName} Exists: $( dscl . -read Groups/"${groupName}" GroupMembership 2>&1)" else notice "Creating ${groupName} …" dscl . create /Groups/"${groupName}" if [[ $? -ne 0 ]]; then fatal "Failed to create ${groupName}" else logComment "Successfully created ${groupName}" fi notice "Assigning ${groupName} a GID of ${groupNumber} …" dscl . create /Groups/"${groupName}" gid "${groupNumber}" if [[ $? -ne 0 ]]; then fatal "Failed to assign ${groupName} a GID of ${groupNumber}" else logComment "Successfully assigned ${groupName} a GID of ${groupNumber}" fi fi } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Add User to Group # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function addUserToGroup(){ userName="${1}" groupName="${2}" notice "Add ${userName} to ${groupName} …" membershipCheck=$( dseditgroup -o checkmember -m "${userName}" "${groupName}" ) if [[ "${membershipCheck}" == *"NOT a member"* ]]; then logComment "Adding ${userName} to ${groupName} …" dseditgroup -o edit -a "${userName}" -t user "${groupName}" if [[ $? -ne 0 ]]; then warning "Failed to adding ${userName} to ${groupName}" else membershipCheck=$( dseditgroup -o checkmember -m "${userName}" "${groupName}" ) logComment "${membershipCheck}" fi else logComment "${membershipCheck}" fi } # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Reset SSH Privileges # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function resetSshPrivileges(){ # Disable SSH notice "Disable SSH" launchctl bootout system /System/Library/LaunchDaemons/ssh.plist if [[ $? -ne 0 ]]; then warning "Failed to unload SSH" else logComment "Unloaded SSH" fi # Brute-force kill all SSH sessions pkill -9 ssh # Delete SSH group to reset access for all accounts logComment "Delete SSH group to reset access for all accounts" dseditgroup -o delete -t group com.apple.access_ssh if [[ $? -ne 0 ]]; then warning "Failed to delete SSH group" else logComment "Deleted SSH group" fi # Enable SSH for Organization Local Admin logComment "Enable SSH for Organization Local Admin" validateCreateGroup "com.apple.access_ssh" "399" addUserToGroup "${organizationLocalAdmin}" "com.apple.access_ssh" launchctl bootstrap system /System/Library/LaunchDaemons/ssh.plist if [[ $? -ne 0 ]]; then warning "Failed to Bootstrap SSH" else logComment "Bootstraped SSH" fi launchctl start /System/Library/LaunchDaemons/ssh.plist if [[ $? -ne 0 ]]; then warning "Failed to start SSH" else logComment "Started SSH" fi } #################################################################################################### # # Pre-flight Checks # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Client-side Logging # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # if [[ ! -f "${scriptLog}" ]]; then touch "${scriptLog}" if [[ -f "${scriptLog}" ]]; then preFlight "Created specified scriptLog" else fatal "Unable to create specified scriptLog; exiting.\n\n(Is this script running as 'root' ?)" fi else preFlight "Specified scriptLog exists; writing log entries to it" fi # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Logging Preamble # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # preFlight "\n\n###\n# $humanReadableScriptName (${scriptVersion})\n###\n" preFlight "Initiating …" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Confirm script is running as root # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # if [[ $(id -u) -ne 0 ]]; then fatal "This script must be run as root; exiting." fi # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Ensure computer does not go to sleep (thanks, @grahampugh!) # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # caffeinatedPID="$$" preFlight "Caffeinating this script (PID: $caffeinatedPID)" caffeinate -dimsu -w $caffeinatedPID & # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Pre-flight Check: Complete # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # preFlight "Complete!" #################################################################################################### # # Program # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Reset SSH Privileges # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # resetSshPrivileges # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Display Message # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # notice "The following message will be displayed to the end-user:" logComment "${heading} ${description}" notice "Lock screen with specified message" osascript -e "set Volume 10" afplay /System/Library/Sounds/Blow.aiff /System/Library/CoreServices/RemoteManagement/AppleVNCServer.bundle/Contents/Support/LockScreen.app/Contents/MacOS/LockScreen & sleep 1.5 displayMessage=$( "$JH" -windowType "fs" -icon "${icon}" -heading "${heading}" -description "${description}" ) & # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Create LaunchDaemon # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # notice "Create the LaunchDaemon:" logComment "/Library/LaunchDaemons/$plistDomainAndLabel.plist" case $launchDaemonVariation in "Remote Policy" ) launchDaemonRemotePolicy ;; "Local Script" | * ) launchDaemonLocalScript ;; esac # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Set the permission on the file # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # notice "Set LaunchDaemon file permissions ..." /usr/sbin/chown root:wheel "/Library/LaunchDaemons/${plistDomainAndLabel}.plist" /bin/chmod 644 "/Library/LaunchDaemons/${plistDomainAndLabel}.plist" /bin/chmod +x "/Library/LaunchDaemons/${plistDomainAndLabel}.plist" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Exit # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # quitOut "Exit" exit 0
B. Add the Jamf Pro Forensically Sound Workstation Lockout.bash
script to your Jamf Pro server
- Add the
Jamf Pro Forensically Sound Workstation Lockout.bash
script to your Jamf Pro server - Specify the following for Options > Parameter Labels
- Parameter 4:
Heading
- Parameter 5:
Icon (absolute path)
- Parameter 6:
Message
- Parameter 7:
LaunchDaemon Variation [ Local Script | Remote Policy ]
- Parameter 8:
Policy Trigger (for Remote Policy)
- Parameter 4:
- Click Save

C. Create a Jamf Pro Policy to execute Jamf Pro Forensically Sound Workstation Lockout.bash
- Create a new Jamf Pro Policy, using the following as a guide for Options > General:
- Set Display Name to
Forensically Sound Workstation Lockout (1.0.0)
- Enable Trigger > Custom and enter a secure name
- Set Execution Frequency to
Ongoing
- Set Display Name to

- Select the Scripts payload and add the
Jamf Pro Forensically Sound Workstation Lockout.bash
script, specifying the following Parameter Values- Heading:
Contact Company SecOps
- Icon (absolute path):
/System/Library/CoreServices/Diagnostics Reporter.app/Contents/Resources/AppIcon.icns
- Message:
Your Mac may be involved in a security incident. Please contact Church SecOps immediately: +1 (801) 555-1212 and mention Code 8675-309.
See: Bonus: Line-breaking Voodoo
- LaunchDaemon Variation:
Local Script
- Policy Trigger: Enter the same Custom trigger specified previously
- Heading:

- Scope the policy to All Computers
- Click Save
D. Add Scripts to CrowdStrike Falcon Real Time Response
Add the following two scripts to Falcon Real Time Response:

CrowdStrike Falcon Forensically Sound Workstation Lockout.zsh
Adjust the customEventNameGoesHere
to match the value specified in Step C.
#!/bin/zsh # macOS Forensically Sound Workstation Lockout (1.0.0) # (Estimated Duration: 0h:0m:10s) echo -e "\n\n\n###\n# macOS Forensically Sound Workstation Lockout (1.0.0)\n# (Estimated Duration: 0h:0m:10s)\n###" # Initialize SECONDS SECONDS="0" # Jamf binary-related Variables jamfVersion=$( /usr/local/bin/jamf -version | cut -d "=" -f2 ) jamfPid=$( pgrep -a "jamf" | head -n 1 ) if [[ -n "${jamfPid}" ]]; then echo "• jamf ${jamfVersion} binary already running …" ps -p "${jamfPid}" echo "• Killing ${jamfPid} …" kill "${jamfPid}" fi echo "• Executing Jamf Pro Policy Trigger …" /usr/local/bin/jamf policy -trigger customEventNameGoesHere -verbose echo -e "\nExecution Duration: $(printf '%dh:%dm:%ds\n' $((SECONDS/3600)) $((SECONDS%3600/60)) $((SECONDS%60)))"
CrowdStrike Falcon Forensically Sound Workstation Release.zsh
Manually modify the LaunchDaemon and script to be removed.
#!/bin/zsh # macOS Forensically Sound Workstation Release (1.0.0) # (Estimated Duration: 0h:0m:03s) echo -e "\n\n\n###\n# macOS Forensically Sound Workstation Release (1.0.0)\n# (Estimated Duration: 0h:0m:03s)\n###" # Initialize SECONDS SECONDS="0" #################################################################################################### # # Functions # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Kill a specified process # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # function killProcess() { process="$1" if process_pid=$( pgrep -a "${process}" 2>/dev/null ) ; then echo "Attempting to terminate the '$process' process …" echo "(Termination message indicates success.)" kill "$process_pid" 2> /dev/null if pgrep -a "$process" >/dev/null ; then echo "ERROR: '$process' could not be terminated." fi else echo "The '$process' process isn't running." fi } #################################################################################################### # # Program # #################################################################################################### # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Release the Forensically Sound Workstation Lockout # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # echo "Unload / Delete LaunchDaemon" /bin/launchctl bootout system /Library/LaunchDaemons/org.churchofjesuschrist.fswl.plist rm -fv /Library/LaunchDaemons/org.churchofjesuschrist.fswl.plist echo "Remove Client-side Script" rm -fv /usr/local/org.churchofjesuschrist/scripts/fswl.bash echo "Kill Processes" killall -v LockScreen killall -v jamfHelper killProcess "caffeinate" echo -e "\nExecution Duration: $(printf '%dh:%dm:%ds\n' $((SECONDS/3600)) $((SECONDS%3600/60)) $((SECONDS%60)))"
E. Testing
CrowdStrike Falcon Real Time Response
Once the script, policy and message are set in Jamf Pro, the “lockout magic” is enabled / disabled from the CrowdStrike Falcon console.

Falcon Real Time Response commands are used to:
- Lock the Mac:
CrowdStrike Falcon Forensically Sound Workstation Lockout.zsh
securely calls the Jamf Pro policy, which executes theJamf Pro Forensically Sound Workstation Lockout.bash
script to prevent the computer from accepting keyboard or mouse input - Investigate the incident
- Release the lock:
CrowdStrike Falcon Forensically Sound Workstation Release.zsh
terminates the processes locking the Mac
